193362482d
A number of the karma tests looked for the .fa-spinner class or the icon element used for the font awesome spinner class. These instances have been refactored to now look for the .spinner class instead.
250 lines
7.7 KiB
JavaScript
250 lines
7.7 KiB
JavaScript
import MockAdapter from 'axios-mock-adapter';
|
|
import axios from '~/lib/utils/axios_utils';
|
|
import Vue from 'vue';
|
|
import appComponent from '~/frequent_items/components/app.vue';
|
|
import eventHub from '~/frequent_items/event_hub';
|
|
import store from '~/frequent_items/store';
|
|
import { FREQUENT_ITEMS, HOUR_IN_MS } from '~/frequent_items/constants';
|
|
import { getTopFrequentItems } from '~/frequent_items/utils';
|
|
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
|
import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data';
|
|
|
|
let session;
|
|
const createComponentWithStore = (namespace = 'projects') => {
|
|
session = currentSession[namespace];
|
|
gon.api_version = session.apiVersion;
|
|
const Component = Vue.extend(appComponent);
|
|
|
|
return mountComponentWithStore(Component, {
|
|
store,
|
|
props: {
|
|
namespace,
|
|
currentUserName: session.username,
|
|
currentItem: session.project || session.group,
|
|
},
|
|
});
|
|
};
|
|
|
|
describe('Frequent Items App Component', () => {
|
|
let vm;
|
|
let mock;
|
|
|
|
beforeEach(() => {
|
|
mock = new MockAdapter(axios);
|
|
vm = createComponentWithStore();
|
|
});
|
|
|
|
afterEach(() => {
|
|
mock.restore();
|
|
vm.$destroy();
|
|
});
|
|
|
|
describe('methods', () => {
|
|
describe('dropdownOpenHandler', () => {
|
|
it('should fetch frequent items when no search has been previously made on desktop', () => {
|
|
spyOn(vm, 'fetchFrequentItems');
|
|
|
|
vm.dropdownOpenHandler();
|
|
|
|
expect(vm.fetchFrequentItems).toHaveBeenCalledWith();
|
|
});
|
|
});
|
|
|
|
describe('logItemAccess', () => {
|
|
let storage;
|
|
|
|
beforeEach(() => {
|
|
storage = {};
|
|
|
|
spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => {
|
|
storage[storageKey] = value;
|
|
});
|
|
|
|
spyOn(window.localStorage, 'getItem').and.callFake(storageKey => {
|
|
if (storage[storageKey]) {
|
|
return storage[storageKey];
|
|
}
|
|
|
|
return null;
|
|
});
|
|
});
|
|
|
|
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);
|
|
});
|
|
|
|
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;
|
|
|
|
vm.logItemAccess(session.storageKey, session.project);
|
|
projects = JSON.parse(storage[session.storageKey]);
|
|
|
|
expect(projects[0].frequency).toBe(1);
|
|
|
|
vm.logItemAccess(session.storageKey, {
|
|
...session.project,
|
|
lastAccessedOn: newTimestamp,
|
|
});
|
|
projects = JSON.parse(storage[session.storageKey]);
|
|
|
|
expect(projects[0].frequency).toBe(2);
|
|
expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
|
|
});
|
|
|
|
it('should always update project metadata', () => {
|
|
let projects;
|
|
const oldProject = {
|
|
...session.project,
|
|
};
|
|
|
|
const newProject = {
|
|
...session.project,
|
|
name: 'New Name',
|
|
avatarUrl: 'new/avatar.png',
|
|
namespace: 'New / Namespace',
|
|
webUrl: 'http://localhost/new/web/url',
|
|
};
|
|
|
|
vm.logItemAccess(session.storageKey, oldProject);
|
|
projects = JSON.parse(storage[session.storageKey]);
|
|
|
|
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);
|
|
|
|
vm.logItemAccess(session.storageKey, newProject);
|
|
projects = JSON.parse(storage[session.storageKey]);
|
|
|
|
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 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);
|
|
}
|
|
|
|
const projects = JSON.parse(storage[session.storageKey]);
|
|
|
|
expect(projects.length).toBe(FREQUENT_ITEMS.MAX_COUNT);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('created', () => {
|
|
it('should bind event listeners on eventHub', done => {
|
|
spyOn(eventHub, '$on');
|
|
|
|
createComponentWithStore().$mount();
|
|
|
|
Vue.nextTick(() => {
|
|
expect(eventHub.$on).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('beforeDestroy', () => {
|
|
it('should unbind event listeners on eventHub', done => {
|
|
spyOn(eventHub, '$off');
|
|
|
|
vm.$mount();
|
|
vm.$destroy();
|
|
|
|
Vue.nextTick(() => {
|
|
expect(eventHub.$off).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('template', () => {
|
|
it('should render search input', () => {
|
|
expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
|
|
});
|
|
|
|
it('should render loading animation', done => {
|
|
vm.$store.dispatch('fetchSearchedItems');
|
|
|
|
Vue.nextTick(() => {
|
|
const loadingEl = vm.$el.querySelector('.loading-animation');
|
|
|
|
expect(loadingEl).toBeDefined();
|
|
expect(loadingEl.classList.contains('prepend-top-20')).toBe(true);
|
|
expect(loadingEl.querySelector('span').getAttribute('aria-label')).toBe('Loading projects');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should render frequent projects list header', done => {
|
|
Vue.nextTick(() => {
|
|
const sectionHeaderEl = vm.$el.querySelector('.section-header');
|
|
|
|
expect(sectionHeaderEl).toBeDefined();
|
|
expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should render frequent projects list', done => {
|
|
const expectedResult = getTopFrequentItems(mockFrequentProjects);
|
|
spyOn(window.localStorage, 'getItem').and.callFake(() =>
|
|
JSON.stringify(mockFrequentProjects),
|
|
);
|
|
|
|
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
|
|
|
|
vm.fetchFrequentItems();
|
|
Vue.nextTick(() => {
|
|
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
|
|
expectedResult.length,
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
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(vm.$nextTick)
|
|
.then(vm.$nextTick)
|
|
.then(() => {
|
|
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
|
|
mockSearchedProjects.length,
|
|
);
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
});
|
|
});
|