Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-16 12:09:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-16 12:09:20 +0300
commit8ae36d93f1a63874b584f0488fde88c1fee999c4 (patch)
treef1788ba1a7fb00248ff008f817f6feea89304339 /spec/frontend
parentb394e58cc2e52e17e7991e3d7b705aa383c362ee (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/search/mock_data.js1
-rw-r--r--spec/frontend/search/sidebar/components/label_dropdown_items_spec.js57
-rw-r--r--spec/frontend/search/sidebar/components/label_filter_spec.js322
-rw-r--r--spec/frontend/search/store/actions_spec.js2
-rw-r--r--spec/frontend/search/store/getters_spec.js4
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/command_autocomplete_item_spec.js.snap19
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/search_item_spec.js.snap122
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap34
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/command_autocomplete_item_spec.js25
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js38
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/fake_search_input_spec.js14
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js54
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/search_item_spec.js33
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js25
-rw-r--r--spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js27
-rw-r--r--spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js7
16 files changed, 633 insertions, 151 deletions
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`] = `
-<div
- class="gl-display-flex gl-align-items-center"
->
- <gl-icon-stub
- class="gl-mr-3"
- name="users"
- size="16"
- />
-
- <span
- class="gl-text-gray-900"
- >
- Manage &gt; Activity
- </span>
-</div>
-`;
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`] = `
+<div
+ class="gl-display-flex gl-align-items-center"
+>
+ <gl-avatar-stub
+ alt="avatar"
+ aria-hidden="true"
+ class="gl-mr-3"
+ entityid="37"
+ entityname=""
+ shape="rect"
+ size="16"
+ src="https://www.gravatar.com/avatar/a9638f4ec70148d51e56bf05ad41e993?s=80&d=identicon"
+ />
+
+ <!---->
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-text-gray-900"
+ />
+
+ <!---->
+ </span>
+</div>
+`;
+
+exports[`SearchItem should render the item 2`] = `
+<div
+ class="gl-display-flex gl-align-items-center"
+>
+ <!---->
+
+ <gl-icon-stub
+ class="gl-mr-3"
+ name="users"
+ size="16"
+ />
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-text-gray-900"
+ >
+ Manage &gt; Activity
+ </span>
+
+ <!---->
+ </span>
+</div>
+`;
+
+exports[`SearchItem should render the item 3`] = `
+<div
+ class="gl-display-flex gl-align-items-center"
+>
+ <gl-avatar-stub
+ alt="avatar"
+ aria-hidden="true"
+ class="gl-mr-3"
+ entityid="1"
+ entityname="MockProject1"
+ shape="rect"
+ size="32"
+ src="/project/avatar/1/avatar.png"
+ />
+
+ <!---->
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-text-gray-900"
+ >
+ MockProject1
+ </span>
+
+ <span
+ class="gl-font-sm gl-text-gray-500"
+ >
+ Gitlab Org / MockProject1
+ </span>
+ </span>
+</div>
+`;
+
+exports[`SearchItem should render the item 4`] = `
+<div
+ class="gl-display-flex gl-align-items-center"
+>
+ <gl-avatar-stub
+ alt="avatar"
+ aria-hidden="true"
+ class="gl-mr-3"
+ entityid="7"
+ entityname="Flight"
+ shape="rect"
+ size="16"
+ src=""
+ />
+
+ <!---->
+
+ <span
+ class="gl-display-flex gl-flex-direction-column"
+ >
+ <span
+ class="gl-text-gray-900"
+ >
+ Dismiss Cipher with no integrity
+ </span>
+
+ <!---->
+ </span>
+</div>
+`;
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`] = `
-<div
- class="gl-display-flex gl-align-items-center"
->
- <gl-avatar-stub
- alt="avatar"
- aria-hidden="true"
- class="gl-mr-3"
- entityid="37"
- entityname="Cole Dickinson"
- shape="rect"
- size="16"
- src="https://www.gravatar.com/avatar/a9638f4ec70148d51e56bf05ad41e993?s=80&d=identicon"
- />
-
- <span
- class="gl-display-flex gl-flex-direction-column"
- >
- <span
- class="gl-text-gray-900"
- >
- Cole Dickinson
- </span>
-
- <span
- class="gl-font-sm gl-text-gray-500"
- >
- reported_user_14
- </span>
- </span>
-</div>
-`;
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, {