summaryrefslogtreecommitdiff
path: root/spec/frontend/search
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-09 12:09:48 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-09 12:09:48 +0000
commit3c53fbc50bf8d084f1184836468850d2a83ef920 (patch)
tree85c451a4082e7b5e8dc3ecb6265edb1aef0b14f0 /spec/frontend/search
parente7462f7b49a60b2ee7be14682c23190f7f7c5ba7 (diff)
downloadgitlab-ce-3c53fbc50bf8d084f1184836468850d2a83ef920.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/search')
-rw-r--r--spec/frontend/search/mock_data.js25
-rw-r--r--spec/frontend/search/store/actions_spec.js44
-rw-r--r--spec/frontend/search/store/mutations_spec.js36
-rw-r--r--spec/frontend/search/topbar/components/app_spec.js14
-rw-r--r--spec/frontend/search/topbar/components/scope_tabs_spec.js122
5 files changed, 231 insertions, 10 deletions
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index d076997b04a..5cd16a856e7 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -61,3 +61,28 @@ export const MOCK_SORT_OPTIONS = [
},
},
];
+
+export const MOCK_SEARCH_COUNTS_INPUT = {
+ scopeTabs: ['issues', 'snippet_titles', 'merge_requests'],
+ activeCount: '15',
+};
+
+export const MOCK_SEARCH_COUNT = { scope: 'issues', count: '15' };
+
+export const MOCK_SEARCH_COUNTS_SUCCESS = [
+ { scope: 'issues', count: '15' },
+ { scope: 'snippet_titles', count: '15' },
+ { scope: 'merge_requests', count: '15' },
+];
+
+export const MOCK_SEARCH_COUNTS = [
+ { scope: 'issues', count: '15' },
+ { scope: 'snippet_titles', count: '5' },
+ { scope: 'merge_requests', count: '1' },
+];
+
+export const MOCK_SCOPE_TABS = [
+ { scope: 'issues', title: 'Issues', count: '15' },
+ { scope: 'snippet_titles', title: 'Titles and Descriptions', count: '5' },
+ { scope: 'merge_requests', title: 'Merge requests', count: '1' },
+];
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index e4536a3e136..f751b857c36 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -7,7 +7,15 @@ import * as urlUtils from '~/lib/utils/url_utility';
import createState from '~/search/store/state';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECT, MOCK_PROJECTS } from '../mock_data';
+import {
+ MOCK_QUERY,
+ MOCK_GROUPS,
+ MOCK_PROJECT,
+ MOCK_PROJECTS,
+ MOCK_SEARCH_COUNT,
+ MOCK_SEARCH_COUNTS_SUCCESS,
+ MOCK_SEARCH_COUNTS_INPUT,
+} from '../mock_data';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
@@ -37,19 +45,21 @@ describe('Global Search Store Actions', () => {
});
describe.each`
- action | axiosMock | type | expectedMutations | callback
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
- ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
- ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
- `(`axios calls`, ({ action, axiosMock, type, expectedMutations, callback }) => {
+ action | axiosMock | payload | type | expectedMutations | callback
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
+ ${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 200, res: MOCK_SEARCH_COUNT }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'success'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }, { type: types.RECEIVE_SEARCH_COUNTS_SUCCESS, payload: MOCK_SEARCH_COUNTS_SUCCESS }]} | ${noCallback}
+ ${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 500, res: null }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'error'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }]} | ${flashCallback}
+ `(`axios calls`, ({ action, axiosMock, payload, type, expectedMutations, callback }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
- mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
+ mock[axiosMock.method]().reply(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
- return testAction({ action, state, expectedMutations }).then(() => callback());
+ return testAction({ action, payload, state, expectedMutations }).then(() => callback());
});
});
});
@@ -115,9 +125,25 @@ describe('Global Search Store Actions', () => {
page: null,
state: null,
confidential: null,
+ nav_source: null,
});
expect(urlUtils.visitUrl).toHaveBeenCalled();
});
});
});
+
+ it('calls setUrlParams with snippets, group_id, and project_id when snippets param is true', () => {
+ return testAction(actions.resetQuery, true, state, [], [], () => {
+ expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
+ ...state.query,
+ page: null,
+ state: null,
+ confidential: null,
+ nav_source: null,
+ group_id: null,
+ project_id: null,
+ snippets: true,
+ });
+ });
+ });
});
diff --git a/spec/frontend/search/store/mutations_spec.js b/spec/frontend/search/store/mutations_spec.js
index 560ed66263b..e9a1107cb07 100644
--- a/spec/frontend/search/store/mutations_spec.js
+++ b/spec/frontend/search/store/mutations_spec.js
@@ -1,7 +1,13 @@
import mutations from '~/search/store/mutations';
import createState from '~/search/store/state';
import * as types from '~/search/store/mutation_types';
-import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data';
+import {
+ MOCK_QUERY,
+ MOCK_GROUPS,
+ MOCK_PROJECTS,
+ MOCK_SEARCH_COUNTS,
+ MOCK_SCOPE_TABS,
+} from '../mock_data';
describe('Global Search Store Mutations', () => {
let state;
@@ -71,4 +77,32 @@ describe('Global Search Store Mutations', () => {
expect(state.query[payload.key]).toBe(payload.value);
});
});
+
+ describe('REQUEST_SEARCH_COUNTS', () => {
+ it('sets the count to for the query.scope activeCount', () => {
+ const payload = { scopeTabs: ['issues'], activeCount: '22' };
+ mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
+
+ expect(state.inflatedScopeTabs).toStrictEqual([
+ { scope: 'issues', title: 'Issues', count: '22' },
+ ]);
+ });
+
+ it('sets other scopes count to empty string', () => {
+ const payload = { scopeTabs: ['milestones'], activeCount: '22' };
+ mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
+
+ expect(state.inflatedScopeTabs).toStrictEqual([
+ { scope: 'milestones', title: 'Milestones', count: '' },
+ ]);
+ });
+ });
+
+ describe('RECEIVE_SEARCH_COUNTS_SUCCESS', () => {
+ it('sets the count from the input for all tabs', () => {
+ mutations[types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, MOCK_SEARCH_COUNTS);
+
+ expect(state.inflatedScopeTabs).toStrictEqual(MOCK_SCOPE_TABS);
+ });
+ });
});
diff --git a/spec/frontend/search/topbar/components/app_spec.js b/spec/frontend/search/topbar/components/app_spec.js
index faf3629b444..ff37c733fdb 100644
--- a/spec/frontend/search/topbar/components/app_spec.js
+++ b/spec/frontend/search/topbar/components/app_spec.js
@@ -5,6 +5,7 @@ import { MOCK_QUERY } from 'jest/search/mock_data';
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 ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -42,6 +43,7 @@ describe('GlobalSearchTopbar', () => {
const findGroupFilter = () => wrapper.find(GroupFilter);
const findProjectFilter = () => wrapper.find(ProjectFilter);
const findSearchButton = () => wrapper.find(GlButton);
+ const findScopeTabs = () => wrapper.find(ScopeTabs);
describe('template', () => {
beforeEach(() => {
@@ -52,6 +54,18 @@ describe('GlobalSearchTopbar', () => {
expect(findTopbarForm().exists()).toBe(true);
});
+ describe('Scope Tabs', () => {
+ it('renders when search param is set', () => {
+ createComponent({ query: { search: 'test' } });
+ expect(findScopeTabs().exists()).toBe(true);
+ });
+ it('does not render search param is blank', () => {
+ createComponent({ query: {} });
+
+ expect(findScopeTabs().exists()).toBe(false);
+ });
+ });
+
describe('Search box', () => {
it('renders always', () => {
expect(findGlSearchBox().exists()).toBe(true);
diff --git a/spec/frontend/search/topbar/components/scope_tabs_spec.js b/spec/frontend/search/topbar/components/scope_tabs_spec.js
new file mode 100644
index 00000000000..5c06ce7a2ad
--- /dev/null
+++ b/spec/frontend/search/topbar/components/scope_tabs_spec.js
@@ -0,0 +1,122 @@
+import Vuex from 'vuex';
+import { createLocalVue, mount } from '@vue/test-utils';
+import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { MOCK_QUERY, MOCK_SCOPE_TABS } from 'jest/search/mock_data';
+import ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ScopeTabs', () => {
+ let wrapper;
+
+ const actionSpies = {
+ fetchSearchCounts: jest.fn(),
+ setQuery: jest.fn(),
+ resetQuery: jest.fn(),
+ };
+
+ const defaultProps = {
+ scopeTabs: ['issues', 'merge_requests', 'milestones'],
+ count: '20',
+ };
+
+ const createComponent = (props = {}, initialState = {}) => {
+ const store = new Vuex.Store({
+ state: {
+ query: {
+ ...MOCK_QUERY,
+ search: 'test',
+ },
+ ...initialState,
+ },
+ actions: actionSpies,
+ });
+
+ wrapper = extendedWrapper(
+ mount(ScopeTabs, {
+ localVue,
+ store,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findScopeTabs = () => wrapper.find(GlTabs);
+ const findTabs = () => wrapper.findAll(GlTab);
+ const findBadges = () => wrapper.findAll(GlBadge);
+ const findTabsTitle = () =>
+ wrapper.findAll('[data-testid="tab-title"]').wrappers.map((w) => w.text());
+ const findBadgesTitle = () => findBadges().wrappers.map((w) => w.text());
+ const findBadgeByScope = (scope) => wrapper.findByTestId(`badge-${scope}`);
+ const findTabByScope = (scope) => wrapper.findByTestId(`tab-${scope}`);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
+ });
+
+ it('always renders Scope Tabs', () => {
+ expect(findScopeTabs().exists()).toBe(true);
+ });
+
+ describe('findTabs', () => {
+ it('renders a tab for each scope', () => {
+ expect(findTabs()).toHaveLength(defaultProps.scopeTabs.length);
+ expect(findTabsTitle()).toStrictEqual([
+ 'Issues',
+ 'Titles and Descriptions',
+ 'Merge requests',
+ ]);
+ });
+ });
+
+ describe('findBadges', () => {
+ it('renders a badge for each scope', () => {
+ expect(findBadges()).toHaveLength(defaultProps.scopeTabs.length);
+ expect(findBadgesTitle()).toStrictEqual(['15', '5', '1']);
+ });
+
+ it('sets the variant to neutral for active tab only', () => {
+ expect(findBadgeByScope('issues').classes()).toContain('badge-neutral');
+ expect(findBadgeByScope('snippet_titles').classes()).toContain('badge-muted');
+ expect(findBadgeByScope('merge_requests').classes()).toContain('badge-muted');
+ });
+ });
+ });
+
+ describe('methods', () => {
+ beforeEach(() => {
+ createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
+
+ findTabByScope('snippet_titles').vm.$emit('click');
+ });
+
+ describe('handleTabChange', () => {
+ it('calls setQuery with scope, applies any search params from ALL_SCOPE_TABS, and sends nulls for page, state, confidential, and nav_source', () => {
+ expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
+ key: 'scope',
+ value: 'snippet_titles',
+ });
+ });
+
+ it('calls resetQuery and sends true for snippet_titles tab', () => {
+ expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), true);
+ });
+
+ it('calls resetQuery and does not send true for other tabs', () => {
+ findTabByScope('issues').vm.$emit('click');
+ expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), false);
+ });
+ });
+ });
+});