293 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
import AxiosMockAdapter from 'axios-mock-adapter';
 | 
						|
import $ from 'jquery';
 | 
						|
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
 | 
						|
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
 | 
						|
import axios from '~/lib/utils/axios_utils';
 | 
						|
import initSearchAutocomplete from '~/search_autocomplete';
 | 
						|
import '~/lib/utils/common_utils';
 | 
						|
 | 
						|
describe('Search autocomplete dropdown', () => {
 | 
						|
  let widget = null;
 | 
						|
 | 
						|
  const userName = 'root';
 | 
						|
  const userId = 1;
 | 
						|
  const dashboardIssuesPath = '/dashboard/issues';
 | 
						|
  const dashboardMRsPath = '/dashboard/merge_requests';
 | 
						|
  const projectIssuesPath = '/gitlab-org/gitlab-foss/issues';
 | 
						|
  const projectMRsPath = '/gitlab-org/gitlab-foss/-/merge_requests';
 | 
						|
  const groupIssuesPath = '/groups/gitlab-org/-/issues';
 | 
						|
  const groupMRsPath = '/groups/gitlab-org/-/merge_requests';
 | 
						|
  const autocompletePath = '/search/autocomplete';
 | 
						|
  const projectName = 'GitLab Community Edition';
 | 
						|
  const groupName = 'Gitlab Org';
 | 
						|
 | 
						|
  const removeBodyAttributes = () => {
 | 
						|
    const { body } = document;
 | 
						|
 | 
						|
    delete body.dataset.page;
 | 
						|
    delete body.dataset.project;
 | 
						|
    delete body.dataset.group;
 | 
						|
  };
 | 
						|
 | 
						|
  // Add required attributes to body before starting the test.
 | 
						|
  // section would be dashboard|group|project
 | 
						|
  const addBodyAttributes = (section = 'dashboard') => {
 | 
						|
    removeBodyAttributes();
 | 
						|
 | 
						|
    const { body } = document;
 | 
						|
    switch (section) {
 | 
						|
      case 'dashboard':
 | 
						|
        body.dataset.page = 'root:index';
 | 
						|
        break;
 | 
						|
      case 'group':
 | 
						|
        body.dataset.page = 'groups:show';
 | 
						|
        body.dataset.group = 'gitlab-org';
 | 
						|
        break;
 | 
						|
      case 'project':
 | 
						|
        body.dataset.page = 'projects:show';
 | 
						|
        body.dataset.project = 'gitlab-ce';
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  const disableProjectIssues = () => {
 | 
						|
    document.querySelector('.js-search-project-options').dataset.issuesDisabled = true;
 | 
						|
  };
 | 
						|
 | 
						|
  // Mock `gl` object in window for dashboard specific page. App code will need it.
 | 
						|
  const mockDashboardOptions = () => {
 | 
						|
    window.gl.dashboardOptions = {
 | 
						|
      issuesPath: dashboardIssuesPath,
 | 
						|
      mrPath: dashboardMRsPath,
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  // Mock `gl` object in window for project specific page. App code will need it.
 | 
						|
  const mockProjectOptions = () => {
 | 
						|
    window.gl.projectOptions = {
 | 
						|
      'gitlab-ce': {
 | 
						|
        issuesPath: projectIssuesPath,
 | 
						|
        mrPath: projectMRsPath,
 | 
						|
        projectName,
 | 
						|
      },
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  const mockGroupOptions = () => {
 | 
						|
    window.gl.groupOptions = {
 | 
						|
      'gitlab-org': {
 | 
						|
        issuesPath: groupIssuesPath,
 | 
						|
        mrPath: groupMRsPath,
 | 
						|
        projectName: groupName,
 | 
						|
      },
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  const assertLinks = (list, issuesPath, mrsPath) => {
 | 
						|
    if (issuesPath) {
 | 
						|
      const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`;
 | 
						|
      const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`;
 | 
						|
 | 
						|
      expect(list.find(issuesAssignedToMeLink).length).toBe(1);
 | 
						|
      expect(list.find(issuesAssignedToMeLink).text()).toBe('Issues assigned to me');
 | 
						|
      expect(list.find(issuesIHaveCreatedLink).length).toBe(1);
 | 
						|
      expect(list.find(issuesIHaveCreatedLink).text()).toBe("Issues I've created");
 | 
						|
    }
 | 
						|
    const mrsAssignedToMeLink = `a[href="${mrsPath}/?assignee_username=${userName}"]`;
 | 
						|
    const mrsIHaveCreatedLink = `a[href="${mrsPath}/?author_username=${userName}"]`;
 | 
						|
 | 
						|
    expect(list.find(mrsAssignedToMeLink).length).toBe(1);
 | 
						|
    expect(list.find(mrsAssignedToMeLink).text()).toBe('Merge requests assigned to me');
 | 
						|
    expect(list.find(mrsIHaveCreatedLink).length).toBe(1);
 | 
						|
    expect(list.find(mrsIHaveCreatedLink).text()).toBe("Merge requests I've created");
 | 
						|
  };
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    loadHTMLFixture('static/search_autocomplete.html');
 | 
						|
 | 
						|
    window.gon = {};
 | 
						|
    window.gon.current_user_id = userId;
 | 
						|
    window.gon.current_username = userName;
 | 
						|
    window.gl = window.gl || (window.gl = {});
 | 
						|
 | 
						|
    widget = initSearchAutocomplete({ autocompletePath });
 | 
						|
  });
 | 
						|
 | 
						|
  afterEach(() => {
 | 
						|
    // Undo what we did to the shared <body>
 | 
						|
    removeBodyAttributes();
 | 
						|
    window.gon = {};
 | 
						|
 | 
						|
    resetHTMLFixture();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should show Dashboard specific dropdown menu', () => {
 | 
						|
    addBodyAttributes();
 | 
						|
    mockDashboardOptions();
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
    return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should show Group specific dropdown menu', () => {
 | 
						|
    addBodyAttributes('group');
 | 
						|
    mockGroupOptions();
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
    return assertLinks(list, groupIssuesPath, groupMRsPath);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should show Project specific dropdown menu', () => {
 | 
						|
    addBodyAttributes('project');
 | 
						|
    mockProjectOptions();
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
    return assertLinks(list, projectIssuesPath, projectMRsPath);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should show only Project mergeRequest dropdown menu items when project issues are disabled', () => {
 | 
						|
    addBodyAttributes('project');
 | 
						|
    disableProjectIssues();
 | 
						|
    mockProjectOptions();
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
    assertLinks(list, null, projectMRsPath);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should not show category related menu if there is text in the input', () => {
 | 
						|
    addBodyAttributes('project');
 | 
						|
    mockProjectOptions();
 | 
						|
    widget.searchInput.val('help');
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
    const link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`;
 | 
						|
 | 
						|
    expect(list.find(link).length).toBe(0);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should not submit the search form when selecting an autocomplete row with the keyboard', () => {
 | 
						|
    const ENTER = 13;
 | 
						|
    const DOWN = 40;
 | 
						|
    addBodyAttributes();
 | 
						|
    mockDashboardOptions(true);
 | 
						|
    const submitSpy = jest.spyOn(document.querySelector('form'), 'submit');
 | 
						|
    widget.searchInput.triggerHandler('focus');
 | 
						|
    widget.wrap.trigger($.Event('keydown', { which: DOWN }));
 | 
						|
    const enterKeyEvent = $.Event('keydown', { which: ENTER });
 | 
						|
    widget.searchInput.trigger(enterKeyEvent);
 | 
						|
 | 
						|
    // This does not currently catch failing behavior. For security reasons,
 | 
						|
    // browsers will not trigger default behavior (form submit, in this
 | 
						|
    // example) on JavaScript-created keypresses.
 | 
						|
    expect(submitSpy).not.toHaveBeenCalled();
 | 
						|
  });
 | 
						|
 | 
						|
  describe('show autocomplete results', () => {
 | 
						|
    beforeEach(() => {
 | 
						|
      widget.enableAutocomplete();
 | 
						|
 | 
						|
      const axiosMock = new AxiosMockAdapter(axios);
 | 
						|
      const autocompleteUrl = new RegExp(autocompletePath);
 | 
						|
 | 
						|
      axiosMock.onGet(autocompleteUrl).reply(200, [
 | 
						|
        {
 | 
						|
          category: 'Projects',
 | 
						|
          id: 1,
 | 
						|
          value: 'Gitlab Test',
 | 
						|
          label: 'Gitlab Org / Gitlab Test',
 | 
						|
          url: '/gitlab-org/gitlab-test',
 | 
						|
          avatar_url: '',
 | 
						|
        },
 | 
						|
        {
 | 
						|
          category: 'Groups',
 | 
						|
          id: 1,
 | 
						|
          value: 'Gitlab Org',
 | 
						|
          label: 'Gitlab Org',
 | 
						|
          url: '/gitlab-org',
 | 
						|
          avatar_url: '',
 | 
						|
        },
 | 
						|
      ]);
 | 
						|
    });
 | 
						|
 | 
						|
    function triggerAutocomplete() {
 | 
						|
      return new Promise((resolve) => {
 | 
						|
        const dropdown = widget.searchInput.data('deprecatedJQueryDropdown');
 | 
						|
        const filterCallback = dropdown.filter.options.callback;
 | 
						|
        dropdown.filter.options.callback = jest.fn((data) => {
 | 
						|
          filterCallback(data);
 | 
						|
 | 
						|
          resolve();
 | 
						|
        });
 | 
						|
 | 
						|
        widget.searchInput.val('Gitlab');
 | 
						|
        widget.searchInput.triggerHandler('input');
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    it('suggest Projects', async () => {
 | 
						|
      await triggerAutocomplete();
 | 
						|
 | 
						|
      const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
      const link = "a[href$='/gitlab-org/gitlab-test']";
 | 
						|
 | 
						|
      expect(list.find(link).length).toBe(1);
 | 
						|
    });
 | 
						|
 | 
						|
    it('suggest Groups', async () => {
 | 
						|
      await triggerAutocomplete();
 | 
						|
 | 
						|
      const list = widget.wrap.find('.dropdown-menu').find('ul');
 | 
						|
      const link = "a[href$='/gitlab-org']";
 | 
						|
 | 
						|
      expect(list.find(link).length).toBe(1);
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('disableAutocomplete', () => {
 | 
						|
    beforeEach(() => {
 | 
						|
      widget.enableAutocomplete();
 | 
						|
    });
 | 
						|
 | 
						|
    it('should close the Dropdown', () => {
 | 
						|
      const toggleSpy = jest.spyOn(widget.dropdownToggle, 'dropdown');
 | 
						|
 | 
						|
      widget.dropdown.addClass('show');
 | 
						|
      widget.disableAutocomplete();
 | 
						|
 | 
						|
      expect(toggleSpy).toHaveBeenCalledWith('toggle');
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('enableAutocomplete', () => {
 | 
						|
    let toggleSpy;
 | 
						|
    let trackingSpy;
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
      toggleSpy = jest.spyOn(widget.dropdownToggle, 'dropdown');
 | 
						|
      trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
 | 
						|
      document.body.dataset.page = 'some:page'; // default tracking for category
 | 
						|
    });
 | 
						|
 | 
						|
    afterEach(() => {
 | 
						|
      unmockTracking();
 | 
						|
    });
 | 
						|
 | 
						|
    it('should open the Dropdown', () => {
 | 
						|
      widget.enableAutocomplete();
 | 
						|
 | 
						|
      expect(toggleSpy).toHaveBeenCalledWith('toggle');
 | 
						|
    });
 | 
						|
 | 
						|
    it('should track the opening', () => {
 | 
						|
      widget.enableAutocomplete();
 | 
						|
 | 
						|
      expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_search_bar', {
 | 
						|
        label: 'main_navigation',
 | 
						|
        property: 'navigation',
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
});
 |