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>2021-05-19 18:44:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/frontend/frequent_items/components
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/frontend/frequent_items/components')
-rw-r--r--spec/frontend/frequent_items/components/app_spec.js365
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_item_spec.js16
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_spec.js10
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_search_input_spec.js16
4 files changed, 208 insertions, 199 deletions
diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js
index 80059c4c87f..7a1026e8bfc 100644
--- a/spec/frontend/frequent_items/components/app_spec.js
+++ b/spec/frontend/frequent_items/components/app_spec.js
@@ -1,10 +1,11 @@
import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
-import { useRealDate } from 'helpers/fake_date';
+import Vuex from 'vuex';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
-import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import appComponent from '~/frequent_items/components/app.vue';
+import App from '~/frequent_items/components/app.vue';
+import FrequentItemsList from '~/frequent_items/components/frequent_items_list.vue';
import { FREQUENT_ITEMS, HOUR_IN_MS } from '~/frequent_items/constants';
import eventHub from '~/frequent_items/event_hub';
import { createStore } from '~/frequent_items/store';
@@ -12,246 +13,230 @@ import { getTopFrequentItems } from '~/frequent_items/utils';
import axios from '~/lib/utils/axios_utils';
import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data';
+Vue.use(Vuex);
+
useLocalStorageSpy();
-let session;
-const createComponentWithStore = (namespace = 'projects') => {
- session = currentSession[namespace];
- gon.api_version = session.apiVersion;
- const Component = Vue.extend(appComponent);
- const store = createStore();
-
- return mountComponentWithStore(Component, {
- store,
- props: {
- namespace,
- currentUserName: session.username,
- currentItem: session.project || session.group,
- },
- });
-};
+const TEST_NAMESPACE = 'projects';
+const TEST_VUEX_MODULE = 'frequentProjects';
+const TEST_PROJECT = currentSession[TEST_NAMESPACE].project;
+const TEST_STORAGE_KEY = currentSession[TEST_NAMESPACE].storageKey;
describe('Frequent Items App Component', () => {
- let vm;
+ let wrapper;
let mock;
+ let store;
+
+ const createComponent = ({ currentItem = null } = {}) => {
+ const session = currentSession[TEST_NAMESPACE];
+ gon.api_version = session.apiVersion;
+
+ wrapper = mountExtended(App, {
+ store,
+ propsData: {
+ namespace: TEST_NAMESPACE,
+ currentUserName: session.username,
+ currentItem: currentItem || session.project,
+ },
+ provide: {
+ vuexModule: TEST_VUEX_MODULE,
+ },
+ });
+ };
+
+ const triggerDropdownOpen = () => eventHub.$emit(`${TEST_NAMESPACE}-dropdownOpen`);
+ const getStoredProjects = () => JSON.parse(localStorage.getItem(TEST_STORAGE_KEY));
+ const findSearchInput = () => wrapper.findByTestId('frequent-items-search-input');
+ const findLoading = () => wrapper.findByTestId('loading');
+ const findSectionHeader = () => wrapper.findByTestId('header');
+ const findFrequentItemsList = () => wrapper.findComponent(FrequentItemsList);
+ const findFrequentItems = () => findFrequentItemsList().findAll('li');
+ const setSearch = (search) => {
+ const searchInput = wrapper.find('input');
+
+ searchInput.setValue(search);
+ };
beforeEach(() => {
mock = new MockAdapter(axios);
- vm = createComponentWithStore();
+ store = createStore();
});
afterEach(() => {
mock.restore();
- vm.$destroy();
+ wrapper.destroy();
});
- describe('methods', () => {
- describe('dropdownOpenHandler', () => {
- it('should fetch frequent items when no search has been previously made on desktop', () => {
- jest.spyOn(vm, 'fetchFrequentItems').mockImplementation(() => {});
-
- vm.dropdownOpenHandler();
+ describe('default', () => {
+ beforeEach(() => {
+ jest.spyOn(store, 'dispatch');
- expect(vm.fetchFrequentItems).toHaveBeenCalledWith();
- });
+ createComponent();
});
- describe('logItemAccess', () => {
- let storage;
-
- beforeEach(() => {
- storage = {};
-
- localStorage.setItem.mockImplementation((storageKey, value) => {
- storage[storageKey] = value;
- });
-
- localStorage.getItem.mockImplementation((storageKey) => {
- if (storage[storageKey]) {
- return storage[storageKey];
- }
-
- return null;
- });
- });
+ it('should fetch frequent items', () => {
+ triggerDropdownOpen();
- it('should create a project store if it does not exist and adds a project', () => {
- vm.logItemAccess(session.storageKey, session.project);
-
- const projects = JSON.parse(storage[session.storageKey]);
-
- expect(projects.length).toBe(1);
- expect(projects[0].frequency).toBe(1);
- expect(projects[0].lastAccessedOn).toBeDefined();
- });
-
- it('should prevent inserting same report multiple times into store', () => {
- vm.logItemAccess(session.storageKey, session.project);
- vm.logItemAccess(session.storageKey, session.project);
-
- const projects = JSON.parse(storage[session.storageKey]);
-
- expect(projects.length).toBe(1);
- });
-
- describe('with real date', () => {
- useRealDate();
-
- it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
- let projects;
- const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+ expect(store.dispatch).toHaveBeenCalledWith(`${TEST_VUEX_MODULE}/fetchFrequentItems`);
+ });
- vm.logItemAccess(session.storageKey, session.project);
- projects = JSON.parse(storage[session.storageKey]);
+ it('should not fetch frequent items if detroyed', () => {
+ wrapper.destroy();
+ triggerDropdownOpen();
- expect(projects[0].frequency).toBe(1);
+ expect(store.dispatch).not.toHaveBeenCalledWith(`${TEST_VUEX_MODULE}/fetchFrequentItems`);
+ });
- vm.logItemAccess(session.storageKey, {
- ...session.project,
- lastAccessedOn: newTimestamp,
- });
- projects = JSON.parse(storage[session.storageKey]);
+ it('should render search input', () => {
+ expect(findSearchInput().exists()).toBe(true);
+ });
- expect(projects[0].frequency).toBe(2);
- expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
- });
- });
+ it('should render loading animation', async () => {
+ triggerDropdownOpen();
+ store.state[TEST_VUEX_MODULE].isLoadingItems = true;
- it('should always update project metadata', () => {
- let projects;
- const oldProject = {
- ...session.project,
- };
+ await wrapper.vm.$nextTick();
- const newProject = {
- ...session.project,
- name: 'New Name',
- avatarUrl: 'new/avatar.png',
- namespace: 'New / Namespace',
- webUrl: 'http://localhost/new/web/url',
- };
+ const loading = findLoading();
- vm.logItemAccess(session.storageKey, oldProject);
- projects = JSON.parse(storage[session.storageKey]);
+ expect(loading.exists()).toBe(true);
+ expect(loading.find('[aria-label="Loading projects"]').exists()).toBe(true);
+ });
- expect(projects[0].name).toBe(oldProject.name);
- expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl);
- expect(projects[0].namespace).toBe(oldProject.namespace);
- expect(projects[0].webUrl).toBe(oldProject.webUrl);
+ it('should render frequent projects list header', () => {
+ const sectionHeader = findSectionHeader();
- vm.logItemAccess(session.storageKey, newProject);
- projects = JSON.parse(storage[session.storageKey]);
+ expect(sectionHeader.exists()).toBe(true);
+ expect(sectionHeader.text()).toBe('Frequently visited');
+ });
- expect(projects[0].name).toBe(newProject.name);
- expect(projects[0].avatarUrl).toBe(newProject.avatarUrl);
- expect(projects[0].namespace).toBe(newProject.namespace);
- expect(projects[0].webUrl).toBe(newProject.webUrl);
- });
+ it('should render frequent projects list', async () => {
+ const expectedResult = getTopFrequentItems(mockFrequentProjects);
+ localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(mockFrequentProjects));
- it('should not add more than 20 projects in store', () => {
- for (let id = 0; id < FREQUENT_ITEMS.MAX_COUNT; id += 1) {
- const project = {
- ...session.project,
- id,
- };
- vm.logItemAccess(session.storageKey, project);
- }
+ expect(findFrequentItems().length).toBe(1);
- const projects = JSON.parse(storage[session.storageKey]);
+ triggerDropdownOpen();
+ await wrapper.vm.$nextTick();
- expect(projects.length).toBe(FREQUENT_ITEMS.MAX_COUNT);
+ expect(findFrequentItems().length).toBe(expectedResult.length);
+ expect(findFrequentItemsList().props()).toEqual({
+ items: expectedResult,
+ namespace: TEST_NAMESPACE,
+ hasSearchQuery: false,
+ isFetchFailed: false,
+ matcher: '',
});
});
- });
-
- describe('created', () => {
- it('should bind event listeners on eventHub', (done) => {
- jest.spyOn(eventHub, '$on').mockImplementation(() => {});
- createComponentWithStore().$mount();
-
- Vue.nextTick(() => {
- expect(eventHub.$on).toHaveBeenCalledWith('projects-dropdownOpen', expect.any(Function));
- done();
- });
+ it('should render searched projects list', async () => {
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects.data);
+
+ setSearch('gitlab');
+ await wrapper.vm.$nextTick();
+
+ expect(findLoading().exists()).toBe(true);
+
+ await waitForPromises();
+
+ expect(findFrequentItems().length).toBe(mockSearchedProjects.data.length);
+ expect(findFrequentItemsList().props()).toEqual(
+ expect.objectContaining({
+ items: mockSearchedProjects.data.map(
+ ({ avatar_url, web_url, name_with_namespace, ...item }) => ({
+ ...item,
+ avatarUrl: avatar_url,
+ webUrl: web_url,
+ namespace: name_with_namespace,
+ }),
+ ),
+ namespace: TEST_NAMESPACE,
+ hasSearchQuery: true,
+ isFetchFailed: false,
+ matcher: 'gitlab',
+ }),
+ );
});
});
- describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', (done) => {
- jest.spyOn(eventHub, '$off').mockImplementation(() => {});
+ describe('logging', () => {
+ it('when created, it should create a project storage entry and adds a project', () => {
+ createComponent();
- vm.$mount();
- vm.$destroy();
+ expect(getStoredProjects()).toEqual([
+ expect.objectContaining({
+ frequency: 1,
+ lastAccessedOn: Date.now(),
+ }),
+ ]);
+ });
- Vue.nextTick(() => {
- expect(eventHub.$off).toHaveBeenCalledWith('projects-dropdownOpen', expect.any(Function));
- done();
+ describe('when created multiple times', () => {
+ beforeEach(() => {
+ createComponent();
+ wrapper.destroy();
+ createComponent();
+ wrapper.destroy();
});
- });
- });
- describe('template', () => {
- it('should render search input', () => {
- expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
- });
+ it('should only log once', () => {
+ expect(getStoredProjects()).toEqual([
+ expect.objectContaining({
+ lastAccessedOn: Date.now(),
+ frequency: 1,
+ }),
+ ]);
+ });
- it('should render loading animation', (done) => {
- vm.$store.dispatch('fetchSearchedItems');
+ it('should increase frequency, when created an hour later', () => {
+ const hourLater = Date.now() + HOUR_IN_MS + 1;
- Vue.nextTick(() => {
- const loadingEl = vm.$el.querySelector('.loading-animation');
+ jest.spyOn(Date, 'now').mockReturnValue(hourLater);
+ createComponent({ currentItem: { ...TEST_PROJECT, lastAccessedOn: hourLater } });
- expect(loadingEl).toBeDefined();
- expect(loadingEl.classList.contains('prepend-top-20')).toBe(true);
- expect(loadingEl.querySelector('span').getAttribute('aria-label')).toBe('Loading projects');
- done();
+ expect(getStoredProjects()).toEqual([
+ expect.objectContaining({
+ lastAccessedOn: hourLater,
+ frequency: 2,
+ }),
+ ]);
});
});
- it('should render frequent projects list header', (done) => {
- Vue.nextTick(() => {
- const sectionHeaderEl = vm.$el.querySelector('.section-header');
+ it('should always update project metadata', () => {
+ const oldProject = {
+ ...TEST_PROJECT,
+ };
- expect(sectionHeaderEl).toBeDefined();
- expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
- done();
- });
- });
+ const newProject = {
+ ...oldProject,
+ name: 'New Name',
+ avatarUrl: 'new/avatar.png',
+ namespace: 'New / Namespace',
+ webUrl: 'http://localhost/new/web/url',
+ };
- it('should render frequent projects list', (done) => {
- const expectedResult = getTopFrequentItems(mockFrequentProjects);
- localStorage.getItem.mockImplementation(() => JSON.stringify(mockFrequentProjects));
+ createComponent({ currentItem: oldProject });
+ wrapper.destroy();
+ expect(getStoredProjects()).toEqual([expect.objectContaining(oldProject)]);
- expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
+ createComponent({ currentItem: newProject });
+ wrapper.destroy();
- vm.fetchFrequentItems();
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
- expectedResult.length,
- );
- done();
- });
+ expect(getStoredProjects()).toEqual([expect.objectContaining(newProject)]);
});
- it('should render searched projects list', (done) => {
- mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects);
-
- expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
-
- vm.$store.dispatch('setSearchQuery', 'gitlab');
- vm.$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
- })
- .then(waitForPromises)
- .then(() => {
- expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
- mockSearchedProjects.data.length,
- );
- })
- .then(done)
- .catch(done.fail);
+ it('should not add more than 20 projects in store', () => {
+ for (let id = 0; id < FREQUENT_ITEMS.MAX_COUNT + 10; id += 1) {
+ const project = {
+ ...TEST_PROJECT,
+ id,
+ };
+ createComponent({ currentItem: project });
+ wrapper.destroy();
+ }
+
+ expect(getStoredProjects().length).toBe(FREQUENT_ITEMS.MAX_COUNT);
});
});
});
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
index 66fb346cb38..9a68115e4f6 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
@@ -1,14 +1,18 @@
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
import { trimText } from 'helpers/text_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import { createStore } from '~/frequent_items/store';
import { mockProject } from '../mock_data';
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
describe('FrequentItemsListItemComponent', () => {
let wrapper;
let trackingSpy;
- let store = createStore();
+ let store;
const findTitle = () => wrapper.find({ ref: 'frequentItemsItemTitle' });
const findAvatar = () => wrapper.find({ ref: 'frequentItemsItemAvatar' });
@@ -31,11 +35,15 @@ describe('FrequentItemsListItemComponent', () => {
avatarUrl: mockProject.avatarUrl,
...props,
},
+ provide: {
+ vuexModule: 'frequentProjects',
+ },
+ localVue,
});
};
beforeEach(() => {
- store = createStore({ dropdownType: 'project' });
+ store = createStore();
trackingSpy = mockTracking('_category_', document, jest.spyOn);
trackingSpy.mockImplementation(() => {});
});
@@ -119,7 +127,7 @@ describe('FrequentItemsListItemComponent', () => {
});
link.trigger('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
- label: 'project_dropdown_frequent_items_list_item',
+ label: 'projects_dropdown_frequent_items_list_item',
});
});
});
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
index bd0711005b3..c015914c991 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
@@ -1,9 +1,13 @@
-import { mount } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import { createStore } from '~/frequent_items/store';
import { mockFrequentProjects } from '../mock_data';
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
describe('FrequentItemsListComponent', () => {
let wrapper;
@@ -18,6 +22,10 @@ describe('FrequentItemsListComponent', () => {
matcher: 'lab',
...props,
},
+ localVue,
+ provide: {
+ vuexModule: 'frequentProjects',
+ },
});
};
diff --git a/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js b/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
index 0280fdb0ca2..c9b7e0f3d13 100644
--- a/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
@@ -1,9 +1,13 @@
import { GlSearchBoxByType } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import searchComponent from '~/frequent_items/components/frequent_items_search_input.vue';
import { createStore } from '~/frequent_items/store';
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
describe('FrequentItemsSearchInputComponent', () => {
let wrapper;
let trackingSpy;
@@ -14,12 +18,16 @@ describe('FrequentItemsSearchInputComponent', () => {
shallowMount(searchComponent, {
store,
propsData: { namespace },
+ localVue,
+ provide: {
+ vuexModule: 'frequentProjects',
+ },
});
const findSearchBoxByType = () => wrapper.find(GlSearchBoxByType);
beforeEach(() => {
- store = createStore({ dropdownType: 'project' });
+ store = createStore();
jest.spyOn(store, 'dispatch').mockImplementation(() => {});
trackingSpy = mockTracking('_category_', document, jest.spyOn);
@@ -57,9 +65,9 @@ describe('FrequentItemsSearchInputComponent', () => {
await wrapper.vm.$nextTick();
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'type_search_query', {
- label: 'project_dropdown_frequent_items_search_input',
+ label: 'projects_dropdown_frequent_items_search_input',
});
- expect(store.dispatch).toHaveBeenCalledWith('setSearchQuery', value);
+ expect(store.dispatch).toHaveBeenCalledWith('frequentProjects/setSearchQuery', value);
});
});
});