diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-19 12:08:12 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-19 12:08:12 +0300 |
commit | 57d1bb82549c6713f87f87d5f35eec3d867c83db (patch) | |
tree | 22f708344121786e286fd318fbfbfda632200909 /spec/javascripts | |
parent | 4e81d9c050bfea4c866329155c17b929d7381340 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/javascripts')
-rw-r--r-- | spec/javascripts/filtered_search/filtered_search_manager_spec.js | 580 | ||||
-rw-r--r-- | spec/javascripts/filtered_search/recent_searches_root_spec.js | 30 | ||||
-rw-r--r-- | spec/javascripts/frequent_items/components/app_spec.js | 257 | ||||
-rw-r--r-- | spec/javascripts/frequent_items/mock_data.js | 168 | ||||
-rw-r--r-- | spec/javascripts/frequent_items/store/actions_spec.js | 228 | ||||
-rw-r--r-- | spec/javascripts/frequent_items/store/mutations_spec.js | 117 | ||||
-rw-r--r-- | spec/javascripts/frequent_items/utils_spec.js | 130 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/csrf_token_spec.js | 50 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/navigation_utility_spec.js | 23 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/poll_spec.js | 222 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/sticky_spec.js | 66 | ||||
-rw-r--r-- | spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js | 88 | ||||
-rw-r--r-- | spec/javascripts/related_merge_requests/store/actions_spec.js | 110 | ||||
-rw-r--r-- | spec/javascripts/related_merge_requests/store/mutations_spec.js | 49 | ||||
-rw-r--r-- | spec/javascripts/sidebar/mock_data.js | 2 |
15 files changed, 1 insertions, 2119 deletions
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js deleted file mode 100644 index d0b54a16747..00000000000 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ /dev/null @@ -1,580 +0,0 @@ -import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; -import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; -import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; -import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import '~/lib/utils/common_utils'; -import DropdownUtils from '~/filtered_search/dropdown_utils'; -import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; -import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; -import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; -import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; -import { BACKSPACE_KEY_CODE, DELETE_KEY_CODE } from '~/lib/utils/keycodes'; - -describe('Filtered Search Manager', function() { - let input; - let manager; - let tokensContainer; - const page = 'issues'; - const placeholder = 'Search or filter results...'; - - function dispatchBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchDeleteEvent(element, eventType) { - const event = new Event(eventType); - event.keyCode = DELETE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchAltBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.altKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchCtrlBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.ctrlKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchMetaBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.metaKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function getVisualTokens() { - return tokensContainer.querySelectorAll('.js-visual-token'); - } - - beforeEach(() => { - setFixtures(` - <div class="filtered-search-box"> - <form> - <ul class="tokens-container list-unstyled"> - ${FilteredSearchSpecHelper.createInputHTML(placeholder)} - </ul> - <button class="clear-search" type="button"> - <i class="fa fa-times"></i> - </button> - </form> - </div> - `); - - spyOn(FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {}); - }); - - const initializeManager = () => { - /* eslint-disable jasmine/no-unsafe-spy */ - spyOn(FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {}); - spyOn(FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {}); - spyOn(FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {}); - spyOn(gl.utils, 'getParameterByName').and.returnValue(null); - spyOn(FilteredSearchVisualTokens, 'unselectTokens').and.callThrough(); - /* eslint-enable jasmine/no-unsafe-spy */ - - input = document.querySelector('.filtered-search'); - tokensContainer = document.querySelector('.tokens-container'); - manager = new FilteredSearchManager({ page }); - manager.setup(); - }; - - afterEach(() => { - manager.cleanup(); - }); - - describe('class constructor', () => { - const isLocalStorageAvailable = 'isLocalStorageAvailable'; - let RecentSearchesStoreSpy; - - beforeEach(() => { - spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable); - spyOn(RecentSearchesRoot.prototype, 'render'); - RecentSearchesStoreSpy = spyOnDependency(FilteredSearchManager, 'RecentSearchesStore'); - }); - - it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => { - manager = new FilteredSearchManager({ page }); - - expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); - expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({ - isLocalStorageAvailable, - allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(), - }); - }); - }); - - describe('setup', () => { - beforeEach(() => { - manager = new FilteredSearchManager({ page }); - }); - - it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => { - spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => - Promise.reject(new RecentSearchesServiceError()), - ); - spyOn(window, 'Flash'); - - manager.setup(); - - expect(window.Flash).not.toHaveBeenCalled(); - }); - }); - - describe('searchState', () => { - beforeEach(() => { - spyOn(FilteredSearchManager.prototype, 'search').and.callFake(() => {}); - initializeManager(); - }); - - it('should blur button', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - }, - }; - spyOn(e.currentTarget, 'blur').and.callThrough(); - manager.searchState(e); - - expect(e.currentTarget.blur).toHaveBeenCalled(); - }); - - it('should not call search if there is no state', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - }, - }; - - manager.searchState(e); - - expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled(); - }); - - it('should call search when there is state', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - dataset: { - state: 'opened', - }, - }, - }; - - manager.searchState(e); - - expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened'); - }); - }); - - describe('search', () => { - const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened'; - - beforeEach(() => { - initializeManager(); - }); - - it('should search with a single word', done => { - input.value = 'searchTerm'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&search=searchTerm`); - done(); - }); - - manager.search(); - }); - - it('should search with multiple words', done => { - input.value = 'awesome search terms'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`); - done(); - }); - - manager.search(); - }); - - it('should search with special characters', done => { - input.value = '~!@#$%^&*()_+{}:<>,.?/'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual( - `${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`, - ); - done(); - }); - - manager.search(); - }); - - it('removes duplicated tokens', done => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` - ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug')} - ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug')} - `); - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&label_name[]=bug`); - done(); - }); - - manager.search(); - }); - }); - - describe('handleInputPlaceholder', () => { - beforeEach(() => { - initializeManager(); - }); - - it('should render placeholder when there is no input', () => { - expect(input.placeholder).toEqual(placeholder); - }); - - it('should not render placeholder when there is input', () => { - input.value = 'test words'; - - const event = new Event('input'); - input.dispatchEvent(event); - - expect(input.placeholder).toEqual(''); - }); - - it('should not render placeholder when there are tokens and no input', () => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - - const event = new Event('input'); - input.dispatchEvent(event); - - expect(input.placeholder).toEqual(''); - }); - }); - - describe('checkForBackspace', () => { - beforeEach(() => { - initializeManager(); - }); - - describe('tokens and no input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes last token', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - dispatchBackspaceEvent(input, 'keyup'); - dispatchBackspaceEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - - it('sets the input', () => { - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - dispatchDeleteEvent(input, 'keyup'); - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled(); - expect(input.value).toEqual('~bug'); - }); - }); - - it('does not remove token or change input when there is existing input', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - - input.value = 'text'; - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - - it('does not remove previous token on single backspace press', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - - input.value = 't'; - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('t'); - }); - }); - - describe('checkForAltOrCtrlBackspace', () => { - beforeEach(() => { - initializeManager(); - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - }); - - describe('tokens and no input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes last token via alt-backspace', () => { - dispatchAltBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - - it('removes last token via ctrl-backspace', () => { - dispatchCtrlBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - }); - - describe('tokens and input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('does not remove token or change input via alt-backspace when there is existing input', () => { - input = manager.filteredSearchInput; - input.value = 'text'; - dispatchAltBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - - it('does not remove token or change input via ctrl-backspace when there is existing input', () => { - input = manager.filteredSearchInput; - input.value = 'text'; - dispatchCtrlBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - }); - }); - - describe('checkForMetaBackspace', () => { - beforeEach(() => { - initializeManager(); - }); - - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes all tokens and input', () => { - spyOn(FilteredSearchManager.prototype, 'clearSearch').and.callThrough(); - dispatchMetaBackspaceEvent(input, 'keydown'); - - expect(manager.clearSearch).toHaveBeenCalled(); - expect(manager.filteredSearchInput.value).toEqual(''); - expect(DropdownUtils.getSearchQuery()).toEqual(''); - }); - }); - - describe('removeToken', () => { - beforeEach(() => { - initializeManager(); - }); - - it('removes token even when it is already selected', () => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none', true), - ); - - tokensContainer.querySelector('.js-visual-token .remove-token').click(); - - expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null); - }); - - describe('unselected token', () => { - beforeEach(() => { - spyOn(FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough(); - - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none'), - ); - tokensContainer.querySelector('.js-visual-token .remove-token').click(); - }); - - it('removes token when remove button is selected', () => { - expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null); - }); - - it('calls removeSelectedToken', () => { - expect(manager.removeSelectedToken).toHaveBeenCalled(); - }); - }); - }); - - describe('removeSelectedTokenKeydown', () => { - beforeEach(() => { - initializeManager(); - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none', true), - ); - }); - - it('removes selected token when the backspace key is pressed', () => { - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(getVisualTokens().length).toEqual(0); - }); - - it('removes selected token when the delete key is pressed', () => { - expect(getVisualTokens().length).toEqual(1); - - dispatchDeleteEvent(document, 'keydown'); - - expect(getVisualTokens().length).toEqual(0); - }); - - it('updates the input placeholder after removal', () => { - manager.handleInputPlaceholder(); - - expect(input.placeholder).toEqual(''); - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(input.placeholder).not.toEqual(''); - expect(getVisualTokens().length).toEqual(0); - }); - - it('updates the clear button after removal', () => { - manager.toggleClearSearchButton(); - - const clearButton = document.querySelector('.clear-search'); - - expect(clearButton.classList.contains('hidden')).toEqual(false); - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(clearButton.classList.contains('hidden')).toEqual(true); - expect(getVisualTokens().length).toEqual(0); - }); - }); - - describe('removeSelectedToken', () => { - beforeEach(() => { - spyOn(FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough(); - spyOn(FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough(); - spyOn(FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough(); - initializeManager(); - }); - - it('calls FilteredSearchVisualTokens.removeSelectedToken', () => { - manager.removeSelectedToken(); - - expect(FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled(); - }); - - it('calls handleInputPlaceholder', () => { - manager.removeSelectedToken(); - - expect(manager.handleInputPlaceholder).toHaveBeenCalled(); - }); - - it('calls toggleClearSearchButton', () => { - manager.removeSelectedToken(); - - expect(manager.toggleClearSearchButton).toHaveBeenCalled(); - }); - - it('calls update dropdown offset', () => { - manager.removeSelectedToken(); - - expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled(); - }); - }); - - describe('Clearing search', () => { - beforeEach(() => { - initializeManager(); - }); - - it('Clicking the "x" clear button, clears the input', () => { - const inputValue = 'label:=~bug'; - manager.filteredSearchInput.value = inputValue; - manager.filteredSearchInput.dispatchEvent(new Event('input')); - - expect(DropdownUtils.getSearchQuery()).toEqual(inputValue); - - manager.clearSearchButton.click(); - - expect(manager.filteredSearchInput.value).toEqual(''); - expect(DropdownUtils.getSearchQuery()).toEqual(''); - }); - }); - - describe('toggleInputContainerFocus', () => { - beforeEach(() => { - initializeManager(); - }); - - it('toggles on focus', () => { - input.focus(); - - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( - true, - ); - }); - - it('toggles on blur', () => { - input.blur(); - - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( - false, - ); - }); - }); - - describe('getAllParams', () => { - beforeEach(() => { - this.paramsArr = ['key=value', 'otherkey=othervalue']; - - initializeManager(); - }); - - it('correctly modifies params when custom modifier is passed', () => { - const modifedParams = manager.getAllParams.call( - { - modifyUrlParams: paramsArr => paramsArr.reverse(), - }, - [].concat(this.paramsArr), - ); - - expect(modifedParams[0]).toBe(this.paramsArr[1]); - }); - - it('does not modify params when no custom modifier is passed', () => { - const modifedParams = manager.getAllParams.call({}, this.paramsArr); - - expect(modifedParams[1]).toBe(this.paramsArr[1]); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js deleted file mode 100644 index 70dd4e9570d..00000000000 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; - -describe('RecentSearchesRoot', () => { - describe('render', () => { - let recentSearchesRoot; - let data; - let template; - let VueSpy; - - beforeEach(() => { - recentSearchesRoot = { - store: { - state: 'state', - }, - }; - - VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake(options => { - ({ data, template } = options); - }); - - RecentSearchesRoot.prototype.render.call(recentSearchesRoot); - }); - - it('should instantiate Vue', () => { - expect(VueSpy).toHaveBeenCalled(); - expect(data()).toBe(recentSearchesRoot.store.state); - expect(template).toContain(':is-local-storage-available="isLocalStorageAvailable"'); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js deleted file mode 100644 index b293ed541fd..00000000000 --- a/spec/javascripts/frequent_items/components/app_spec.js +++ /dev/null @@ -1,257 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import Vue from 'vue'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -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 { 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(); - }) - - // This test waits for multiple ticks in order to allow the responses to - // propagate through each interceptor installed on the Axios instance. - // This shouldn't be necessary; this test should be refactored to avoid this. - // https://gitlab.com/gitlab-org/gitlab/issues/32479 - .then(vm.$nextTick) - .then(vm.$nextTick) - .then(vm.$nextTick) - - .then(() => { - expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe( - mockSearchedProjects.data.length, - ); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/mock_data.js b/spec/javascripts/frequent_items/mock_data.js deleted file mode 100644 index 419f70e41af..00000000000 --- a/spec/javascripts/frequent_items/mock_data.js +++ /dev/null @@ -1,168 +0,0 @@ -export const currentSession = { - groups: { - username: 'root', - storageKey: 'root/frequent-groups', - apiVersion: 'v4', - group: { - id: 1, - name: 'dummy-group', - full_name: 'dummy-parent-group', - webUrl: `${gl.TEST_HOST}/dummy-group`, - avatarUrl: null, - lastAccessedOn: Date.now(), - }, - }, - projects: { - username: 'root', - storageKey: 'root/frequent-projects', - apiVersion: 'v4', - project: { - id: 1, - name: 'dummy-project', - namespace: 'SampleGroup / Dummy-Project', - webUrl: `${gl.TEST_HOST}/samplegroup/dummy-project`, - avatarUrl: null, - lastAccessedOn: Date.now(), - }, - }, -}; - -export const mockNamespace = 'projects'; - -export const mockStorageKey = 'test-user/frequent-projects'; - -export const mockGroup = { - id: 1, - name: 'Sub451', - namespace: 'Commit451 / Sub451', - webUrl: `${gl.TEST_HOST}/Commit451/Sub451`, - avatarUrl: null, -}; - -export const mockRawGroup = { - id: 1, - name: 'Sub451', - full_name: 'Commit451 / Sub451', - web_url: `${gl.TEST_HOST}/Commit451/Sub451`, - avatar_url: null, -}; - -export const mockFrequentGroups = [ - { - id: 3, - name: 'Subgroup451', - full_name: 'Commit451 / Subgroup451', - webUrl: '/Commit451/Subgroup451', - avatarUrl: null, - frequency: 7, - lastAccessedOn: 1497979281815, - }, - { - id: 1, - name: 'Commit451', - full_name: 'Commit451', - webUrl: '/Commit451', - avatarUrl: null, - frequency: 3, - lastAccessedOn: 1497979281815, - }, -]; - -export const mockSearchedGroups = [mockRawGroup]; -export const mockProcessedSearchedGroups = [mockGroup]; - -export const mockProject = { - id: 1, - name: 'GitLab Community Edition', - namespace: 'gitlab-org / gitlab-ce', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatarUrl: null, -}; - -export const mockRawProject = { - id: 1, - name: 'GitLab Community Edition', - name_with_namespace: 'gitlab-org / gitlab-ce', - web_url: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatar_url: null, -}; - -export const mockFrequentProjects = [ - { - id: 1, - name: 'GitLab Community Edition', - namespace: 'gitlab-org / gitlab-ce', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatarUrl: null, - frequency: 1, - lastAccessedOn: Date.now(), - }, - { - id: 2, - name: 'GitLab CI', - namespace: 'gitlab-org / gitlab-ci', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ci`, - avatarUrl: null, - frequency: 9, - lastAccessedOn: Date.now(), - }, - { - id: 3, - name: 'Typeahead.Js', - namespace: 'twitter / typeahead-js', - webUrl: `${gl.TEST_HOST}/twitter/typeahead-js`, - avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png', - frequency: 2, - lastAccessedOn: Date.now(), - }, - { - id: 4, - name: 'Intel', - namespace: 'platform / hardware / bsp / intel', - webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/intel`, - avatarUrl: null, - frequency: 3, - lastAccessedOn: Date.now(), - }, - { - id: 5, - name: 'v4.4', - namespace: 'platform / hardware / bsp / kernel / common / v4.4', - webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/kernel/common/v4.4`, - avatarUrl: null, - frequency: 8, - lastAccessedOn: Date.now(), - }, -]; - -export const mockSearchedProjects = { data: [mockRawProject] }; -export const mockProcessedSearchedProjects = [mockProject]; - -export const unsortedFrequentItems = [ - { id: 1, frequency: 12, lastAccessedOn: 1491400843391 }, - { id: 2, frequency: 14, lastAccessedOn: 1488240890738 }, - { id: 3, frequency: 44, lastAccessedOn: 1497675908472 }, - { id: 4, frequency: 8, lastAccessedOn: 1497979281815 }, - { id: 5, frequency: 34, lastAccessedOn: 1488089211943 }, - { id: 6, frequency: 14, lastAccessedOn: 1493517292488 }, - { id: 7, frequency: 42, lastAccessedOn: 1486815299875 }, - { id: 8, frequency: 33, lastAccessedOn: 1500762279114 }, - { id: 10, frequency: 46, lastAccessedOn: 1483251641543 }, -]; - -/** - * This const has a specific order which tests authenticity - * of `getTopFrequentItems` method so - * DO NOT change order of items in this const. - */ -export const sortedFrequentItems = [ - { id: 10, frequency: 46, lastAccessedOn: 1483251641543 }, - { id: 3, frequency: 44, lastAccessedOn: 1497675908472 }, - { id: 7, frequency: 42, lastAccessedOn: 1486815299875 }, - { id: 5, frequency: 34, lastAccessedOn: 1488089211943 }, - { id: 8, frequency: 33, lastAccessedOn: 1500762279114 }, - { id: 6, frequency: 14, lastAccessedOn: 1493517292488 }, - { id: 2, frequency: 14, lastAccessedOn: 1488240890738 }, - { id: 1, frequency: 12, lastAccessedOn: 1491400843391 }, - { id: 4, frequency: 8, lastAccessedOn: 1497979281815 }, -]; diff --git a/spec/javascripts/frequent_items/store/actions_spec.js b/spec/javascripts/frequent_items/store/actions_spec.js deleted file mode 100644 index 7b065b69cce..00000000000 --- a/spec/javascripts/frequent_items/store/actions_spec.js +++ /dev/null @@ -1,228 +0,0 @@ -import testAction from 'spec/helpers/vuex_action_helper'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import AccessorUtilities from '~/lib/utils/accessor'; -import * as actions from '~/frequent_items/store/actions'; -import * as types from '~/frequent_items/store/mutation_types'; -import state from '~/frequent_items/store/state'; -import { - mockNamespace, - mockStorageKey, - mockFrequentProjects, - mockSearchedProjects, -} from '../mock_data'; - -describe('Frequent Items Dropdown Store Actions', () => { - let mockedState; - let mock; - - beforeEach(() => { - mockedState = state(); - mock = new MockAdapter(axios); - - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - }); - - afterEach(() => { - mock.restore(); - }); - - describe('setNamespace', () => { - it('should set namespace', done => { - testAction( - actions.setNamespace, - mockNamespace, - mockedState, - [{ type: types.SET_NAMESPACE, payload: mockNamespace }], - [], - done, - ); - }); - }); - - describe('setStorageKey', () => { - it('should set storage key', done => { - testAction( - actions.setStorageKey, - mockStorageKey, - mockedState, - [{ type: types.SET_STORAGE_KEY, payload: mockStorageKey }], - [], - done, - ); - }); - }); - - describe('requestFrequentItems', () => { - it('should request frequent items', done => { - testAction( - actions.requestFrequentItems, - null, - mockedState, - [{ type: types.REQUEST_FREQUENT_ITEMS }], - [], - done, - ); - }); - }); - - describe('receiveFrequentItemsSuccess', () => { - it('should set frequent items', done => { - testAction( - actions.receiveFrequentItemsSuccess, - mockFrequentProjects, - mockedState, - [{ type: types.RECEIVE_FREQUENT_ITEMS_SUCCESS, payload: mockFrequentProjects }], - [], - done, - ); - }); - }); - - describe('receiveFrequentItemsError', () => { - it('should set frequent items error state', done => { - testAction( - actions.receiveFrequentItemsError, - null, - mockedState, - [{ type: types.RECEIVE_FREQUENT_ITEMS_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchFrequentItems', () => { - it('should dispatch `receiveFrequentItemsSuccess`', done => { - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - - testAction( - actions.fetchFrequentItems, - null, - mockedState, - [], - [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsSuccess', payload: [] }], - done, - ); - }); - - it('should dispatch `receiveFrequentItemsError`', done => { - spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(false); - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - - testAction( - actions.fetchFrequentItems, - null, - mockedState, - [], - [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsError' }], - done, - ); - }); - }); - - describe('requestSearchedItems', () => { - it('should request searched items', done => { - testAction( - actions.requestSearchedItems, - null, - mockedState, - [{ type: types.REQUEST_SEARCHED_ITEMS }], - [], - done, - ); - }); - }); - - describe('receiveSearchedItemsSuccess', () => { - it('should set searched items', done => { - testAction( - actions.receiveSearchedItemsSuccess, - mockSearchedProjects, - mockedState, - [{ type: types.RECEIVE_SEARCHED_ITEMS_SUCCESS, payload: mockSearchedProjects }], - [], - done, - ); - }); - }); - - describe('receiveSearchedItemsError', () => { - it('should set searched items error state', done => { - testAction( - actions.receiveSearchedItemsError, - null, - mockedState, - [{ type: types.RECEIVE_SEARCHED_ITEMS_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchSearchedItems', () => { - beforeEach(() => { - gon.api_version = 'v4'; - }); - - it('should dispatch `receiveSearchedItemsSuccess`', done => { - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects, {}); - - testAction( - actions.fetchSearchedItems, - null, - mockedState, - [], - [ - { type: 'requestSearchedItems' }, - { - type: 'receiveSearchedItemsSuccess', - payload: { data: mockSearchedProjects, headers: {} }, - }, - ], - done, - ); - }); - - it('should dispatch `receiveSearchedItemsError`', done => { - gon.api_version = 'v4'; - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(500); - - testAction( - actions.fetchSearchedItems, - null, - mockedState, - [], - [{ type: 'requestSearchedItems' }, { type: 'receiveSearchedItemsError' }], - done, - ); - }); - }); - - describe('setSearchQuery', () => { - it('should commit query and dispatch `fetchSearchedItems` when query is present', done => { - testAction( - actions.setSearchQuery, - { query: 'test' }, - mockedState, - [{ type: types.SET_SEARCH_QUERY, payload: { query: 'test' } }], - [{ type: 'fetchSearchedItems', payload: { query: 'test' } }], - done, - ); - }); - - it('should commit query and dispatch `fetchFrequentItems` when query is empty', done => { - testAction( - actions.setSearchQuery, - null, - mockedState, - [{ type: types.SET_SEARCH_QUERY, payload: null }], - [{ type: 'fetchFrequentItems' }], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/store/mutations_spec.js b/spec/javascripts/frequent_items/store/mutations_spec.js deleted file mode 100644 index d36964b2600..00000000000 --- a/spec/javascripts/frequent_items/store/mutations_spec.js +++ /dev/null @@ -1,117 +0,0 @@ -import state from '~/frequent_items/store/state'; -import mutations from '~/frequent_items/store/mutations'; -import * as types from '~/frequent_items/store/mutation_types'; -import { - mockNamespace, - mockStorageKey, - mockFrequentProjects, - mockSearchedProjects, - mockProcessedSearchedProjects, - mockSearchedGroups, - mockProcessedSearchedGroups, -} from '../mock_data'; - -describe('Frequent Items dropdown mutations', () => { - let stateCopy; - - beforeEach(() => { - stateCopy = state(); - }); - - describe('SET_NAMESPACE', () => { - it('should set namespace', () => { - mutations[types.SET_NAMESPACE](stateCopy, mockNamespace); - - expect(stateCopy.namespace).toEqual(mockNamespace); - }); - }); - - describe('SET_STORAGE_KEY', () => { - it('should set storage key', () => { - mutations[types.SET_STORAGE_KEY](stateCopy, mockStorageKey); - - expect(stateCopy.storageKey).toEqual(mockStorageKey); - }); - }); - - describe('SET_SEARCH_QUERY', () => { - it('should set search query', () => { - const searchQuery = 'gitlab-ce'; - - mutations[types.SET_SEARCH_QUERY](stateCopy, searchQuery); - - expect(stateCopy.searchQuery).toEqual(searchQuery); - }); - }); - - describe('REQUEST_FREQUENT_ITEMS', () => { - it('should set view states when requesting frequent items', () => { - mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy); - - expect(stateCopy.isLoadingItems).toEqual(true); - expect(stateCopy.hasSearchQuery).toEqual(false); - }); - }); - - describe('RECEIVE_FREQUENT_ITEMS_SUCCESS', () => { - it('should set view states when receiving frequent items', () => { - mutations[types.RECEIVE_FREQUENT_ITEMS_SUCCESS](stateCopy, mockFrequentProjects); - - expect(stateCopy.items).toEqual(mockFrequentProjects); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(false); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - }); - - describe('RECEIVE_FREQUENT_ITEMS_ERROR', () => { - it('should set items and view states when error occurs retrieving frequent items', () => { - mutations[types.RECEIVE_FREQUENT_ITEMS_ERROR](stateCopy); - - expect(stateCopy.items).toEqual([]); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(false); - expect(stateCopy.isFetchFailed).toEqual(true); - }); - }); - - describe('REQUEST_SEARCHED_ITEMS', () => { - it('should set view states when requesting searched items', () => { - mutations[types.REQUEST_SEARCHED_ITEMS](stateCopy); - - expect(stateCopy.isLoadingItems).toEqual(true); - expect(stateCopy.hasSearchQuery).toEqual(true); - }); - }); - - describe('RECEIVE_SEARCHED_ITEMS_SUCCESS', () => { - it('should set items and view states when receiving searched items', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedProjects); - - expect(stateCopy.items).toEqual(mockProcessedSearchedProjects); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - - it('should also handle the different `full_name` key for namespace in groups payload', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedGroups); - - expect(stateCopy.items).toEqual(mockProcessedSearchedGroups); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - }); - - describe('RECEIVE_SEARCHED_ITEMS_ERROR', () => { - it('should set view states when error occurs retrieving searched items', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_ERROR](stateCopy); - - expect(stateCopy.items).toEqual([]); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/utils_spec.js b/spec/javascripts/frequent_items/utils_spec.js deleted file mode 100644 index 2939b46bc31..00000000000 --- a/spec/javascripts/frequent_items/utils_spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; -import { - isMobile, - getTopFrequentItems, - updateExistingFrequentItem, - sanitizeItem, -} from '~/frequent_items/utils'; -import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants'; -import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data'; - -describe('Frequent Items utils spec', () => { - describe('isMobile', () => { - it('returns true when the screen is medium ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('md'); - - expect(isMobile()).toBe(true); - }); - - it('returns true when the screen is small ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('sm'); - - expect(isMobile()).toBe(true); - }); - - it('returns true when the screen is extra-small ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xs'); - - expect(isMobile()).toBe(true); - }); - - it('returns false when the screen is larger than medium ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('lg'); - - expect(isMobile()).toBe(false); - }); - }); - - describe('getTopFrequentItems', () => { - it('returns empty array if no items provided', () => { - const result = getTopFrequentItems(); - - expect(result.length).toBe(0); - }); - - it('returns correct amount of items for mobile', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('md'); - const result = getTopFrequentItems(unsortedFrequentItems); - - expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_MOBILE); - }); - - it('returns correct amount of items for desktop', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xl'); - const result = getTopFrequentItems(unsortedFrequentItems); - - expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_DESKTOP); - }); - - it('sorts frequent items in order of frequency and lastAccessedOn', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xl'); - const result = getTopFrequentItems(unsortedFrequentItems); - const expectedResult = sortedFrequentItems.slice(0, FREQUENT_ITEMS.LIST_COUNT_DESKTOP); - - expect(result).toEqual(expectedResult); - }); - }); - - describe('updateExistingFrequentItem', () => { - let mockedProject; - - beforeEach(() => { - mockedProject = { - ...mockProject, - frequency: 1, - lastAccessedOn: 1497979281815, - }; - }); - - it('updates item if accessed over an hour ago', () => { - const newTimestamp = Date.now() + HOUR_IN_MS + 1; - const newItem = { - ...mockedProject, - lastAccessedOn: newTimestamp, - }; - const result = updateExistingFrequentItem(mockedProject, newItem); - - expect(result.frequency).toBe(mockedProject.frequency + 1); - }); - - it('does not update item if accessed within the hour', () => { - const newItem = { - ...mockedProject, - lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS, - }; - const result = updateExistingFrequentItem(mockedProject, newItem); - - expect(result.frequency).toBe(mockedProject.frequency); - }); - }); - - describe('sanitizeItem', () => { - it('strips HTML tags for name and namespace', () => { - const input = { - name: '<br><b>test</b>', - namespace: '<br>test', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 }); - }); - - it("skips `name` key if it doesn't exist on the item", () => { - const input = { - namespace: '<br>test', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ namespace: 'test', id: 1 }); - }); - - it("skips `namespace` key if it doesn't exist on the item", () => { - const input = { - name: '<br><b>test</b>', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ name: 'test', id: 1 }); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/csrf_token_spec.js b/spec/javascripts/lib/utils/csrf_token_spec.js deleted file mode 100644 index 867bee34ee5..00000000000 --- a/spec/javascripts/lib/utils/csrf_token_spec.js +++ /dev/null @@ -1,50 +0,0 @@ -import csrf from '~/lib/utils/csrf'; - -describe('csrf', function() { - beforeEach(() => { - this.tokenKey = 'X-CSRF-Token'; - this.token = - 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ=='; - }); - - it('returns the correct headerKey', () => { - expect(csrf.headerKey).toBe(this.tokenKey); - }); - - describe('when csrf token is in the DOM', () => { - beforeEach(() => { - setFixtures(` - <meta name="csrf-token" content="${this.token}"> - `); - - csrf.init(); - }); - - it('returns the csrf token', () => { - expect(csrf.token).toBe(this.token); - }); - - it('returns the csrf headers object', () => { - expect(csrf.headers[this.tokenKey]).toBe(this.token); - }); - }); - - describe('when csrf token is not in the DOM', () => { - beforeEach(() => { - setFixtures(` - <meta name="some-other-token"> - `); - - csrf.init(); - }); - - it('returns null for token', () => { - expect(csrf.token).toBeNull(); - }); - - it('returns empty object for headers', () => { - expect(typeof csrf.headers).toBe('object'); - expect(Object.keys(csrf.headers).length).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/navigation_utility_spec.js b/spec/javascripts/lib/utils/navigation_utility_spec.js deleted file mode 100644 index be620e4a27c..00000000000 --- a/spec/javascripts/lib/utils/navigation_utility_spec.js +++ /dev/null @@ -1,23 +0,0 @@ -import findAndFollowLink from '~/lib/utils/navigation_utility'; - -describe('findAndFollowLink', () => { - it('visits a link when the selector exists', () => { - const href = '/some/path'; - const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl'); - - setFixtures(`<a class="my-shortcut" href="${href}">link</a>`); - - findAndFollowLink('.my-shortcut'); - - expect(visitUrl).toHaveBeenCalledWith(href); - }); - - it('does not throw an exception when the selector does not exist', () => { - const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl'); - - // this should not throw an exception - findAndFollowLink('.this-selector-does-not-exist'); - - expect(visitUrl).not.toHaveBeenCalled(); - }); -}); diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js deleted file mode 100644 index 138041a349f..00000000000 --- a/spec/javascripts/lib/utils/poll_spec.js +++ /dev/null @@ -1,222 +0,0 @@ -/* eslint-disable jasmine/no-unsafe-spy */ - -import Poll from '~/lib/utils/poll'; -import { successCodes } from '~/lib/utils/http_status'; - -const waitForAllCallsToFinish = (service, waitForCount, successCallback) => { - const timer = () => { - setTimeout(() => { - if (service.fetch.calls.count() === waitForCount) { - successCallback(); - } else { - timer(); - } - }, 0); - }; - - timer(); -}; - -function mockServiceCall(service, response, shouldFail = false) { - const action = shouldFail ? Promise.reject : Promise.resolve; - const responseObject = response; - - if (!responseObject.headers) responseObject.headers = {}; - - service.fetch.and.callFake(action.bind(Promise, responseObject)); -} - -describe('Poll', () => { - const service = jasmine.createSpyObj('service', ['fetch']); - const callbacks = jasmine.createSpyObj('callbacks', ['success', 'error', 'notification']); - - function setup() { - return new Poll({ - resource: service, - method: 'fetch', - successCallback: callbacks.success, - errorCallback: callbacks.error, - notificationCallback: callbacks.notification, - }).makeRequest(); - } - - afterEach(() => { - callbacks.success.calls.reset(); - callbacks.error.calls.reset(); - callbacks.notification.calls.reset(); - service.fetch.calls.reset(); - }); - - it('calls the success callback when no header for interval is provided', done => { - mockServiceCall(service, { status: 200 }); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }); - }); - - it('calls the error callback when the http request returns an error', done => { - mockServiceCall(service, { status: 500 }, true); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).not.toHaveBeenCalled(); - expect(callbacks.error).toHaveBeenCalled(); - - done(); - }); - }); - - it('skips the error callback when request is aborted', done => { - mockServiceCall(service, { status: 0 }, true); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).not.toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - expect(callbacks.notification).toHaveBeenCalled(); - - done(); - }); - }); - - it('should call the success callback when the interval header is -1', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } }); - setup() - .then(() => { - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); - }); - - describe('for 2xx status code', () => { - successCodes.forEach(httpCode => { - it(`starts polling when http status is ${httpCode} and interval header is provided`, done => { - mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: callbacks.success, - errorCallback: callbacks.error, - }); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 2, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(2); - expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }); - }); - }); - }); - - describe('stop', () => { - it('stops polling when method is called', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => { - Polling.stop(); - }, - errorCallback: callbacks.error, - }); - - spyOn(Polling, 'stop').and.callThrough(); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 1, () => { - expect(service.fetch.calls.count()).toEqual(1); - expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); - expect(Polling.stop).toHaveBeenCalled(); - - done(); - }); - }); - }); - - describe('enable', () => { - it('should enable polling upon a response', done => { - jasmine.clock().install(); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => {}, - }); - - Polling.enable({ - data: { page: 4 }, - response: { status: 200, headers: { 'poll-interval': 1 } }, - }); - - jasmine.clock().tick(1); - jasmine.clock().uninstall(); - - waitForAllCallsToFinish(service, 1, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(1); - expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); - expect(Polling.options.data).toEqual({ page: 4 }); - done(); - }); - }); - }); - - describe('restart', () => { - it('should restart polling when its called', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => { - Polling.stop(); - setTimeout(() => { - Polling.restart({ data: { page: 4 } }); - }, 0); - }, - errorCallback: callbacks.error, - }); - - spyOn(Polling, 'stop').and.callThrough(); - spyOn(Polling, 'enable').and.callThrough(); - spyOn(Polling, 'restart').and.callThrough(); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 2, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(2); - expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); - expect(Polling.stop).toHaveBeenCalled(); - expect(Polling.enable).toHaveBeenCalled(); - expect(Polling.restart).toHaveBeenCalled(); - expect(Polling.options.data).toEqual({ page: 4 }); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js deleted file mode 100644 index 1b1e7da1ed3..00000000000 --- a/spec/javascripts/lib/utils/sticky_spec.js +++ /dev/null @@ -1,66 +0,0 @@ -import { isSticky } from '~/lib/utils/sticky'; - -describe('sticky', () => { - let el; - - beforeEach(() => { - document.body.innerHTML += ` - <div class="parent"> - <div id="js-sticky"></div> - </div> - `; - - el = document.getElementById('js-sticky'); - }); - - afterEach(() => { - el.parentNode.remove(); - }); - - describe('when stuck', () => { - it('does not remove is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, el.offsetTop); - - expect(el.classList.contains('is-stuck')).toBeTruthy(); - }); - - it('adds is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - - expect(el.classList.contains('is-stuck')).toBeTruthy(); - }); - - it('inserts placeholder element', () => { - isSticky(el, 0, el.offsetTop, true); - - expect(document.querySelector('.sticky-placeholder')).not.toBeNull(); - }); - }); - - describe('when not stuck', () => { - it('removes is-stuck class', () => { - spyOn(el.classList, 'remove').and.callThrough(); - - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, 0); - - expect(el.classList.remove).toHaveBeenCalledWith('is-stuck'); - - expect(el.classList.contains('is-stuck')).toBeFalsy(); - }); - - it('does not add is-stuck class', () => { - isSticky(el, 0, 0); - - expect(el.classList.contains('is-stuck')).toBeFalsy(); - }); - - it('removes placeholder', () => { - isSticky(el, 0, el.offsetTop, true); - isSticky(el, 0, 0, true); - - expect(document.querySelector('.sticky-placeholder')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js b/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js deleted file mode 100644 index d8bdf69dfee..00000000000 --- a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js +++ /dev/null @@ -1,88 +0,0 @@ -import { mount, createLocalVue } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; -import RelatedMergeRequests from '~/related_merge_requests/components/related_merge_requests.vue'; -import createStore from '~/related_merge_requests/store/index'; - -const FIXTURE_PATH = 'issues/related_merge_requests.json'; -const API_ENDPOINT = '/api/v4/projects/2/issues/33/related_merge_requests'; -const localVue = createLocalVue(); - -describe('RelatedMergeRequests', () => { - let wrapper; - let mock; - let mockData; - - beforeEach(done => { - loadFixtures(FIXTURE_PATH); - mockData = getJSONFixture(FIXTURE_PATH); - mock = new MockAdapter(axios); - mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 }); - - wrapper = mount(localVue.extend(RelatedMergeRequests), { - localVue, - store: createStore(), - propsData: { - endpoint: API_ENDPOINT, - projectNamespace: 'gitlab-org', - projectPath: 'gitlab-ce', - }, - }); - - setTimeout(done); - }); - - afterEach(() => { - wrapper.destroy(); - mock.restore(); - }); - - describe('methods', () => { - describe('getAssignees', () => { - const assignees = [{ name: 'foo' }, { name: 'bar' }]; - - describe('when there is assignees array', () => { - it('should return assignees array', () => { - const mr = { assignees }; - - expect(wrapper.vm.getAssignees(mr)).toEqual(assignees); - }); - }); - - it('should return an array with single assingee', () => { - const mr = { assignee: assignees[0] }; - - expect(wrapper.vm.getAssignees(mr)).toEqual([assignees[0]]); - }); - - it('should return empty array when assignee is not set', () => { - expect(wrapper.vm.getAssignees({})).toEqual([]); - expect(wrapper.vm.getAssignees({ assignee: null })).toEqual([]); - }); - }); - }); - - describe('template', () => { - it('should render related merge request items', () => { - expect(wrapper.find('.js-items-count').text()).toEqual('2'); - expect(wrapper.findAll(RelatedIssuableItem).length).toEqual(2); - - const props = wrapper - .findAll(RelatedIssuableItem) - .at(1) - .props(); - const data = mockData[1]; - - expect(props.idKey).toEqual(data.id); - expect(props.pathIdSeparator).toEqual('!'); - expect(props.pipelineStatus).toBe(data.head_pipeline.detailed_status); - expect(props.assignees).toEqual([data.assignee]); - expect(props.isMergeRequest).toBe(true); - expect(props.confidential).toEqual(false); - expect(props.title).toEqual(data.title); - expect(props.state).toEqual(data.state); - expect(props.createdAt).toEqual(data.created_at); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/store/actions_spec.js b/spec/javascripts/related_merge_requests/store/actions_spec.js deleted file mode 100644 index c4cd9f5f803..00000000000 --- a/spec/javascripts/related_merge_requests/store/actions_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import axios from '~/lib/utils/axios_utils'; -import * as types from '~/related_merge_requests/store/mutation_types'; -import actionsModule, * as actions from '~/related_merge_requests/store/actions'; - -describe('RelatedMergeRequest store actions', () => { - let state; - let flashSpy; - let mock; - - beforeEach(() => { - state = { - apiEndpoint: '/api/related_merge_requests', - }; - flashSpy = spyOnDependency(actionsModule, 'createFlash'); - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('setInitialState', () => { - it('commits types.SET_INITIAL_STATE with given props', done => { - const props = { a: 1, b: 2 }; - - testAction( - actions.setInitialState, - props, - {}, - [{ type: types.SET_INITIAL_STATE, payload: props }], - [], - done, - ); - }); - }); - - describe('requestData', () => { - it('commits types.REQUEST_DATA', done => { - testAction(actions.requestData, null, {}, [{ type: types.REQUEST_DATA }], [], done); - }); - }); - - describe('receiveDataSuccess', () => { - it('commits types.RECEIVE_DATA_SUCCESS with data', done => { - const data = { a: 1, b: 2 }; - - testAction( - actions.receiveDataSuccess, - data, - {}, - [{ type: types.RECEIVE_DATA_SUCCESS, payload: data }], - [], - done, - ); - }); - }); - - describe('receiveDataError', () => { - it('commits types.RECEIVE_DATA_ERROR', done => { - testAction( - actions.receiveDataError, - null, - {}, - [{ type: types.RECEIVE_DATA_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchMergeRequests', () => { - describe('for a successful request', () => { - it('should dispatch success action', done => { - const data = { a: 1 }; - mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(200, data, { 'x-total': 2 }); - - testAction( - actions.fetchMergeRequests, - null, - state, - [], - [{ type: 'requestData' }, { type: 'receiveDataSuccess', payload: { data, total: 2 } }], - done, - ); - }); - }); - - describe('for a failing request', () => { - it('should dispatch error action', done => { - mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(400); - - testAction( - actions.fetchMergeRequests, - null, - state, - [], - [{ type: 'requestData' }, { type: 'receiveDataError' }], - () => { - expect(flashSpy).toHaveBeenCalledTimes(1); - expect(flashSpy).toHaveBeenCalledWith(jasmine.stringMatching('Something went wrong')); - - done(); - }, - ); - }); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/store/mutations_spec.js b/spec/javascripts/related_merge_requests/store/mutations_spec.js deleted file mode 100644 index 21b6e26376b..00000000000 --- a/spec/javascripts/related_merge_requests/store/mutations_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import mutations from '~/related_merge_requests/store/mutations'; -import * as types from '~/related_merge_requests/store/mutation_types'; - -describe('RelatedMergeRequests Store Mutations', () => { - describe('SET_INITIAL_STATE', () => { - it('should set initial state according to given data', () => { - const apiEndpoint = '/api'; - const state = {}; - - mutations[types.SET_INITIAL_STATE](state, { apiEndpoint }); - - expect(state.apiEndpoint).toEqual(apiEndpoint); - }); - }); - - describe('REQUEST_DATA', () => { - it('should set loading flag', () => { - const state = {}; - - mutations[types.REQUEST_DATA](state); - - expect(state.isFetchingMergeRequests).toEqual(true); - }); - }); - - describe('RECEIVE_DATA_SUCCESS', () => { - it('should set loading flag and data', () => { - const state = {}; - const mrs = [1, 2, 3]; - - mutations[types.RECEIVE_DATA_SUCCESS](state, { data: mrs, total: mrs.length }); - - expect(state.isFetchingMergeRequests).toEqual(false); - expect(state.mergeRequests).toEqual(mrs); - expect(state.totalCount).toEqual(mrs.length); - }); - }); - - describe('RECEIVE_DATA_ERROR', () => { - it('should set loading and error flags', () => { - const state = {}; - - mutations[types.RECEIVE_DATA_ERROR](state); - - expect(state.isFetchingMergeRequests).toEqual(false); - expect(state.hasErrorFetchingMergeRequests).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js index c869ff96933..701a97ba5a6 100644 --- a/spec/javascripts/sidebar/mock_data.js +++ b/spec/javascripts/sidebar/mock_data.js @@ -2,6 +2,6 @@ // file this one re-exports from. For more detail about why, see: // https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 -import mockData from '../../../spec/frontend/sidebar/mock_data'; +import mockData from '../../frontend/sidebar/mock_data'; export default mockData; |