diff options
Diffstat (limited to 'spec/frontend/search/topbar')
6 files changed, 150 insertions, 729 deletions
diff --git a/spec/frontend/search/topbar/components/app_spec.js b/spec/frontend/search/topbar/components/app_spec.js index 9704277c86b..d17bdc2a6e1 100644 --- a/spec/frontend/search/topbar/components/app_spec.js +++ b/spec/frontend/search/topbar/components/app_spec.js @@ -1,14 +1,14 @@ -import { GlSearchBoxByClick, GlButton } from '@gitlab/ui'; +import { GlSearchBoxByType, GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; import { MOCK_QUERY } from 'jest/search/mock_data'; import { stubComponent } from 'helpers/stub_component'; import GlobalSearchTopbar from '~/search/topbar/components/app.vue'; -import GroupFilter from '~/search/topbar/components/group_filter.vue'; -import ProjectFilter from '~/search/topbar/components/project_filter.vue'; import MarkdownDrawer from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue'; +import SearchTypeIndicator from '~/search/topbar/components/search_type_indicator.vue'; +import { ENTER_KEY } from '~/lib/utils/keys'; import { SYNTAX_OPTIONS_ADVANCED_DOCUMENT, SYNTAX_OPTIONS_ZOEKT_DOCUMENT, @@ -41,42 +41,22 @@ describe('GlobalSearchTopbar', () => { }); }; - const findGlSearchBox = () => wrapper.findComponent(GlSearchBoxByClick); - const findGroupFilter = () => wrapper.findComponent(GroupFilter); - const findProjectFilter = () => wrapper.findComponent(ProjectFilter); + const findGlSearchBox = () => wrapper.findComponent(GlSearchBoxByType); const findSyntaxOptionButton = () => wrapper.findComponent(GlButton); const findSyntaxOptionDrawer = () => wrapper.findComponent(MarkdownDrawer); + const findSearchTypeIndicator = () => wrapper.findComponent(SearchTypeIndicator); describe('template', () => { beforeEach(() => { createComponent(); }); - describe('Search box', () => { - it('renders always', () => { - expect(findGlSearchBox().exists()).toBe(true); - }); + it('always renders Search box', () => { + expect(findGlSearchBox().exists()).toBe(true); }); - describe.each` - snippets | showFilters - ${null} | ${true} - ${{ query: { snippets: '' } }} | ${true} - ${{ query: { snippets: false } }} | ${true} - ${{ query: { snippets: true } }} | ${false} - ${{ query: { snippets: 'false' } }} | ${true} - ${{ query: { snippets: 'true' } }} | ${false} - `('topbar filters', ({ snippets, showFilters }) => { - beforeEach(() => { - createComponent(snippets); - }); - - it(`does${showFilters ? '' : ' not'} render when snippets is ${JSON.stringify( - snippets, - )}`, () => { - expect(findGroupFilter().exists()).toBe(showFilters); - expect(findProjectFilter().exists()).toBe(showFilters); - }); + it('always renders Search indicator', () => { + expect(findSearchTypeIndicator().exists()).toBe(true); }); describe.each` @@ -128,15 +108,15 @@ describe('GlobalSearchTopbar', () => { }); describe.each` - state | defaultBranchName | hasSyntaxOptions - ${{ query: { repository_ref: '' }, searchType: 'basic' }} | ${'master'} | ${false} - ${{ query: { repository_ref: 'v0.1' }, searchType: 'basic' }} | ${''} | ${false} - ${{ query: { repository_ref: 'master' }, searchType: 'basic' }} | ${'master'} | ${false} - ${{ query: { repository_ref: 'master' }, searchType: 'advanced' }} | ${''} | ${false} - ${{ query: { repository_ref: '' }, searchType: 'advanced' }} | ${'master'} | ${true} - ${{ query: { repository_ref: 'v0.1' }, searchType: 'advanced' }} | ${''} | ${false} - ${{ query: { repository_ref: 'master' }, searchType: 'advanced' }} | ${'master'} | ${true} - ${{ query: { repository_ref: 'master' }, searchType: 'zoekt' }} | ${'master'} | ${true} + state | hasSyntaxOptions + ${{ query: { repository_ref: '' }, searchType: 'basic', searchLevel: 'project', defaultBranchName: 'master' }} | ${false} + ${{ query: { repository_ref: 'v0.1' }, searchType: 'basic', searchLevel: 'project', defaultBranchName: '' }} | ${false} + ${{ query: { repository_ref: 'master' }, searchType: 'basic', searchLevel: 'project', defaultBranchName: 'master' }} | ${false} + ${{ query: { repository_ref: 'master' }, searchType: 'advanced', searchLevel: 'project', defaultBranchName: '' }} | ${false} + ${{ query: { repository_ref: '' }, searchType: 'advanced', searchLevel: 'project', defaultBranchName: 'master' }} | ${true} + ${{ query: { repository_ref: 'v0.1' }, searchType: 'advanced', searchLevel: 'project', defaultBranchName: '' }} | ${false} + ${{ query: { repository_ref: 'master' }, searchType: 'advanced', searchLevel: 'project', defaultBranchName: 'master' }} | ${true} + ${{ query: { repository_ref: 'master' }, searchType: 'zoekt', searchLevel: 'project', defaultBranchName: 'master' }} | ${true} `( `the syntax option based on component state`, ({ state, defaultBranchName, hasSyntaxOptions }) => { @@ -162,9 +142,10 @@ describe('GlobalSearchTopbar', () => { createComponent(); }); - it('clicking search button inside search box calls applyQuery', () => { - findGlSearchBox().vm.$emit('submit', { preventDefault: () => {} }); + it('clicking search button inside search box calls applyQuery', async () => { + await nextTick(); + findGlSearchBox().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY })); expect(actionSpies.applyQuery).toHaveBeenCalled(); }); }); diff --git a/spec/frontend/search/topbar/components/group_filter_spec.js b/spec/frontend/search/topbar/components/group_filter_spec.js deleted file mode 100644 index fa8036a7f97..00000000000 --- a/spec/frontend/search/topbar/components/group_filter_spec.js +++ /dev/null @@ -1,187 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { MOCK_GROUP, MOCK_QUERY, CURRENT_SCOPE } from 'jest/search/mock_data'; -import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; -import { GROUPS_LOCAL_STORAGE_KEY } from '~/search/store/constants'; -import GroupFilter from '~/search/topbar/components/group_filter.vue'; -import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue'; -import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/topbar/constants'; - -Vue.use(Vuex); - -jest.mock('~/lib/utils/url_utility', () => ({ - visitUrl: jest.fn(), - setUrlParams: jest.fn(), -})); - -describe('GroupFilter', () => { - let wrapper; - - const actionSpies = { - fetchGroups: jest.fn(), - setFrequentGroup: jest.fn(), - loadFrequentGroups: jest.fn(), - }; - - const defaultProps = { - initialData: null, - }; - - const createComponent = (initialState, props) => { - const store = new Vuex.Store({ - state: { - query: MOCK_QUERY, - ...initialState, - }, - actions: actionSpies, - getters: { - frequentGroups: () => [], - currentScope: () => CURRENT_SCOPE, - }, - }); - - wrapper = shallowMount(GroupFilter, { - store, - propsData: { - ...defaultProps, - ...props, - }, - }); - }; - - const findSearchableDropdown = () => wrapper.findComponent(SearchableDropdown); - - describe('template', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders SearchableDropdown always', () => { - expect(findSearchableDropdown().exists()).toBe(true); - }); - }); - - describe('events', () => { - beforeEach(() => { - createComponent(); - }); - - describe('when @search is emitted', () => { - const search = 'test'; - - beforeEach(() => { - findSearchableDropdown().vm.$emit('search', search); - }); - - it('calls fetchGroups with the search paramter', () => { - expect(actionSpies.fetchGroups).toHaveBeenCalledTimes(1); - expect(actionSpies.fetchGroups).toHaveBeenCalledWith(expect.any(Object), search); - }); - }); - - describe('when @change is emitted with Any', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('change', ANY_OPTION); - }); - - it('calls setUrlParams with group null, project id null, nav_source null, and then calls visitUrl', () => { - expect(setUrlParams).toHaveBeenCalledWith({ - [GROUP_DATA.queryParam]: null, - [PROJECT_DATA.queryParam]: null, - nav_source: null, - scope: CURRENT_SCOPE, - }); - - expect(visitUrl).toHaveBeenCalled(); - }); - - it('does not call setFrequentGroup', () => { - expect(actionSpies.setFrequentGroup).not.toHaveBeenCalled(); - }); - }); - - describe('when @change is emitted with a group', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('change', MOCK_GROUP); - }); - - it('calls setUrlParams with group id, project id null, nav_source null, and then calls visitUrl', () => { - expect(setUrlParams).toHaveBeenCalledWith({ - [GROUP_DATA.queryParam]: MOCK_GROUP.id, - [PROJECT_DATA.queryParam]: null, - nav_source: null, - scope: CURRENT_SCOPE, - }); - - expect(visitUrl).toHaveBeenCalled(); - }); - - it(`calls setFrequentGroup with the group and ${GROUPS_LOCAL_STORAGE_KEY}`, () => { - expect(actionSpies.setFrequentGroup).toHaveBeenCalledWith(expect.any(Object), MOCK_GROUP); - }); - }); - - describe('when @first-open is emitted', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('first-open'); - }); - - it('calls loadFrequentGroups', () => { - expect(actionSpies.loadFrequentGroups).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('computed', () => { - describe('selectedGroup', () => { - describe('when initialData is null', () => { - beforeEach(() => { - createComponent(); - }); - - it('sets selectedGroup to ANY_OPTION', () => { - expect(wrapper.vm.selectedGroup).toBe(ANY_OPTION); - }); - }); - - describe('when initialData is set', () => { - beforeEach(() => { - createComponent({}, { initialData: MOCK_GROUP }); - }); - - it('sets selectedGroup to ANY_OPTION', () => { - expect(wrapper.vm.selectedGroup).toBe(MOCK_GROUP); - }); - }); - }); - }); - - describe.each` - navSource | initialData | callMethod - ${null} | ${null} | ${false} - ${null} | ${MOCK_GROUP} | ${false} - ${'navbar'} | ${null} | ${false} - ${'navbar'} | ${MOCK_GROUP} | ${true} - `('onCreate', ({ navSource, initialData, callMethod }) => { - describe(`when nav_source is ${navSource} and ${ - initialData ? 'has' : 'does not have' - } an initial group`, () => { - beforeEach(() => { - createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData }); - }); - - it(`${callMethod ? 'does' : 'does not'} call setFrequentGroup`, () => { - if (callMethod) { - expect(actionSpies.setFrequentGroup).toHaveBeenCalledWith( - expect.any(Object), - initialData, - ); - } else { - expect(actionSpies.setFrequentGroup).not.toHaveBeenCalled(); - } - }); - }); - }); -}); diff --git a/spec/frontend/search/topbar/components/project_filter_spec.js b/spec/frontend/search/topbar/components/project_filter_spec.js deleted file mode 100644 index e7808370098..00000000000 --- a/spec/frontend/search/topbar/components/project_filter_spec.js +++ /dev/null @@ -1,188 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { MOCK_PROJECT, MOCK_QUERY, CURRENT_SCOPE } from 'jest/search/mock_data'; -import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; -import { PROJECTS_LOCAL_STORAGE_KEY } from '~/search/store/constants'; -import ProjectFilter from '~/search/topbar/components/project_filter.vue'; -import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue'; -import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/topbar/constants'; - -Vue.use(Vuex); - -jest.mock('~/lib/utils/url_utility', () => ({ - visitUrl: jest.fn(), - setUrlParams: jest.fn(), -})); - -describe('ProjectFilter', () => { - let wrapper; - - const actionSpies = { - fetchProjects: jest.fn(), - setFrequentProject: jest.fn(), - loadFrequentProjects: jest.fn(), - }; - - const defaultProps = { - initialData: null, - }; - - const createComponent = (initialState, props) => { - const store = new Vuex.Store({ - state: { - query: MOCK_QUERY, - ...initialState, - }, - actions: actionSpies, - getters: { - frequentProjects: () => [], - currentScope: () => CURRENT_SCOPE, - }, - }); - - wrapper = shallowMount(ProjectFilter, { - store, - propsData: { - ...defaultProps, - ...props, - }, - }); - }; - - const findSearchableDropdown = () => wrapper.findComponent(SearchableDropdown); - - describe('template', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders SearchableDropdown always', () => { - expect(findSearchableDropdown().exists()).toBe(true); - }); - }); - - describe('events', () => { - beforeEach(() => { - createComponent(); - }); - - describe('when @search is emitted', () => { - const search = 'test'; - - beforeEach(() => { - findSearchableDropdown().vm.$emit('search', search); - }); - - it('calls fetchProjects with the search paramter', () => { - expect(actionSpies.fetchProjects).toHaveBeenCalledWith(expect.any(Object), search); - }); - }); - - describe('when @change is emitted', () => { - describe('with Any', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('change', ANY_OPTION); - }); - - it('calls setUrlParams with null, no group id, nav_source null, then calls visitUrl', () => { - expect(setUrlParams).toHaveBeenCalledWith({ - [PROJECT_DATA.queryParam]: null, - nav_source: null, - scope: CURRENT_SCOPE, - }); - expect(visitUrl).toHaveBeenCalled(); - }); - - it('does not call setFrequentProject', () => { - expect(actionSpies.setFrequentProject).not.toHaveBeenCalled(); - }); - }); - - describe('with a Project', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('change', MOCK_PROJECT); - }); - - it('calls setUrlParams with project id, group id, nav_source null, then calls visitUrl', () => { - expect(setUrlParams).toHaveBeenCalledWith({ - [GROUP_DATA.queryParam]: MOCK_PROJECT.namespace.id, - [PROJECT_DATA.queryParam]: MOCK_PROJECT.id, - nav_source: null, - scope: CURRENT_SCOPE, - }); - expect(visitUrl).toHaveBeenCalled(); - }); - - it(`calls setFrequentProject with the group and ${PROJECTS_LOCAL_STORAGE_KEY}`, () => { - expect(actionSpies.setFrequentProject).toHaveBeenCalledWith( - expect.any(Object), - MOCK_PROJECT, - ); - }); - }); - }); - - describe('when @first-open is emitted', () => { - beforeEach(() => { - findSearchableDropdown().vm.$emit('first-open'); - }); - - it('calls loadFrequentProjects', () => { - expect(actionSpies.loadFrequentProjects).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('computed', () => { - describe('selectedProject', () => { - describe('when initialData is null', () => { - beforeEach(() => { - createComponent(); - }); - - it('sets selectedProject to ANY_OPTION', () => { - expect(wrapper.vm.selectedProject).toBe(ANY_OPTION); - }); - }); - - describe('when initialData is set', () => { - beforeEach(() => { - createComponent({}, { initialData: MOCK_PROJECT }); - }); - - it('sets selectedProject to the initialData', () => { - expect(wrapper.vm.selectedProject).toBe(MOCK_PROJECT); - }); - }); - }); - }); - - describe.each` - navSource | initialData | callMethod - ${null} | ${null} | ${false} - ${null} | ${MOCK_PROJECT} | ${false} - ${'navbar'} | ${null} | ${false} - ${'navbar'} | ${MOCK_PROJECT} | ${true} - `('onCreate', ({ navSource, initialData, callMethod }) => { - describe(`when nav_source is ${navSource} and ${ - initialData ? 'has' : 'does not have' - } an initial project`, () => { - beforeEach(() => { - createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData }); - }); - - it(`${callMethod ? 'does' : 'does not'} call setFrequentProject`, () => { - if (callMethod) { - expect(actionSpies.setFrequentProject).toHaveBeenCalledWith( - expect.any(Object), - initialData, - ); - } else { - expect(actionSpies.setFrequentProject).not.toHaveBeenCalled(); - } - }); - }); - }); -}); diff --git a/spec/frontend/search/topbar/components/search_type_indicator_spec.js b/spec/frontend/search/topbar/components/search_type_indicator_spec.js new file mode 100644 index 00000000000..d69ca6dfb16 --- /dev/null +++ b/spec/frontend/search/topbar/components/search_type_indicator_spec.js @@ -0,0 +1,128 @@ +import Vue from 'vue'; +// eslint-disable-next-line no-restricted-imports +import Vuex from 'vuex'; +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { MOCK_QUERY } from 'jest/search/mock_data'; +import SearchTypeIndicator from '~/search/topbar/components/search_type_indicator.vue'; + +Vue.use(Vuex); + +describe('SearchTypeIndicator', () => { + let wrapper; + + const actionSpies = { + applyQuery: jest.fn(), + setQuery: jest.fn(), + preloadStoredFrequentItems: jest.fn(), + }; + + const createComponent = (initialState = {}) => { + const store = new Vuex.Store({ + state: { + query: MOCK_QUERY, + ...initialState, + }, + actions: actionSpies, + }); + + wrapper = shallowMountExtended(SearchTypeIndicator, { + store, + stubs: { + GlSprintf, + }, + }); + }; + + const findIndicator = (id) => wrapper.findAllByTestId(id); + const findDocsLink = () => wrapper.findComponentByTestId('docs-link'); + const findSyntaxDocsLink = () => wrapper.findComponentByTestId('syntax-docs-link'); + + // searchType and search level params cobination in this test reflects + // all possible combinations + + describe.each` + searchType | searchLevel | repository | showSearchTypeIndicator + ${'advanced'} | ${'project'} | ${'master'} | ${'advanced-enabled'} + ${'advanced'} | ${'project'} | ${'v0.1'} | ${'advanced-disabled'} + ${'advanced'} | ${'group'} | ${'master'} | ${'advanced-enabled'} + ${'advanced'} | ${'global'} | ${'master'} | ${'advanced-enabled'} + ${'zoekt'} | ${'project'} | ${'master'} | ${'zoekt-enabled'} + ${'zoekt'} | ${'project'} | ${'v0.1'} | ${'zoekt-disabled'} + ${'zoekt'} | ${'group'} | ${'master'} | ${'zoekt-enabled'} + `( + 'search type indicator for $searchType $searchLevel', + ({ searchType, repository, showSearchTypeIndicator, searchLevel }) => { + beforeEach(() => { + createComponent({ + query: { repository_ref: repository }, + searchType, + searchLevel, + defaultBranchName: 'master', + }); + }); + it('renders correctly', () => { + expect(findIndicator(showSearchTypeIndicator).exists()).toBe(true); + }); + }, + ); + + describe.each` + searchType | repository | showSearchTypeIndicator + ${'basic'} | ${'master'} | ${true} + ${'basic'} | ${'v0.1'} | ${true} + `( + 'search type indicator for $searchType and $repository', + ({ searchType, repository, showSearchTypeIndicator }) => { + beforeEach(() => { + createComponent({ + query: { repository_ref: repository }, + searchType, + defaultBranchName: 'master', + }); + }); + it.each(['zoekt-enabled', 'zoekt-disabled', 'advanced-enabled', 'advanced-disabled'])( + 'renders correct indicator %s', + () => { + expect(findIndicator(searchType).exists()).toBe(showSearchTypeIndicator); + }, + ); + }, + ); + + describe.each` + searchType | docsLink + ${'advanced'} | ${'/help/user/search/advanced_search'} + ${'zoekt'} | ${'/help/user/search/exact_code_search'} + `('documentation link for $searchType', ({ searchType, docsLink }) => { + beforeEach(() => { + createComponent({ + query: { repository_ref: 'master' }, + searchType, + searchLevel: 'project', + defaultBranchName: 'master', + }); + }); + it('has correct link', () => { + expect(findDocsLink().attributes('href')).toBe(docsLink); + }); + }); + + describe.each` + searchType | syntaxdocsLink + ${'advanced'} | ${'/help/user/search/advanced_search#use-the-advanced-search-syntax'} + ${'zoekt'} | ${'/help/user/search/exact_code_search#syntax'} + `('Syntax documentation $searchType', ({ searchType, syntaxdocsLink }) => { + beforeEach(() => { + createComponent({ + query: { repository_ref: '000' }, + searchType, + searchLevel: 'project', + defaultBranchName: 'master', + }); + }); + it('has correct link', () => { + expect(findSyntaxDocsLink().attributes('href')).toBe(syntaxdocsLink); + }); + }); +}); diff --git a/spec/frontend/search/topbar/components/searchable_dropdown_item_spec.js b/spec/frontend/search/topbar/components/searchable_dropdown_item_spec.js deleted file mode 100644 index c911fe53d40..00000000000 --- a/spec/frontend/search/topbar/components/searchable_dropdown_item_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import { GlDropdownItem, GlAvatar } from '@gitlab/ui'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { MOCK_GROUPS } from 'jest/search/mock_data'; -import { truncateNamespace } from '~/lib/utils/text_utility'; -import SearchableDropdownItem from '~/search/topbar/components/searchable_dropdown_item.vue'; -import { GROUP_DATA } from '~/search/topbar/constants'; - -describe('Global Search Searchable Dropdown Item', () => { - let wrapper; - - const defaultProps = { - item: MOCK_GROUPS[0], - selectedItem: MOCK_GROUPS[0], - name: GROUP_DATA.name, - fullName: GROUP_DATA.fullName, - }; - - const createComponent = (props) => { - wrapper = shallowMountExtended(SearchableDropdownItem, { - propsData: { - ...defaultProps, - ...props, - }, - }); - }; - - const findGlDropdownItem = () => wrapper.findComponent(GlDropdownItem); - const findGlAvatar = () => wrapper.findComponent(GlAvatar); - const findDropdownTitle = () => wrapper.findByTestId('item-title'); - const findDropdownSubtitle = () => wrapper.findByTestId('item-namespace'); - - describe('template', () => { - describe('always', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders GlDropdownItem', () => { - expect(findGlDropdownItem().exists()).toBe(true); - }); - - it('renders GlAvatar', () => { - expect(findGlAvatar().exists()).toBe(true); - }); - - it('renders Dropdown Title correctly', () => { - const titleEl = findDropdownTitle(); - - expect(titleEl.exists()).toBe(true); - expect(titleEl.text()).toBe(MOCK_GROUPS[0][GROUP_DATA.name]); - }); - - it('renders Dropdown Subtitle correctly', () => { - const subtitleEl = findDropdownSubtitle(); - - expect(subtitleEl.exists()).toBe(true); - expect(subtitleEl.text()).toBe(truncateNamespace(MOCK_GROUPS[0][GROUP_DATA.fullName])); - }); - }); - - describe('when item === selectedItem', () => { - beforeEach(() => { - createComponent({ item: MOCK_GROUPS[0], selectedItem: MOCK_GROUPS[0] }); - }); - - it('marks the dropdown as checked', () => { - expect(findGlDropdownItem().attributes('ischecked')).toBe('true'); - }); - }); - - describe('when item !== selectedItem', () => { - beforeEach(() => { - createComponent({ item: MOCK_GROUPS[0], selectedItem: MOCK_GROUPS[1] }); - }); - - it('marks the dropdown as not checked', () => { - expect(findGlDropdownItem().attributes('ischecked')).toBeUndefined(); - }); - }); - }); - - describe('actions', () => { - beforeEach(() => { - createComponent(); - }); - - it('clicking the dropdown item $emits change with the item', () => { - findGlDropdownItem().vm.$emit('click'); - - expect(wrapper.emitted('change')[0]).toEqual([MOCK_GROUPS[0]]); - }); - }); -}); diff --git a/spec/frontend/search/topbar/components/searchable_dropdown_spec.js b/spec/frontend/search/topbar/components/searchable_dropdown_spec.js deleted file mode 100644 index 5acaa1c1900..00000000000 --- a/spec/frontend/search/topbar/components/searchable_dropdown_spec.js +++ /dev/null @@ -1,220 +0,0 @@ -import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui'; -import { shallowMount, mount } from '@vue/test-utils'; -import Vue, { nextTick } from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import { MOCK_GROUPS, MOCK_GROUP, MOCK_QUERY } from 'jest/search/mock_data'; -import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue'; -import { ANY_OPTION, GROUP_DATA } from '~/search/topbar/constants'; - -Vue.use(Vuex); - -describe('Global Search Searchable Dropdown', () => { - let wrapper; - - const defaultProps = { - headerText: GROUP_DATA.headerText, - name: GROUP_DATA.name, - fullName: GROUP_DATA.fullName, - loading: false, - selectedItem: ANY_OPTION, - items: [], - }; - - const createComponent = (initialState, props, mountFn = shallowMount) => { - const store = new Vuex.Store({ - state: { - query: MOCK_QUERY, - ...initialState, - }, - }); - - wrapper = extendedWrapper( - mountFn(SearchableDropdown, { - store, - propsData: { - ...defaultProps, - ...props, - }, - }), - ); - }; - - const findGlDropdown = () => wrapper.findComponent(GlDropdown); - const findGlDropdownSearch = () => findGlDropdown().findComponent(GlSearchBoxByType); - const findDropdownText = () => findGlDropdown().find('.dropdown-toggle-text'); - const findSearchableDropdownItems = () => wrapper.findAllByTestId('searchable-items'); - const findFrequentDropdownItems = () => wrapper.findAllByTestId('frequent-items'); - const findAnyDropdownItem = () => findGlDropdown().findComponent(GlDropdownItem); - const findFirstSearchableDropdownItem = () => findSearchableDropdownItems().at(0); - const findFirstFrequentDropdownItem = () => findFrequentDropdownItems().at(0); - const findLoader = () => wrapper.findComponent(GlSkeletonLoader); - - describe('template', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders GlDropdown', () => { - expect(findGlDropdown().exists()).toBe(true); - }); - - describe('findGlDropdownSearch', () => { - it('renders always', () => { - expect(findGlDropdownSearch().exists()).toBe(true); - }); - - it('has debounce prop', () => { - expect(findGlDropdownSearch().attributes('debounce')).toBe('500'); - }); - - describe('onSearch', () => { - const search = 'test search'; - - beforeEach(() => { - findGlDropdownSearch().vm.$emit('input', search); - }); - - it('$emits @search when input event is fired from GlSearchBoxByType', () => { - expect(wrapper.emitted('search')[0]).toEqual([search]); - }); - }); - }); - - describe('Searchable Dropdown Items', () => { - describe('when loading is false', () => { - beforeEach(() => { - createComponent({}, { items: MOCK_GROUPS }); - }); - - it('does not render loader', () => { - expect(findLoader().exists()).toBe(false); - }); - - it('renders the Any Dropdown', () => { - expect(findAnyDropdownItem().exists()).toBe(true); - }); - - it('renders searchable dropdown item for each item', () => { - expect(findSearchableDropdownItems()).toHaveLength(MOCK_GROUPS.length); - }); - }); - - describe('when loading is true', () => { - beforeEach(() => { - createComponent({}, { loading: true, items: MOCK_GROUPS }); - }); - - it('does render loader', () => { - expect(findLoader().exists()).toBe(true); - }); - - it('renders the Any Dropdown', () => { - expect(findAnyDropdownItem().exists()).toBe(true); - }); - - it('does not render searchable dropdown items', () => { - expect(findSearchableDropdownItems()).toHaveLength(0); - }); - }); - }); - - describe.each` - searchText | frequentItems | length - ${''} | ${[]} | ${0} - ${''} | ${MOCK_GROUPS} | ${MOCK_GROUPS.length} - ${'test'} | ${[]} | ${0} - ${'test'} | ${MOCK_GROUPS} | ${0} - `('Frequent Dropdown Items', ({ searchText, frequentItems, length }) => { - describe(`when search is ${searchText} and frequentItems length is ${frequentItems.length}`, () => { - beforeEach(() => { - createComponent({}, { frequentItems }); - findGlDropdownSearch().vm.$emit('input', searchText); - }); - - it(`should${length ? '' : ' not'} render frequent dropdown items`, () => { - expect(findFrequentDropdownItems()).toHaveLength(length); - }); - }); - }); - - describe('Dropdown Text', () => { - describe('when selectedItem is any', () => { - beforeEach(() => { - createComponent({}, {}, mount); - }); - - it('sets dropdown text to Any', () => { - expect(findDropdownText().text()).toBe(ANY_OPTION.name); - }); - }); - - describe('selectedItem is set', () => { - beforeEach(() => { - createComponent({}, { selectedItem: MOCK_GROUP }, mount); - }); - - it('sets dropdown text to the selectedItem name', () => { - expect(findDropdownText().text()).toBe(MOCK_GROUP[GROUP_DATA.name]); - }); - }); - }); - }); - - describe('actions', () => { - beforeEach(() => { - createComponent({}, { items: MOCK_GROUPS, frequentItems: MOCK_GROUPS }); - }); - - it('clicking "Any" dropdown item $emits @change with ANY_OPTION', () => { - findAnyDropdownItem().vm.$emit('click'); - - expect(wrapper.emitted('change')[0]).toEqual([ANY_OPTION]); - }); - - it('on searchable item @change, the wrapper $emits change with the item', () => { - findFirstSearchableDropdownItem().vm.$emit('change', MOCK_GROUPS[0]); - - expect(wrapper.emitted('change')[0]).toEqual([MOCK_GROUPS[0]]); - }); - - it('on frequent item @change, the wrapper $emits change with the item', () => { - findFirstFrequentDropdownItem().vm.$emit('change', MOCK_GROUPS[0]); - - expect(wrapper.emitted('change')[0]).toEqual([MOCK_GROUPS[0]]); - }); - - describe('opening the dropdown', () => { - beforeEach(() => { - findGlDropdown().vm.$emit('show'); - }); - - it('$emits @search and @first-open on the first open', () => { - expect(wrapper.emitted('search')[0]).toStrictEqual(['']); - expect(wrapper.emitted('first-open')[0]).toStrictEqual([]); - }); - - describe('when the dropdown has been opened', () => { - it('$emits @search with the searchText', async () => { - const searchText = 'foo'; - - findGlDropdownSearch().vm.$emit('input', searchText); - await nextTick(); - - expect(wrapper.emitted('search')[1]).toStrictEqual([searchText]); - expect(wrapper.emitted('first-open')).toHaveLength(1); - }); - - it('does not emit @first-open again', async () => { - expect(wrapper.emitted('first-open')).toHaveLength(1); - - findGlDropdownSearch().vm.$emit('input'); - await nextTick(); - - expect(wrapper.emitted('first-open')).toHaveLength(1); - }); - }); - }); - }); -}); |