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:
Diffstat (limited to 'spec/frontend/search')
-rw-r--r--spec/frontend/search/mock_data.js84
-rw-r--r--spec/frontend/search/sidebar/components/app_spec.js120
-rw-r--r--spec/frontend/search/sidebar/components/filters_spec.js132
-rw-r--r--spec/frontend/search/sidebar/components/scope_navigation_spec.js80
-rw-r--r--spec/frontend/search/store/actions_spec.js35
-rw-r--r--spec/frontend/search/store/mutations_spec.js22
6 files changed, 388 insertions, 85 deletions
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index 0542e96c77c..fa5ccfeb478 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -107,3 +107,87 @@ export const PROMISE_ALL_EXPECTED_MUTATIONS = {
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] },
},
};
+
+export const MOCK_NAVIGATION = {
+ projects: {
+ label: 'Projects',
+ scope: 'projects',
+ link: '/search?scope=projects&search=et',
+ count_link: '/search/count?scope=projects&search=et',
+ },
+ blobs: {
+ label: 'Code',
+ scope: 'blobs',
+ link: '/search?scope=blobs&search=et',
+ count_link: '/search/count?scope=blobs&search=et',
+ },
+ issues: {
+ label: 'Issues',
+ scope: 'issues',
+ link: '/search?scope=issues&search=et',
+ active: true,
+ count: '2,430',
+ },
+ merge_requests: {
+ label: 'Merge requests',
+ scope: 'merge_requests',
+ link: '/search?scope=merge_requests&search=et',
+ count_link: '/search/count?scope=merge_requests&search=et',
+ },
+ wiki_blobs: {
+ label: 'Wiki',
+ scope: 'wiki_blobs',
+ link: '/search?scope=wiki_blobs&search=et',
+ count_link: '/search/count?scope=wiki_blobs&search=et',
+ },
+ commits: {
+ label: 'Commits',
+ scope: 'commits',
+ link: '/search?scope=commits&search=et',
+ count_link: '/search/count?scope=commits&search=et',
+ },
+ notes: {
+ label: 'Comments',
+ scope: 'notes',
+ link: '/search?scope=notes&search=et',
+ count_link: '/search/count?scope=notes&search=et',
+ },
+ milestones: {
+ label: 'Milestones',
+ scope: 'milestones',
+ link: '/search?scope=milestones&search=et',
+ count_link: '/search/count?scope=milestones&search=et',
+ },
+ users: {
+ label: 'Users',
+ scope: 'users',
+ link: '/search?scope=users&search=et',
+ count_link: '/search/count?scope=users&search=et',
+ },
+};
+
+export const MOCK_NAVIGATION_DATA = {
+ projects: {
+ label: 'Projects',
+ scope: 'projects',
+ link: '/search?scope=projects&search=et',
+ count_link: '/search/count?scope=projects&search=et',
+ },
+};
+
+export const MOCK_ENDPOINT_RESPONSE = { count: '13' };
+
+export const MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION = {
+ projects: {
+ count: '13',
+ label: 'Projects',
+ scope: 'projects',
+ link: '/search?scope=projects&search=et',
+ count_link: '/search/count?scope=projects&search=et',
+ },
+};
+
+export const MOCK_NAVIGATION_ACTION_MUTATION = {
+ type: types.RECEIVE_NAVIGATION_COUNT,
+ payload: { key: 'projects', count: '13' },
+};
diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js
index 89959feec39..e87217950cd 100644
--- a/spec/frontend/search/sidebar/components/app_spec.js
+++ b/spec/frontend/search/sidebar/components/app_spec.js
@@ -1,11 +1,10 @@
-import { GlButton, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { MOCK_QUERY } from 'jest/search/mock_data';
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
-import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
-import StatusFilter from '~/search/sidebar/components/status_filter.vue';
+import ResultsFilters from '~/search/sidebar/components/results_filters.vue';
+import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue';
Vue.use(Vuex);
@@ -17,7 +16,7 @@ describe('GlobalSearchSidebar', () => {
resetQuery: jest.fn(),
};
- const createComponent = (initialState) => {
+ const createComponent = (initialState, featureFlags) => {
const store = new Vuex.Store({
state: {
urlQuery: MOCK_QUERY,
@@ -28,6 +27,11 @@ describe('GlobalSearchSidebar', () => {
wrapper = shallowMount(GlobalSearchSidebar, {
store,
+ provide: {
+ glFeatures: {
+ ...featureFlags,
+ },
+ },
});
};
@@ -35,118 +39,68 @@ describe('GlobalSearchSidebar', () => {
wrapper.destroy();
});
- const findSidebarForm = () => wrapper.find('form');
- const findStatusFilter = () => wrapper.findComponent(StatusFilter);
- const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter);
- const findApplyButton = () => wrapper.findComponent(GlButton);
- const findResetLinkButton = () => wrapper.findComponent(GlLink);
+ const findSidebarSection = () => wrapper.find('section');
+ const findFilters = () => wrapper.findComponent(ResultsFilters);
+ const findSidebarNavigation = () => wrapper.findComponent(ScopeNavigation);
- describe('template', () => {
+ describe('renders properly', () => {
describe('scope=projects', () => {
beforeEach(() => {
createComponent({ urlQuery: { ...MOCK_QUERY, scope: 'projects' } });
});
- it("doesn't render StatusFilter", () => {
- expect(findStatusFilter().exists()).toBe(false);
- });
-
- it("doesn't render ConfidentialityFilter", () => {
- expect(findConfidentialityFilter().exists()).toBe(false);
+ it('shows section', () => {
+ expect(findSidebarSection().exists()).toBe(true);
});
- it("doesn't render ApplyButton", () => {
- expect(findApplyButton().exists()).toBe(false);
+ it("doesn't shows filters", () => {
+ expect(findFilters().exists()).toBe(false);
});
});
- describe('scope=issues', () => {
+ describe('scope=merge_requests', () => {
beforeEach(() => {
- createComponent({ urlQuery: MOCK_QUERY });
- });
- it('renders StatusFilter', () => {
- expect(findStatusFilter().exists()).toBe(true);
+ createComponent({ urlQuery: { ...MOCK_QUERY, scope: 'merge_requests' } });
});
- it('renders ConfidentialityFilter', () => {
- expect(findConfidentialityFilter().exists()).toBe(true);
+ it('shows section', () => {
+ expect(findSidebarSection().exists()).toBe(true);
});
- it('renders ApplyButton', () => {
- expect(findApplyButton().exists()).toBe(true);
+ it('shows filters', () => {
+ expect(findFilters().exists()).toBe(true);
});
});
- });
- describe('ApplyButton', () => {
- describe('when sidebarDirty is false', () => {
+ describe('scope=issues', () => {
beforeEach(() => {
- createComponent({ sidebarDirty: false });
- });
-
- it('disables the button', () => {
- expect(findApplyButton().attributes('disabled')).toBe('true');
+ createComponent({ urlQuery: MOCK_QUERY });
});
- });
-
- describe('when sidebarDirty is true', () => {
- beforeEach(() => {
- createComponent({ sidebarDirty: true });
+ it('shows section', () => {
+ expect(findSidebarSection().exists()).toBe(true);
});
- it('enables the button', () => {
- expect(findApplyButton().attributes('disabled')).toBe(undefined);
+ it('shows filters', () => {
+ expect(findFilters().exists()).toBe(true);
});
});
});
- describe('ResetLinkButton', () => {
- describe('with no filter selected', () => {
- beforeEach(() => {
- createComponent({ urlQuery: {} });
- });
-
- it('does not render', () => {
- expect(findResetLinkButton().exists()).toBe(false);
- });
- });
-
- describe('with filter selected', () => {
- beforeEach(() => {
- createComponent({ urlQuery: MOCK_QUERY });
- });
-
- it('does render', () => {
- expect(findResetLinkButton().exists()).toBe(true);
- });
+ describe('when search_page_vertical_nav is enabled', () => {
+ beforeEach(() => {
+ createComponent({}, { searchPageVerticalNav: true });
});
-
- describe('with filter selected and user updated query back to default', () => {
- beforeEach(() => {
- createComponent({ urlQuery: MOCK_QUERY, query: {} });
- });
-
- it('does render', () => {
- expect(findResetLinkButton().exists()).toBe(true);
- });
+ it('shows the vertical navigation', () => {
+ expect(findSidebarNavigation().exists()).toBe(true);
});
});
- describe('actions', () => {
+ describe('when search_page_vertical_nav is disabled', () => {
beforeEach(() => {
- createComponent({});
+ createComponent({}, { searchPageVerticalNav: false });
});
-
- it('clicking ApplyButton calls applyQuery', () => {
- findSidebarForm().trigger('submit');
-
- expect(actionSpies.applyQuery).toHaveBeenCalled();
- });
-
- it('clicking ResetLinkButton calls resetQuery', () => {
- findResetLinkButton().vm.$emit('click');
-
- expect(actionSpies.resetQuery).toHaveBeenCalled();
+ it('hides the vertical navigation', () => {
+ expect(findSidebarNavigation().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/search/sidebar/components/filters_spec.js b/spec/frontend/search/sidebar/components/filters_spec.js
new file mode 100644
index 00000000000..4f217709297
--- /dev/null
+++ b/spec/frontend/search/sidebar/components/filters_spec.js
@@ -0,0 +1,132 @@
+import { GlButton, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { MOCK_QUERY } from 'jest/search/mock_data';
+import ResultsFilters from '~/search/sidebar/components/results_filters.vue';
+import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
+import StatusFilter from '~/search/sidebar/components/status_filter.vue';
+
+Vue.use(Vuex);
+
+describe('GlobalSearchSidebarFilters', () => {
+ let wrapper;
+
+ const actionSpies = {
+ applyQuery: jest.fn(),
+ resetQuery: jest.fn(),
+ };
+
+ const createComponent = (initialState) => {
+ const store = new Vuex.Store({
+ state: {
+ urlQuery: MOCK_QUERY,
+ ...initialState,
+ },
+ actions: actionSpies,
+ });
+
+ wrapper = shallowMount(ResultsFilters, {
+ store,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findSidebarForm = () => wrapper.find('form');
+ const findStatusFilter = () => wrapper.findComponent(StatusFilter);
+ const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter);
+ const findApplyButton = () => wrapper.findComponent(GlButton);
+ const findResetLinkButton = () => wrapper.findComponent(GlLink);
+
+ describe('Renders correctly', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: MOCK_QUERY });
+ });
+ it('renders StatusFilter', () => {
+ expect(findStatusFilter().exists()).toBe(true);
+ });
+
+ it('renders ConfidentialityFilter', () => {
+ expect(findConfidentialityFilter().exists()).toBe(true);
+ });
+
+ it('renders ApplyButton', () => {
+ expect(findApplyButton().exists()).toBe(true);
+ });
+ });
+
+ describe('ApplyButton', () => {
+ describe('when sidebarDirty is false', () => {
+ beforeEach(() => {
+ createComponent({ sidebarDirty: false });
+ });
+
+ it('disables the button', () => {
+ expect(findApplyButton().attributes('disabled')).toBe('true');
+ });
+ });
+
+ describe('when sidebarDirty is true', () => {
+ beforeEach(() => {
+ createComponent({ sidebarDirty: true });
+ });
+
+ it('enables the button', () => {
+ expect(findApplyButton().attributes('disabled')).toBe(undefined);
+ });
+ });
+ });
+
+ describe('ResetLinkButton', () => {
+ describe('with no filter selected', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: {} });
+ });
+
+ it('does not render', () => {
+ expect(findResetLinkButton().exists()).toBe(false);
+ });
+ });
+
+ describe('with filter selected', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: MOCK_QUERY });
+ });
+
+ it('does render', () => {
+ expect(findResetLinkButton().exists()).toBe(true);
+ });
+ });
+
+ describe('with filter selected and user updated query back to default', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: MOCK_QUERY, query: {} });
+ });
+
+ it('does render', () => {
+ expect(findResetLinkButton().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('actions', () => {
+ beforeEach(() => {
+ createComponent({});
+ });
+
+ it('clicking ApplyButton calls applyQuery', () => {
+ findSidebarForm().trigger('submit');
+
+ expect(actionSpies.applyQuery).toHaveBeenCalled();
+ });
+
+ it('clicking ResetLinkButton calls resetQuery', () => {
+ findResetLinkButton().vm.$emit('click');
+
+ expect(actionSpies.resetQuery).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/search/sidebar/components/scope_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_navigation_spec.js
new file mode 100644
index 00000000000..6262a52e01a
--- /dev/null
+++ b/spec/frontend/search/sidebar/components/scope_navigation_spec.js
@@ -0,0 +1,80 @@
+import { GlNav, GlNavItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { MOCK_QUERY, MOCK_NAVIGATION } from 'jest/search/mock_data';
+import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue';
+
+Vue.use(Vuex);
+
+describe('ScopeNavigation', () => {
+ let wrapper;
+
+ const actionSpies = {
+ fetchSidebarCount: jest.fn(),
+ };
+
+ const createComponent = (initialState) => {
+ const store = new Vuex.Store({
+ state: {
+ urlQuery: MOCK_QUERY,
+ navigation: MOCK_NAVIGATION,
+ ...initialState,
+ },
+ actions: actionSpies,
+ });
+
+ wrapper = shallowMount(ScopeNavigation, {
+ store,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findNavElement = () => wrapper.find('nav');
+ const findGlNav = () => wrapper.findComponent(GlNav);
+ const findGlNavItems = () => wrapper.findAllComponents(GlNavItem);
+ const findGlNavItemActive = () => findGlNavItems().wrappers.filter((w) => w.attributes('active'));
+ const findGlNavItemActiveCount = () => findGlNavItemActive().at(0).findAll('span').at(1);
+
+ describe('scope navigation', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders section', () => {
+ expect(findNavElement().exists()).toBe(true);
+ });
+
+ it('renders nav component', () => {
+ expect(findGlNav().exists()).toBe(true);
+ });
+
+ it('renders all nav item components', () => {
+ expect(findGlNavItems()).toHaveLength(9);
+ });
+
+ it('nav items have proper links', () => {
+ const linkAtPosition = 3;
+ const { link } = MOCK_NAVIGATION[Object.keys(MOCK_NAVIGATION)[linkAtPosition]];
+
+ expect(findGlNavItems().at(linkAtPosition).attributes('href')).toBe(link);
+ });
+ });
+
+ describe('scope navigation sets proper state', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets proper class to active item', () => {
+ expect(findGlNavItemActive()).toHaveLength(1);
+ });
+
+ it('active item', () => {
+ expect(findGlNavItemActiveCount().text()).toBe('2.4K');
+ });
+ });
+});
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index c442ffa521d..3d19b27ff86 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
import { createAlert } from '~/flash';
+import * as logger from '~/lib/logger';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import * as actions from '~/search/store/actions';
@@ -23,6 +24,9 @@ import {
MOCK_FRESH_DATA_RES,
PRELOAD_EXPECTED_MUTATIONS,
PROMISE_ALL_EXPECTED_MUTATIONS,
+ MOCK_NAVIGATION_DATA,
+ MOCK_NAVIGATION_ACTION_MUTATION,
+ MOCK_ENDPOINT_RESPONSE,
} from '../mock_data';
jest.mock('~/flash');
@@ -31,6 +35,9 @@ jest.mock('~/lib/utils/url_utility', () => ({
joinPaths: jest.fn().mockReturnValue(''),
visitUrl: jest.fn(),
}));
+jest.mock('~/lib/logger', () => ({
+ logError: jest.fn(),
+}));
describe('Global Search Store Actions', () => {
let mock;
@@ -260,4 +267,32 @@ describe('Global Search Store Actions', () => {
);
});
});
+
+ describe.each`
+ action | axiosMock | type | scope | expectedMutations | errorLogs
+ ${actions.fetchSidebarCount} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${'issues'} | ${[MOCK_NAVIGATION_ACTION_MUTATION]} | ${0}
+ ${actions.fetchSidebarCount} | ${{ method: null, code: 0 }} | ${'success'} | ${'projects'} | ${[]} | ${0}
+ ${actions.fetchSidebarCount} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${'issues'} | ${[]} | ${1}
+ `('fetchSidebarCount', ({ action, axiosMock, type, expectedMutations, scope, errorLogs }) => {
+ describe(`on ${type}`, () => {
+ beforeEach(() => {
+ state.navigation = MOCK_NAVIGATION_DATA;
+ state.urlQuery = {
+ scope,
+ };
+
+ if (axiosMock.method) {
+ mock[axiosMock.method]().reply(axiosMock.code, MOCK_ENDPOINT_RESPONSE);
+ }
+ });
+
+ it(`should ${expectedMutations.length === 0 ? 'NOT ' : ''}dispatch ${
+ expectedMutations.length === 0 ? '' : 'the correct '
+ }mutations for ${scope}`, () => {
+ return testAction({ action, state, expectedMutations }).then(() => {
+ expect(logger.logError).toHaveBeenCalledTimes(errorLogs);
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/search/store/mutations_spec.js b/spec/frontend/search/store/mutations_spec.js
index 25f9b692955..a79ec8f70b0 100644
--- a/spec/frontend/search/store/mutations_spec.js
+++ b/spec/frontend/search/store/mutations_spec.js
@@ -1,13 +1,20 @@
import * as types from '~/search/store/mutation_types';
import mutations from '~/search/store/mutations';
import createState from '~/search/store/state';
-import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data';
+import {
+ MOCK_QUERY,
+ MOCK_GROUPS,
+ MOCK_PROJECTS,
+ MOCK_NAVIGATION_DATA,
+ MOCK_NAVIGATION_ACTION_MUTATION,
+ MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION,
+} from '../mock_data';
describe('Global Search Store Mutations', () => {
let state;
beforeEach(() => {
- state = createState({ query: MOCK_QUERY });
+ state = createState({ query: MOCK_QUERY, navigation: MOCK_NAVIGATION_DATA });
});
describe('REQUEST_GROUPS', () => {
@@ -90,4 +97,15 @@ describe('Global Search Store Mutations', () => {
expect(state.frequentItems[payload.key]).toStrictEqual(payload.data);
});
});
+
+ describe('RECEIVE_NAVIGATION_COUNT', () => {
+ it('sets frequentItems[key] to data', () => {
+ const { payload } = MOCK_NAVIGATION_ACTION_MUTATION;
+ mutations[types.RECEIVE_NAVIGATION_COUNT](state, payload);
+
+ expect(state.navigation[payload.key]).toStrictEqual(
+ MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION[payload.key],
+ );
+ });
+ });
});