Commit 8795067b authored by Zack Cuddy's avatar Zack Cuddy

Global Search - Track top nav searches

This change adds logic to track the
group and projects used when searching
via the top nav bar.

This is done via the 'nav_source'
query parameter.

Changelog: changed
parent 88a88c89
......@@ -46,38 +46,44 @@ export const fetchProjects = ({ commit, state }, search) => {
}
};
export const loadFrequentGroups = async ({ commit }) => {
const data = loadDataFromLS(GROUPS_LOCAL_STORAGE_KEY);
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data });
export const preloadStoredFrequentItems = ({ commit }) => {
const storedGroups = loadDataFromLS(GROUPS_LOCAL_STORAGE_KEY);
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: storedGroups });
const promises = data.map((d) => Api.group(d.id));
const storedProjects = loadDataFromLS(PROJECTS_LOCAL_STORAGE_KEY);
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: storedProjects });
};
export const loadFrequentGroups = async ({ commit, state }) => {
const storedData = state.frequentItems[GROUPS_LOCAL_STORAGE_KEY];
const promises = storedData.map((d) => Api.group(d.id));
try {
const inflatedData = mergeById(await Promise.all(promises), data);
const inflatedData = mergeById(await Promise.all(promises), storedData);
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: inflatedData });
} catch {
createFlash({ message: __('There was a problem fetching recent groups.') });
}
};
export const loadFrequentProjects = async ({ commit }) => {
const data = loadDataFromLS(PROJECTS_LOCAL_STORAGE_KEY);
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data });
const promises = data.map((d) => Api.project(d.id).then((res) => res.data));
export const loadFrequentProjects = async ({ commit, state }) => {
const storedData = state.frequentItems[PROJECTS_LOCAL_STORAGE_KEY];
const promises = storedData.map((d) => Api.project(d.id).then((res) => res.data));
try {
const inflatedData = mergeById(await Promise.all(promises), data);
const inflatedData = mergeById(await Promise.all(promises), storedData);
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: inflatedData });
} catch {
createFlash({ message: __('There was a problem fetching recent projects.') });
}
};
export const setFrequentGroup = ({ state }, item) => {
setFrequentItemToLS(GROUPS_LOCAL_STORAGE_KEY, state.frequentItems, item);
export const setFrequentGroup = ({ state, commit }, item) => {
const frequentItems = setFrequentItemToLS(GROUPS_LOCAL_STORAGE_KEY, state.frequentItems, item);
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: frequentItems });
};
export const setFrequentProject = ({ state }, item) => {
setFrequentItemToLS(PROJECTS_LOCAL_STORAGE_KEY, state.frequentItems, item);
export const setFrequentProject = ({ state, commit }, item) => {
const frequentItems = setFrequentItemToLS(PROJECTS_LOCAL_STORAGE_KEY, state.frequentItems, item);
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: frequentItems });
};
export const setQuery = ({ commit }, { key, value }) => {
......
......@@ -21,7 +21,7 @@ export const loadDataFromLS = (key) => {
export const setFrequentItemToLS = (key, data, itemData) => {
if (!AccessorUtilities.isLocalStorageAccessSafe()) {
return;
return [];
}
const keyList = [
......@@ -66,9 +66,11 @@ export const setFrequentItemToLS = (key, data, itemData) => {
// Note we do not need to commit a mutation here as immediately after this we refresh the page to
// update the search results.
localStorage.setItem(key, JSON.stringify(frequentItems));
return frequentItems;
} catch {
// The LS got in a bad state, let's wipe it
localStorage.removeItem(key);
return [];
}
};
......
......@@ -39,8 +39,11 @@ export default {
return !this.query.snippets || this.query.snippets === 'false';
},
},
created() {
this.preloadStoredFrequentItems();
},
methods: {
...mapActions(['applyQuery', 'setQuery']),
...mapActions(['applyQuery', 'setQuery', 'preloadStoredFrequentItems']),
},
};
</script>
......
......@@ -18,12 +18,18 @@ export default {
},
},
computed: {
...mapState(['groups', 'fetchingGroups']),
...mapState(['query', 'groups', 'fetchingGroups']),
...mapGetters(['frequentGroups']),
selectedGroup() {
return isEmpty(this.initialData) ? ANY_OPTION : this.initialData;
},
},
created() {
// This tracks groups searched via the top nav search bar
if (this.query.nav_source === 'navbar' && this.initialData?.id) {
this.setFrequentGroup(this.initialData);
}
},
methods: {
...mapActions(['fetchGroups', 'setFrequentGroup', 'loadFrequentGroups']),
handleGroupChange(group) {
......@@ -33,7 +39,11 @@ export default {
}
visitUrl(
setUrlParams({ [GROUP_DATA.queryParam]: group.id, [PROJECT_DATA.queryParam]: null }),
setUrlParams({
[GROUP_DATA.queryParam]: group.id,
[PROJECT_DATA.queryParam]: null,
nav_source: null,
}),
);
},
},
......
......@@ -17,12 +17,18 @@ export default {
},
},
computed: {
...mapState(['projects', 'fetchingProjects']),
...mapState(['query', 'projects', 'fetchingProjects']),
...mapGetters(['frequentProjects']),
selectedProject() {
return this.initialData ? this.initialData : ANY_OPTION;
},
},
created() {
// This tracks projects searched via the top nav search bar
if (this.query.nav_source === 'navbar' && this.initialData?.id) {
this.setFrequentProject(this.initialData);
}
},
methods: {
...mapActions(['fetchProjects', 'setFrequentProject', 'loadFrequentProjects']),
handleProjectChange(project) {
......@@ -35,6 +41,7 @@ export default {
const queryParams = {
...(project.namespace?.id && { [GROUP_DATA.queryParam]: project.namespace.id }),
[PROJECT_DATA.queryParam]: project.id,
nav_source: null,
};
visitUrl(setUrlParams(queryParams));
......
......@@ -284,8 +284,8 @@ export class SearchAutocomplete {
if (projectId) {
const projectOptions = gl.projectOptions[getProjectSlug()];
const url = groupId
? `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&group_id=${groupId}`
: `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}`;
? `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&group_id=${groupId}&nav_source=navbar`
: `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&nav_source=navbar`;
options.push({
icon,
......@@ -313,7 +313,7 @@ export class SearchAutocomplete {
},
false,
),
url: `${gon.relative_url_root}/search?search=${term}&group_id=${groupId}`,
url: `${gon.relative_url_root}/search?search=${term}&group_id=${groupId}&nav_source=navbar`,
});
}
......@@ -321,7 +321,7 @@ export class SearchAutocomplete {
icon,
text: term,
template: s__('SearchAutocomplete|in all GitLab'),
url: `${gon.relative_url_root}/search?search=${term}`,
url: `${gon.relative_url_root}/search?search=${term}&nav_source=navbar`,
});
return options;
......
......@@ -5,6 +5,7 @@
= hidden_field_tag :group_id, params[:group_id]
- if params[:project_id].present?
= hidden_field_tag :project_id, params[:project_id]
- group_attributes = @group&.attributes&.slice('id', 'name')&.merge(full_name: @group&.full_name)
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
- if @search_results
......@@ -16,7 +17,7 @@
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
.gl-mt-3
#js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
#js-search-topbar{ data: { "group-initial-data": group_attributes.to_json, "project-initial-data": project_attributes.to_json } }
- if @search_term
= render 'search/category'
= render 'search/results'
......@@ -254,6 +254,7 @@ RSpec.describe 'User uses header search field', :js do
href = search_path(search: term)
href.concat("&project_id=#{project_id}") if project_id
href.concat("&group_id=#{group_id}") if group_id
href.concat("&nav_source=navbar")
".dropdown a[href='#{href}']"
end
......
......@@ -86,18 +86,21 @@ export const STALE_STORED_DATA = [
export const MOCK_FRESH_DATA_RES = { name: 'fresh' };
export const PROMISE_ALL_EXPECTED_MUTATIONS = {
initGroups: {
export const PRELOAD_EXPECTED_MUTATIONS = [
{
type: types.LOAD_FREQUENT_ITEMS,
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
},
resGroups: {
{
type: types.LOAD_FREQUENT_ITEMS,
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] },
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
},
initProjects: {
];
export const PROMISE_ALL_EXPECTED_MUTATIONS = {
resGroups: {
type: types.LOAD_FREQUENT_ITEMS,
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] },
},
resProjects: {
type: types.LOAD_FREQUENT_ITEMS,
......
......@@ -17,6 +17,7 @@ import {
MOCK_GROUP,
FRESH_STORED_DATA,
MOCK_FRESH_DATA_RES,
PRELOAD_EXPECTED_MUTATIONS,
PROMISE_ALL_EXPECTED_MUTATIONS,
} from '../mock_data';
......@@ -68,31 +69,31 @@ describe('Global Search Store Actions', () => {
});
describe.each`
action | axiosMock | type | expectedMutations | flashCallCount | lsKey
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initGroups, PROMISE_ALL_EXPECTED_MUTATIONS.resGroups]} | ${0} | ${GROUPS_LOCAL_STORAGE_KEY}
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initGroups]} | ${1} | ${GROUPS_LOCAL_STORAGE_KEY}
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initProjects, PROMISE_ALL_EXPECTED_MUTATIONS.resProjects]} | ${0} | ${PROJECTS_LOCAL_STORAGE_KEY}
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initProjects]} | ${1} | ${PROJECTS_LOCAL_STORAGE_KEY}
`(
'Promise.all calls',
({ action, axiosMock, type, expectedMutations, flashCallCount, lsKey }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
storeUtils.loadDataFromLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
mock[axiosMock.method]().reply(axiosMock.code, MOCK_FRESH_DATA_RES);
});
action | axiosMock | type | expectedMutations | flashCallCount
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.resGroups]} | ${0}
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[]} | ${1}
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.resProjects]} | ${0}
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[]} | ${1}
`('Promise.all calls', ({ action, axiosMock, type, expectedMutations, flashCallCount }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
state.frequentItems = {
[GROUPS_LOCAL_STORAGE_KEY]: FRESH_STORED_DATA,
[PROJECTS_LOCAL_STORAGE_KEY]: FRESH_STORED_DATA,
};
mock[axiosMock.method]().reply(axiosMock.code, MOCK_FRESH_DATA_RES);
});
it(`should dispatch the correct mutations`, () => {
return testAction({ action, state, expectedMutations }).then(() => {
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(lsKey);
flashCallback(flashCallCount);
});
it(`should dispatch the correct mutations`, () => {
return testAction({ action, state, expectedMutations }).then(() => {
flashCallback(flashCallCount);
});
});
});
},
);
});
});
describe('getGroupsData', () => {
const mockCommit = () => {};
......@@ -182,14 +183,38 @@ describe('Global Search Store Actions', () => {
});
});
describe('preloadStoredFrequentItems', () => {
beforeEach(() => {
storeUtils.loadDataFromLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
});
it('calls preloadStoredFrequentItems for both groups and projects and commits LOAD_FREQUENT_ITEMS', async () => {
await testAction({
action: actions.preloadStoredFrequentItems,
state,
expectedMutations: PRELOAD_EXPECTED_MUTATIONS,
});
expect(storeUtils.loadDataFromLS).toHaveBeenCalledTimes(2);
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(GROUPS_LOCAL_STORAGE_KEY);
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(PROJECTS_LOCAL_STORAGE_KEY);
});
});
describe('setFrequentGroup', () => {
beforeEach(() => {
storeUtils.setFrequentItemToLS = jest.fn();
storeUtils.setFrequentItemToLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
});
it(`calls setFrequentItemToLS with ${GROUPS_LOCAL_STORAGE_KEY} and item data`, async () => {
it(`calls setFrequentItemToLS with ${GROUPS_LOCAL_STORAGE_KEY} and item data then commits LOAD_FREQUENT_ITEMS`, async () => {
await testAction({
action: actions.setFrequentGroup,
expectedMutations: [
{
type: types.LOAD_FREQUENT_ITEMS,
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
},
],
payload: MOCK_GROUP,
state,
});
......@@ -204,12 +229,18 @@ describe('Global Search Store Actions', () => {
describe('setFrequentProject', () => {
beforeEach(() => {
storeUtils.setFrequentItemToLS = jest.fn();
storeUtils.setFrequentItemToLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
});
it(`calls setFrequentItemToLS with ${PROJECTS_LOCAL_STORAGE_KEY} and item data`, async () => {
await testAction({
action: actions.setFrequentProject,
expectedMutations: [
{
type: types.LOAD_FREQUENT_ITEMS,
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
},
],
payload: MOCK_PROJECT,
state,
});
......
......@@ -51,19 +51,25 @@ describe('Global Search Store Utils', () => {
describe('setFrequentItemToLS', () => {
const frequentItems = {};
let res;
describe('with existing data', () => {
describe(`when frequency is less than ${MAX_FREQUENCY}`, () => {
beforeEach(() => {
frequentItems[MOCK_LS_KEY] = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: PREV_TIME }];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
});
it('adds 1 to the frequency, tracks lastUsed, and calls localStorage.setItem', () => {
it('adds 1 to the frequency, tracks lastUsed, calls localStorage.setItem and returns the array', () => {
const updatedFrequentItems = [
{ ...MOCK_GROUPS[0], frequency: 2, lastUsed: CURRENT_TIME },
];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 2, lastUsed: CURRENT_TIME }]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
......@@ -72,16 +78,19 @@ describe('Global Search Store Utils', () => {
frequentItems[MOCK_LS_KEY] = [
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: PREV_TIME },
];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
});
it(`does not further increase frequency past ${MAX_FREQUENCY}, tracks lastUsed, and calls localStorage.setItem`, () => {
it(`does not further increase frequency past ${MAX_FREQUENCY}, tracks lastUsed, calls localStorage.setItem, and returns the array`, () => {
const updatedFrequentItems = [
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: CURRENT_TIME },
];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: CURRENT_TIME },
]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
});
......@@ -89,14 +98,17 @@ describe('Global Search Store Utils', () => {
describe('with no existing data', () => {
beforeEach(() => {
frequentItems[MOCK_LS_KEY] = [];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
});
it('adds a new entry with frequency 1, tracks lastUsed, and calls localStorage.setItem', () => {
it('adds a new entry with frequency 1, tracks lastUsed, calls localStorage.setItem, and returns the array', () => {
const updatedFrequentItems = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
......@@ -107,18 +119,21 @@ describe('Global Search Store Utils', () => {
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
{ id: 3, frequency: 1, lastUsed: PREV_TIME },
];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 3 });
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 3 });
});
it('sorts the array by most frequent and lastUsed', () => {
it('sorts the array by most frequent and lastUsed and returns the array', () => {
const updatedFrequentItems = [
{ id: 3, frequency: 2, lastUsed: CURRENT_TIME },
{ id: 1, frequency: 2, lastUsed: PREV_TIME },
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([
{ id: 3, frequency: 2, lastUsed: CURRENT_TIME },
{ id: 1, frequency: 2, lastUsed: PREV_TIME },
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
......@@ -131,31 +146,35 @@ describe('Global Search Store Utils', () => {
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
{ id: 5, frequency: 1, lastUsed: PREV_TIME },
];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 6 });
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 6 });
});
it('removes the last item in the array', () => {
it('removes the last item in the array and returns the array', () => {
const updatedFrequentItems = [
{ id: 1, frequency: 5, lastUsed: PREV_TIME },
{ id: 2, frequency: 4, lastUsed: PREV_TIME },
{ id: 3, frequency: 3, lastUsed: PREV_TIME },
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
{ id: 6, frequency: 1, lastUsed: CURRENT_TIME },
];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([
{ id: 1, frequency: 5, lastUsed: PREV_TIME },
{ id: 2, frequency: 4, lastUsed: PREV_TIME },
{ id: 3, frequency: 3, lastUsed: PREV_TIME },
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
{ id: 6, frequency: 1, lastUsed: CURRENT_TIME },
]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
describe('with null data loaded in', () => {
beforeEach(() => {
frequentItems[MOCK_LS_KEY] = null;
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
});
it('wipes local storage', () => {
it('wipes local storage and returns empty array', () => {
expect(localStorage.removeItem).toHaveBeenCalledWith(MOCK_LS_KEY);
expect(res).toEqual([]);
});
});
......@@ -163,14 +182,17 @@ describe('Global Search Store Utils', () => {
beforeEach(() => {
const MOCK_ADDITIONAL_DATA_GROUP = { ...MOCK_GROUPS[0], extraData: 'test' };
frequentItems[MOCK_LS_KEY] = [];
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_ADDITIONAL_DATA_GROUP);
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_ADDITIONAL_DATA_GROUP);
});
it('parses out extra data for LS', () => {
it('parses out extra data for LS and returns the array', () => {
const updatedFrequentItems = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }];
expect(localStorage.setItem).toHaveBeenCalledWith(
MOCK_LS_KEY,
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }]),
JSON.stringify(updatedFrequentItems),
);
expect(res).toEqual(updatedFrequentItems);
});
});
});
......
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
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';
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
describe('GlobalSearchTopbar', () => {
let wrapper;
......@@ -15,6 +15,7 @@ describe('GlobalSearchTopbar', () => {
const actionSpies = {
applyQuery: jest.fn(),
setQuery: jest.fn(),
preloadStoredFrequentItems: jest.fn(),
};
const createComponent = (initialState) => {
......@@ -27,14 +28,12 @@ describe('GlobalSearchTopbar', () => {
});
wrapper = shallowMount(GlobalSearchTopbar, {
localVue,
store,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findTopbarForm = () => wrapper.find(GlForm);
......@@ -110,4 +109,14 @@ describe('GlobalSearchTopbar', () => {
expect(actionSpies.applyQuery).toHaveBeenCalled();
});
});
describe('onCreate', () => {
beforeEach(() => {
createComponent();
});
it('calls preloadStoredFrequentItems', () => {
expect(actionSpies.preloadStoredFrequentItems).toHaveBeenCalled();
});
});
});
......@@ -51,7 +51,6 @@ describe('GroupFilter', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
......@@ -89,10 +88,11 @@ describe('GroupFilter', () => {
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
});
it('calls setUrlParams with group null, project id null, and then calls visitUrl', () => {
it('calls setUrlParams with group null, project id null, nav_source null, and then calls visitUrl', () => {
expect(setUrlParams).toHaveBeenCalledWith({
[GROUP_DATA.queryParam]: null,
[PROJECT_DATA.queryParam]: null,
nav_source: null,
});
expect(visitUrl).toHaveBeenCalled();
......@@ -108,10 +108,11 @@ describe('GroupFilter', () => {
findSearchableDropdown().vm.$emit('change', MOCK_GROUP);
});
it('calls setUrlParams with group id, project id null, and then calls visitUrl', () => {
it('calls setUrlParams with group id, project id null, nav_source null, and then calls visitUrl', () => {
expect(setUrlParams).toHaveBeenCalledWith({
[GROUP_DATA.queryParam]: MOCK_GROUP.id,
[PROJECT_DATA.queryParam]: null,
nav_source: null,
});
expect(visitUrl).toHaveBeenCalled();
......@@ -156,4 +157,31 @@ describe('GroupFilter', () => {
});
});
});
describe.each`
navSource | initialData | callMethod
${null} | ${null} | ${false}
${null} | ${MOCK_GROUP} | ${false}
${'navbar'} | ${null} | ${false}
${'navbar'} | ${MOCK_GROUP} | ${true}
`('onCreate', ({ navSource, initialData, callMethod }) => {
describe(`when nav_source is ${navSource} and ${
initialData ? 'has' : 'does not have'
} an initial group`, () => {
beforeEach(() => {
createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData });
});
it(`${callMethod ? 'does' : 'does not'} call setFrequentGroup`, () => {
if (callMethod) {
expect(actionSpies.setFrequentGroup).toHaveBeenCalledWith(
expect.any(Object),
initialData,
);
} else {
expect(actionSpies.setFrequentGroup).not.toHaveBeenCalled();
}
});
});
});
});
......@@ -51,7 +51,6 @@ describe('ProjectFilter', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
......@@ -89,9 +88,10 @@ describe('ProjectFilter', () => {
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
});
it('calls setUrlParams with null, no group id, then calls visitUrl', () => {
it('calls setUrlParams with null, no group id, nav_source null, then calls visitUrl', () => {
expect(setUrlParams).toHaveBeenCalledWith({
[PROJECT_DATA.queryParam]: null,
nav_source: null,
});
expect(visitUrl).toHaveBeenCalled();
});
......@@ -106,10 +106,11 @@ describe('ProjectFilter', () => {
findSearchableDropdown().vm.$emit('change', MOCK_PROJECT);
});
it('calls setUrlParams with project id, group id, then calls visitUrl', () => {
it('calls setUrlParams with project id, group id, nav_source null, then calls visitUrl', () => {
expect(setUrlParams).toHaveBeenCalledWith({
[GROUP_DATA.queryParam]: MOCK_PROJECT.namespace.id,
[PROJECT_DATA.queryParam]: MOCK_PROJECT.id,
nav_source: null,
});
expect(visitUrl).toHaveBeenCalled();
});
......@@ -157,4 +158,31 @@ describe('ProjectFilter', () => {
});
});
});
describe.each`
navSource | initialData | callMethod
${null} | ${null} | ${false}
${null} | ${MOCK_PROJECT} | ${false}
${'navbar'} | ${null} | ${false}
${'navbar'} | ${MOCK_PROJECT} | ${true}
`('onCreate', ({ navSource, initialData, callMethod }) => {
describe(`when nav_source is ${navSource} and ${
initialData ? 'has' : 'does not have'
} an initial project`, () => {
beforeEach(() => {
createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData });
});
it(`${callMethod ? 'does' : 'does not'} call setFrequentProject`, () => {
if (callMethod) {
expect(actionSpies.setFrequentProject).toHaveBeenCalledWith(
expect.any(Object),
initialData,
);
} else {
expect(actionSpies.setFrequentProject).not.toHaveBeenCalled();
}
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment