From 8ae36d93f1a63874b584f0488fde88c1fee999c4 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 16 Jun 2023 09:09:20 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- spec/frontend/search/mock_data.js | 1 + .../components/label_dropdown_items_spec.js | 57 ++++ .../search/sidebar/components/label_filter_spec.js | 322 +++++++++++++++++++++ spec/frontend/search/store/actions_spec.js | 2 +- spec/frontend/search/store/getters_spec.js | 4 - .../command_autocomplete_item_spec.js.snap | 19 -- .../__snapshots__/search_item_spec.js.snap | 122 ++++++++ .../user_autocomplete_item_spec.js.snap | 34 --- .../command_autocomplete_item_spec.js | 25 -- .../command_palette/command_palette_items_spec.js | 38 +-- .../command_palette/fake_search_input_spec.js | 14 +- .../global_search/command_palette/mock_data.js | 54 +++- .../command_palette/search_item_spec.js | 33 +++ .../command_palette/user_autocomplete_item_spec.js | 25 -- .../global_search/command_palette/utils_spec.js | 27 +- .../global_search/components/global_search_spec.js | 7 +- 16 files changed, 633 insertions(+), 151 deletions(-) create mode 100644 spec/frontend/search/sidebar/components/label_dropdown_items_spec.js create mode 100644 spec/frontend/search/sidebar/components/label_filter_spec.js delete mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/command_autocomplete_item_spec.js.snap create mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/search_item_spec.js.snap delete mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap delete mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/command_autocomplete_item_spec.js create mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/search_item_spec.js delete mode 100644 spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js (limited to 'spec/frontend') diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index 58824f8023d..7cf8633d749 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -8,6 +8,7 @@ export const MOCK_QUERY = { group_id: 1, language: ['C', 'JavaScript'], labels: ['60', '37'], + search: '*', }; export const MOCK_GROUP = { diff --git a/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js b/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js new file mode 100644 index 00000000000..135b12956b2 --- /dev/null +++ b/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js @@ -0,0 +1,57 @@ +import { GlFormCheckbox } from '@gitlab/ui'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { shallowMount } from '@vue/test-utils'; +import { PROCESS_LABELS_DATA } from 'jest/search/mock_data'; +import LabelDropdownItems from '~/search/sidebar/components/label_filter/label_dropdown_items.vue'; + +Vue.use(Vuex); + +describe('LabelDropdownItems', () => { + let wrapper; + + const defaultProps = { + labels: PROCESS_LABELS_DATA, + }; + + const createComponent = (Props = defaultProps) => { + wrapper = shallowMount(LabelDropdownItems, { + propsData: { + ...Props, + }, + }); + }; + + const findAllLabelItems = () => wrapper.findAll('.label-filter-menu-item'); + const findFirstLabelCheckbox = () => findAllLabelItems().at(0).findComponent(GlFormCheckbox); + const findFirstLabelTitle = () => findAllLabelItems().at(0).findComponent('.label-title'); + const findFirstLabelColor = () => + findAllLabelItems().at(0).findComponent('[data-testid="label-color-indicator"]'); + + describe('Renders correctly', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders items', () => { + expect(findAllLabelItems().exists()).toBe(true); + expect(findAllLabelItems()).toHaveLength(defaultProps.labels.length); + }); + + it('renders items checkbox', () => { + expect(findFirstLabelCheckbox().exists()).toBe(true); + }); + + it('renders label title', () => { + expect(findFirstLabelTitle().exists()).toBe(true); + expect(findFirstLabelTitle().text()).toBe(defaultProps.labels[0].title); + }); + + it('renders label color', () => { + expect(findFirstLabelColor().exists()).toBe(true); + expect(findFirstLabelColor().attributes('style')).toBe( + `background-color: ${defaultProps.labels[0].color};`, + ); + }); + }); +}); diff --git a/spec/frontend/search/sidebar/components/label_filter_spec.js b/spec/frontend/search/sidebar/components/label_filter_spec.js new file mode 100644 index 00000000000..c5df374d4ef --- /dev/null +++ b/spec/frontend/search/sidebar/components/label_filter_spec.js @@ -0,0 +1,322 @@ +import { + GlAlert, + GlLoadingIcon, + GlSearchBoxByType, + GlLabel, + GlDropdownForm, + GlFormCheckboxGroup, + GlDropdownSectionHeader, + GlDropdownDivider, +} from '@gitlab/ui'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import { MOCK_QUERY, MOCK_LABEL_AGGREGATIONS } from 'jest/search/mock_data'; +import LabelFilter from '~/search/sidebar/components/label_filter/index.vue'; +import LabelDropdownItems from '~/search/sidebar/components/label_filter/label_dropdown_items.vue'; + +import * as actions from '~/search/store/actions'; +import * as getters from '~/search/store/getters'; +import mutations from '~/search/store/mutations'; +import createState from '~/search/store/state'; + +import { + TRACKING_LABEL_FILTER, + TRACKING_LABEL_DROPDOWN, + TRACKING_LABEL_CHECKBOX, + TRACKING_ACTION_SELECT, + TRACKING_ACTION_SHOW, +} from '~/search/sidebar/components/label_filter/tracking'; + +import { labelFilterData } from '~/search/sidebar/components/label_filter/data'; + +import { + RECEIVE_AGGREGATIONS_SUCCESS, + REQUEST_AGGREGATIONS, + RECEIVE_AGGREGATIONS_ERROR, +} from '~/search/store/mutation_types'; + +Vue.use(Vuex); + +const actionSpies = { + fetchAllAggregation: jest.fn(), + setQuery: jest.fn(), + closeLabel: jest.fn(), + setLabelFilterSearch: jest.fn(), +}; + +describe('GlobalSearchSidebarLabelFilter', () => { + let wrapper; + let trackingSpy; + let config; + let store; + + const createComponent = (initialState) => { + config = { + actions: { + ...actions, + fetchAllAggregation: actionSpies.fetchAllAggregation, + closeLabel: actionSpies.closeLabel, + setLabelFilterSearch: actionSpies.setLabelFilterSearch, + setQuery: actionSpies.setQuery, + }, + getters, + mutations, + state: createState({ + query: MOCK_QUERY, + aggregations: MOCK_LABEL_AGGREGATIONS, + ...initialState, + }), + }; + + store = new Vuex.Store(config); + + wrapper = mountExtended(LabelFilter, { + store, + provide: { + glFeatures: { + searchIssueLabelAggregation: true, + }, + }, + }); + }; + + const findComponentTitle = () => wrapper.findComponentByTestId('label-filter-title'); + const findAllSelectedLabelsAbove = () => wrapper.findAllComponents(GlLabel); + const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); + const findDropdownForm = () => wrapper.findComponent(GlDropdownForm); + const findCheckboxGroup = () => wrapper.findComponent(GlFormCheckboxGroup); + const findDropdownSectionHeader = () => wrapper.findComponent(GlDropdownSectionHeader); + const findDivider = () => wrapper.findComponent(GlDropdownDivider); + const findCheckboxFilter = () => wrapper.findAllComponents(LabelDropdownItems); + const findAlert = () => wrapper.findComponent(GlAlert); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + + describe('Renders correctly closed', () => { + beforeEach(async () => { + createComponent(); + store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data); + + await Vue.nextTick(); + }); + + it('renders component title', () => { + expect(findComponentTitle().exists()).toBe(true); + }); + + it('renders selected labels above search box', () => { + expect(findAllSelectedLabelsAbove().exists()).toBe(true); + expect(findAllSelectedLabelsAbove()).toHaveLength(2); + }); + + it('renders search box', () => { + expect(findSearchBox().exists()).toBe(true); + }); + + it("doesn't render dropdown form", () => { + expect(findDropdownForm().exists()).toBe(false); + }); + + it("doesn't render checkbox group", () => { + expect(findCheckboxGroup().exists()).toBe(false); + }); + + it("doesn't render dropdown section header", () => { + expect(findDropdownSectionHeader().exists()).toBe(false); + }); + + it("doesn't render divider", () => { + expect(findDivider().exists()).toBe(false); + }); + + it("doesn't render checkbox filter", () => { + expect(findCheckboxFilter().exists()).toBe(false); + }); + + it("doesn't render alert", () => { + expect(findAlert().exists()).toBe(false); + }); + + it("doesn't render loading icon", () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + }); + + describe('Renders correctly opened', () => { + beforeEach(async () => { + createComponent(); + store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data); + + await Vue.nextTick(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + findSearchBox().vm.$emit('focusin'); + }); + + afterEach(() => { + unmockTracking(); + }); + + it('renders component title', () => { + expect(findComponentTitle().exists()).toBe(true); + }); + + it('renders selected labels above search box', () => { + // default data need to provide at least two selected labels + expect(findAllSelectedLabelsAbove().exists()).toBe(true); + expect(findAllSelectedLabelsAbove()).toHaveLength(2); + }); + + it('renders search box', () => { + expect(findSearchBox().exists()).toBe(true); + }); + + it('renders dropdown form', () => { + expect(findDropdownForm().exists()).toBe(true); + }); + + it('renders checkbox group', () => { + expect(findCheckboxGroup().exists()).toBe(true); + }); + + it('renders dropdown section header', () => { + expect(findDropdownSectionHeader().exists()).toBe(true); + }); + + it('renders divider', () => { + expect(findDivider().exists()).toBe(true); + }); + + it('renders checkbox filter', () => { + expect(findCheckboxFilter().exists()).toBe(true); + }); + + it("doesn't render alert", () => { + expect(findAlert().exists()).toBe(false); + }); + + it("doesn't render loading icon", () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + + it('sends tracking information when dropdown is opened', () => { + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SHOW, TRACKING_LABEL_DROPDOWN, { + label: TRACKING_LABEL_DROPDOWN, + }); + }); + }); + + describe('Renders loading state correctly', () => { + beforeEach(async () => { + createComponent(); + store.commit(REQUEST_AGGREGATIONS); + await Vue.nextTick(); + + findSearchBox().vm.$emit('focusin'); + }); + + it('renders checkbox filter', () => { + expect(findCheckboxFilter().exists()).toBe(false); + }); + + it("doesn't render alert", () => { + expect(findAlert().exists()).toBe(false); + }); + + it('renders loading icon', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); + }); + + describe('Renders error state correctly', () => { + beforeEach(async () => { + createComponent(); + store.commit(RECEIVE_AGGREGATIONS_ERROR); + await Vue.nextTick(); + + findSearchBox().vm.$emit('focusin'); + }); + + it("doesn't render checkbox filter", () => { + expect(findCheckboxFilter().exists()).toBe(false); + }); + + it('renders alert', () => { + expect(findAlert().exists()).toBe(true); + }); + + it("doesn't render loading icon", () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + }); + + describe('Actions', () => { + describe('dispatch action when component is created', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders checkbox filter', async () => { + await Vue.nextTick(); + expect(actionSpies.fetchAllAggregation).toHaveBeenCalled(); + }); + }); + + describe('Closing label works correctly', () => { + beforeEach(async () => { + createComponent(); + store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data); + await Vue.nextTick(); + }); + + it('renders checkbox filter', async () => { + await findAllSelectedLabelsAbove().at(0).find('.btn-reset').trigger('click'); + expect(actionSpies.closeLabel).toHaveBeenCalled(); + }); + }); + + describe('label search input box works properly', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders checkbox filter', () => { + findSearchBox().find('input').setValue('test'); + expect(actionSpies.setLabelFilterSearch).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + value: 'test', + }), + ); + }); + }); + + describe('dropdown checkboxes work', () => { + beforeEach(async () => { + createComponent(); + + await findSearchBox().vm.$emit('focusin'); + await Vue.nextTick(); + + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + + await findCheckboxGroup().vm.$emit('input', 6); + await Vue.nextTick(); + }); + + it('trigger event', () => { + expect(actionSpies.setQuery).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ key: labelFilterData?.filterParam, value: 6 }), + ); + }); + + it('sends tracking information when checkbox is selected', () => { + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SELECT, TRACKING_LABEL_CHECKBOX, { + label: TRACKING_LABEL_FILTER, + property: 6, + }); + }); + }); + }); +}); diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js index 18f7e1ce21c..2051e731647 100644 --- a/spec/frontend/search/store/actions_spec.js +++ b/spec/frontend/search/store/actions_spec.js @@ -133,7 +133,7 @@ describe('Global Search Store Actions', () => { describe('when groupId is set', () => { it('calls Api.groupProjects with expected parameters', () => { - actions.fetchProjects({ commit: mockCommit, state }, undefined); + actions.fetchProjects({ commit: mockCommit, state }, MOCK_QUERY.search); expect(Api.groupProjects).toHaveBeenCalledWith(state.query.group_id, state.query.search, { order_by: 'similarity', include_subgroups: true, diff --git a/spec/frontend/search/store/getters_spec.js b/spec/frontend/search/store/getters_spec.js index 51692cb1ab4..772acb39a57 100644 --- a/spec/frontend/search/store/getters_spec.js +++ b/spec/frontend/search/store/getters_spec.js @@ -33,10 +33,6 @@ describe('Global Search Store Getters', () => { useMockLocationHelper(); }); - afterEach(() => { - state = cloneDeep(defaultState); - }); - describe('frequentGroups', () => { it('returns the correct data', () => { state.frequentItems[GROUPS_LOCAL_STORAGE_KEY] = MOCK_GROUPS; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/command_autocomplete_item_spec.js.snap b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/command_autocomplete_item_spec.js.snap deleted file mode 100644 index 3c52cc195db..00000000000 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/command_autocomplete_item_spec.js.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CommandAutocompleteItem should render user item 1`] = ` -
- - - - Manage > Activity - -
-`; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/search_item_spec.js.snap b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/search_item_spec.js.snap new file mode 100644 index 00000000000..d16d137db2f --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/search_item_spec.js.snap @@ -0,0 +1,122 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SearchItem should render the item 1`] = ` +
+
+`; + +exports[`SearchItem should render the item 2`] = ` +
+ + + + + + + Manage > Activity + + + + +
+`; + +exports[`SearchItem should render the item 3`] = ` +
+
+`; + +exports[`SearchItem should render the item 4`] = ` +
+
+`; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap deleted file mode 100644 index 431cdce2955..00000000000 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UserAutocompleteItem should render user item 1`] = ` -
-
-`; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/command_autocomplete_item_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/command_autocomplete_item_spec.js deleted file mode 100644 index 2597812acaf..00000000000 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/command_autocomplete_item_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import CommandAutocompleteItem from '~/super_sidebar/components/global_search/command_palette/command_autocomplete_item.vue'; -import { linksReducer } from '~/super_sidebar/components/global_search/command_palette/utils'; -import { LINKS } from './mock_data'; - -describe('CommandAutocompleteItem', () => { - let wrapper; - - const createComponent = () => { - wrapper = shallowMount(CommandAutocompleteItem, { - propsData: { - command: LINKS.reduce(linksReducer, [])[1], - searchQuery: 'root', - }, - }); - }; - - beforeEach(() => { - createComponent(); - }); - - it('should render user item', () => { - expect(wrapper.element).toMatchSnapshot(); - }); -}); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js index bd1e0dbc15d..21d085dc0fb 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js @@ -7,11 +7,13 @@ import { COMMAND_HANDLE, USERS_GROUP_TITLE, USER_HANDLE, + SEARCH_SCOPE, } from '~/super_sidebar/components/global_search/command_palette/constants'; import { - userMapper, + commandMapper, linksReducer, } from '~/super_sidebar/components/global_search/command_palette/utils'; +import { getFormattedItem } from '~/super_sidebar/components/global_search/utils'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import waitForPromises from 'helpers/wait_for_promises'; @@ -21,6 +23,8 @@ const links = LINKS.reduce(linksReducer, []); describe('CommandPaletteItems', () => { let wrapper; + const autocompletePath = '/autocomplete'; + const searchContext = { project: { id: 1 }, group: { id: 2 } }; const createComponent = (props) => { wrapper = shallowMount(CommandPaletteItems, { @@ -36,32 +40,34 @@ describe('CommandPaletteItems', () => { provide: { commandPaletteCommands: COMMANDS, commandPaletteLinks: LINKS, + autocompletePath, + searchContext, }, }); }; const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem); - const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup); + const findGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup); const findLoader = () => wrapper.findComponent(GlLoadingIcon); describe('COMMANDS & LINKS', () => { it('renders all commands initially', () => { createComponent(); - const commandGroup = COMMANDS[0]; + const commandGroup = COMMANDS.map(commandMapper)[0]; expect(findItems()).toHaveLength(commandGroup.items.length); - expect(findGroup().props('group')).toEqual({ + expect(findGroups().at(0).props('group')).toEqual({ name: commandGroup.name, items: commandGroup.items, }); }); describe('with search query', () => { - it('should filter comamnds and links by the search query', async () => { + it('should filter commands and links by the search query', async () => { jest.spyOn(fuzzaldrinPlus, 'filter'); createComponent({ searchQuery: 'mr' }); const searchQuery = 'todo'; await wrapper.setProps({ searchQuery }); - const commandGroup = COMMANDS[0]; + const commandGroup = COMMANDS.map(commandMapper)[0]; expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith( commandGroup.items, searchQuery, @@ -84,14 +90,14 @@ describe('CommandPaletteItems', () => { }); }); - describe('USERS', () => { + describe('USERS, ISSUES, PROJECTS', () => { let mockAxios; beforeEach(() => { mockAxios = new MockAdapter(axios); }); - it('should NOT start search for users by the search query which is less than 3 chars', () => { + it('should NOT start search by the search query which is less than 3 chars', () => { jest.spyOn(axios, 'get'); const searchQuery = 'us'; createComponent({ handle: USER_HANDLE, searchQuery }); @@ -101,24 +107,18 @@ describe('CommandPaletteItems', () => { expect(findLoader().exists()).toBe(false); }); - it('should start search for users by the search query with 3+ chars and display a loader', () => { + it('should start scoped search with 3+ chars and display a loader', () => { jest.spyOn(axios, 'get'); const searchQuery = 'user'; createComponent({ handle: USER_HANDLE, searchQuery }); expect(axios.get).toHaveBeenCalledWith( - expect.any(String), - expect.objectContaining({ - params: { - search: searchQuery, - }, - }), + `${autocompletePath}?term=${searchQuery}&project_id=${searchContext.project.id}&filter=search&scope=${SEARCH_SCOPE[USER_HANDLE]}`, ); - expect(findLoader().exists()).toBe(true); }); - it('should render returned users', async () => { + it('should render returned items', async () => { mockAxios.onGet().replyOnce(HTTP_STATUS_OK, USERS); const searchQuery = 'user'; @@ -126,9 +126,9 @@ describe('CommandPaletteItems', () => { await waitForPromises(); expect(findItems()).toHaveLength(USERS.length); - expect(findGroup().props('group')).toEqual({ + expect(findGroups().at(0).props('group')).toMatchObject({ name: USERS_GROUP_TITLE, - items: USERS.map(userMapper), + items: USERS.map(getFormattedItem), }); }); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/fake_search_input_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/fake_search_input_spec.js index 0aeb4c89d06..a8e91395303 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/fake_search_input_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/fake_search_input_spec.js @@ -1,8 +1,9 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import FakeSearchInput from '~/super_sidebar/components/global_search/command_palette/fake_search_input.vue'; import { + SEARCH_SCOPE_PLACEHOLDER, + COMMON_HANDLES, COMMAND_HANDLE, - SEARCH_SCOPE, } from '~/super_sidebar/components/global_search/command_palette/constants'; describe('FakeSearchInput', () => { @@ -27,10 +28,13 @@ describe('FakeSearchInput', () => { }); describe('placeholder', () => { - it('should render the placeholder for its search scope when there is no user input', () => { - createComponent(); - expect(findSearchScopePlaceholder().text()).toBe(SEARCH_SCOPE[COMMAND_HANDLE]); - }); + it.each(COMMON_HANDLES)( + 'should render the placeholder for the %s scope when there is no user input', + (scope) => { + createComponent({ scope }); + expect(findSearchScopePlaceholder().text()).toBe(SEARCH_SCOPE_PLACEHOLDER[scope]); + }, + ); it('should NOT render the placeholder when there is user input', () => { createComponent({ userInput: 'todo' }); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js index 726339f56a1..ec65a43d549 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js @@ -5,17 +5,19 @@ export const COMMANDS = [ { text: 'New project/repository', href: '/projects/new', - keywords: ['new', 'project', 'repository'], }, { text: 'New group', href: '/groups/new', - keywords: ['new', 'group'], }, { text: 'New snippet', href: '/-/snippets/new', - keywords: ['new', 'snippet'], + }, + { + text: 'Invite members', + href: '/-/snippets/new', + component: 'invite_members', }, ], }, @@ -61,6 +63,33 @@ export const LINKS = [ }, ]; +export const TRANSFORMED_LINKS = [ + { + href: '/flightjs/Flight/activity', + icon: 'users', + keywords: 'Manage', + text: 'Manage', + }, + { + href: '/flightjs/Flight/activity', + icon: 'users', + keywords: 'Activity', + text: 'Manage > Activity', + }, + { + href: '/flightjs/Flight/-/project_members', + icon: 'users', + keywords: 'Members', + text: 'Manage > Members', + }, + { + href: '/flightjs/Flight/-/labels', + icon: 'users', + keywords: 'Labels', + text: 'Manage > Labels', + }, +]; + export const USERS = [ { id: 37, @@ -83,3 +112,22 @@ export const USERS = [ web_url: 'http://127.0.0.1:3000/reported_user_7', }, ]; + +export const PROJECT = { + category: 'Projects', + id: 1, + label: 'Gitlab Org / MockProject1', + value: 'MockProject1', + url: 'project/1', + avatar_url: '/project/avatar/1/avatar.png', +}; + +export const ISSUE = { + avatar_url: '', + category: 'Recent issues', + id: 516, + label: 'Dismiss Cipher with no integrity', + project_id: 7, + project_name: 'Flight', + url: '/flightjs/Flight/-/issues/37', +}; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/search_item_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/search_item_spec.js new file mode 100644 index 00000000000..c7e49310588 --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/search_item_spec.js @@ -0,0 +1,33 @@ +import { shallowMount } from '@vue/test-utils'; +import SearchItem from '~/super_sidebar/components/global_search/command_palette/search_item.vue'; +import { getFormattedItem } from '~/super_sidebar/components/global_search/utils'; +import { linksReducer } from '~/super_sidebar/components/global_search/command_palette/utils'; +import { USERS, LINKS, PROJECT, ISSUE } from './mock_data'; + +jest.mock('~/lib/utils/highlight', () => ({ + __esModule: true, + default: (text) => text, +})); +const mockUser = getFormattedItem(USERS[0]); +const mockCommand = LINKS.reduce(linksReducer, [])[1]; +const mockProject = getFormattedItem(PROJECT); +const mockIssue = getFormattedItem(ISSUE); + +describe('SearchItem', () => { + let wrapper; + + const createComponent = (item) => { + wrapper = shallowMount(SearchItem, { + propsData: { + item, + searchQuery: 'root', + }, + }); + }; + + it.each([mockUser, mockCommand, mockProject, mockIssue])('should render the item', (item) => { + createComponent(item); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js deleted file mode 100644 index 5abb56228a4..00000000000 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import UserAutocompleteItem from '~/super_sidebar/components/global_search/command_palette/user_autocomplete_item.vue'; -import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils'; -import { USERS } from './mock_data'; - -describe('UserAutocompleteItem', () => { - let wrapper; - - const createComponent = () => { - wrapper = shallowMount(UserAutocompleteItem, { - propsData: { - user: USERS.map(userMapper)[0], - searchQuery: 'root', - }, - }); - }; - - beforeEach(() => { - createComponent(); - }); - - it('should render user item', () => { - expect(wrapper.element).toMatchSnapshot(); - }); -}); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js index 74a5247add5..0b75787723e 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js @@ -1,15 +1,18 @@ -import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils'; -import { USERS } from './mock_data'; +import { + commandMapper, + linksReducer, +} from '~/super_sidebar/components/global_search/command_palette/utils'; +import { COMMANDS, LINKS, TRANSFORMED_LINKS } from './mock_data'; -describe('userMapper', () => { - it('should transform users response', () => { - const user = USERS[0]; - expect(userMapper(user)).toEqual({ - id: user.id, - username: user.username, - text: user.name, - href: user.web_url, - avatar_url: user.avatar_url, - }); +describe('linksReducer', () => { + it('should transform links', () => { + expect(LINKS.reduce(linksReducer, [])).toEqual(TRANSFORMED_LINKS); + }); +}); + +describe('commandMapper', () => { + it('should temporarily remove the `invite_members` item', () => { + const initialCommandsLength = COMMANDS[0].items.length; + expect(COMMANDS.map(commandMapper)[0].items).toHaveLength(initialCommandsLength - 1); }); }); diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js index 0a6d5919207..9b7b9e288df 100644 --- a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js @@ -11,8 +11,7 @@ import FakeSearchInput from '~/super_sidebar/components/global_search/command_pa import CommandPaletteItems from '~/super_sidebar/components/global_search/command_palette/command_palette_items.vue'; import { SEARCH_OR_COMMAND_MODE_PLACEHOLDER, - COMMAND_HANDLE, - USER_HANDLE, + COMMON_HANDLES, } from '~/super_sidebar/components/global_search/command_palette/constants'; import { SEARCH_INPUT_DESCRIPTION, @@ -320,8 +319,8 @@ describe('GlobalSearchModal', () => { }); }); - describe.each([COMMAND_HANDLE, USER_HANDLE])( - 'when FF `command_palette` is enabled', + describe.each(COMMON_HANDLES)( + 'when FF `command_palette` is enabled and search handle is %s', (handle) => { beforeEach(() => { createComponent({ search: handle }, undefined, undefined, { -- cgit v1.2.3