diff options
author | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-05-05 17:59:41 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-05 17:59:41 +0000 |
commit | bef42d9a36bf465878e0edc19c9e154f73f12b13 (patch) | |
tree | 0609f74a8e1824c2450c26dcffe469b2d9e5e92e /spec | |
parent | b81535351023a3b19870ce052d8c3d0563391686 (diff) | |
download | gitlab-ce-bef42d9a36bf465878e0edc19c9e154f73f12b13.tar.gz |
Fallback localstorage cases
Diffstat (limited to 'spec')
9 files changed, 544 insertions, 3 deletions
diff --git a/spec/javascripts/autosave_spec.js b/spec/javascripts/autosave_spec.js new file mode 100644 index 00000000000..9f9acc392c2 --- /dev/null +++ b/spec/javascripts/autosave_spec.js @@ -0,0 +1,134 @@ +import Autosave from '~/autosave'; +import AccessorUtilities from '~/lib/utils/accessor'; + +describe('Autosave', () => { + let autosave; + + describe('class constructor', () => { + const key = 'key'; + const field = jasmine.createSpyObj('field', ['data', 'on']); + + beforeEach(() => { + spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true); + spyOn(Autosave.prototype, 'restore'); + + autosave = new Autosave(field, key); + }); + + it('should set .isLocalStorageAvailable', () => { + expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); + expect(autosave.isLocalStorageAvailable).toBe(true); + }); + }); + + describe('restore', () => { + const key = 'key'; + const field = jasmine.createSpyObj('field', ['trigger']); + + beforeEach(() => { + autosave = { + field, + key, + }; + + spyOn(window.localStorage, 'getItem'); + }); + + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = false; + + Autosave.prototype.restore.call(autosave); + }); + + it('should not call .getItem', () => { + expect(window.localStorage.getItem).not.toHaveBeenCalled(); + }); + }); + + describe('if .isLocalStorageAvailable is `true`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = true; + + Autosave.prototype.restore.call(autosave); + }); + + it('should call .getItem', () => { + expect(window.localStorage.getItem).toHaveBeenCalledWith(key); + }); + }); + }); + + describe('save', () => { + const field = jasmine.createSpyObj('field', ['val']); + + beforeEach(() => { + autosave = jasmine.createSpyObj('autosave', ['reset']); + autosave.field = field; + + field.val.and.returnValue('value'); + + spyOn(window.localStorage, 'setItem'); + }); + + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = false; + + Autosave.prototype.save.call(autosave); + }); + + it('should not call .setItem', () => { + expect(window.localStorage.setItem).not.toHaveBeenCalled(); + }); + }); + + describe('if .isLocalStorageAvailable is `true`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = true; + + Autosave.prototype.save.call(autosave); + }); + + it('should call .setItem', () => { + expect(window.localStorage.setItem).toHaveBeenCalled(); + }); + }); + }); + + describe('reset', () => { + const key = 'key'; + + beforeEach(() => { + autosave = { + key, + }; + + spyOn(window.localStorage, 'removeItem'); + }); + + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = false; + + Autosave.prototype.reset.call(autosave); + }); + + it('should not call .removeItem', () => { + expect(window.localStorage.removeItem).not.toHaveBeenCalled(); + }); + }); + + describe('if .isLocalStorageAvailable is `true`', () => { + beforeEach(() => { + autosave.isLocalStorageAvailable = true; + + Autosave.prototype.reset.call(autosave); + }); + + it('should call .removeItem', () => { + expect(window.localStorage.removeItem).toHaveBeenCalledWith(key); + }); + }); + }); +}); diff --git a/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js new file mode 100644 index 00000000000..1ed96a67478 --- /dev/null +++ b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js @@ -0,0 +1,47 @@ +import { getUnicodeSupportMap } from '~/behaviors/gl_emoji/unicode_support_map'; +import AccessorUtilities from '~/lib/utils/accessor'; + +describe('Unicode Support Map', () => { + describe('getUnicodeSupportMap', () => { + const stringSupportMap = 'stringSupportMap'; + + beforeEach(() => { + spyOn(AccessorUtilities, 'isLocalStorageAccessSafe'); + spyOn(window.localStorage, 'getItem'); + spyOn(window.localStorage, 'setItem'); + spyOn(JSON, 'parse'); + spyOn(JSON, 'stringify').and.returnValue(stringSupportMap); + }); + + describe('if isLocalStorageAvailable is `true`', function () { + beforeEach(() => { + AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(true); + + getUnicodeSupportMap(); + }); + + it('should call .getItem and .setItem', () => { + const allArgs = window.localStorage.setItem.calls.allArgs(); + + expect(window.localStorage.getItem).toHaveBeenCalledWith('gl-emoji-user-agent'); + expect(allArgs[0][0]).toBe('gl-emoji-user-agent'); + expect(allArgs[0][1]).toBe(navigator.userAgent); + expect(allArgs[1][0]).toBe('gl-emoji-unicode-support-map'); + expect(allArgs[1][1]).toBe(stringSupportMap); + }); + }); + + describe('if isLocalStorageAvailable is `false`', function () { + beforeEach(() => { + AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(false); + + getUnicodeSupportMap(); + }); + + it('should not call .getItem or .setItem', () => { + expect(window.localStorage.getItem.calls.count()).toBe(1); + expect(window.localStorage.setItem).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 2722882375f..d0f09a561d5 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -76,6 +76,26 @@ describe('RecentSearchesDropdownContent', () => { }); }); + describe('if isLocalStorageAvailable is `false`', () => { + let el; + + beforeEach(() => { + const props = Object.assign({ isLocalStorageAvailable: false }, propsDataWithItems); + + vm = createComponent(props); + el = vm.$el; + }); + + it('should render an info note', () => { + const note = el.querySelector('.dropdown-info-note'); + const items = el.querySelectorAll('.filtered-search-history-dropdown-item'); + + expect(note).toBeDefined(); + expect(note.innerText.trim()).toBe('This feature requires local storage to be enabled'); + expect(items.length).toEqual(propsDataWithoutItems.items.length); + }); + }); + describe('computed', () => { describe('processedItems', () => { it('with items', () => { diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js index e747aa497c2..063d547d00c 100644 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js @@ -1,3 +1,7 @@ +import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searches_store'; +import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; +import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; + require('~/lib/utils/url_utility'); require('~/lib/utils/common_utils'); require('~/filtered_search/filtered_search_token_keys'); @@ -60,6 +64,36 @@ describe('Filtered Search Manager', () => { manager.cleanup(); }); + describe('class constructor', () => { + const isLocalStorageAvailable = 'isLocalStorageAvailable'; + let filteredSearchManager; + + beforeEach(() => { + spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable); + spyOn(recentSearchesStoreSrc, 'default'); + + filteredSearchManager = new gl.FilteredSearchManager(); + + return filteredSearchManager; + }); + + it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => { + expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); + expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({ + isLocalStorageAvailable, + }); + }); + + it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => { + spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError())); + spyOn(window, 'Flash'); + + filteredSearchManager = new gl.FilteredSearchManager(); + + expect(window.Flash).not.toHaveBeenCalled(); + }); + }); + describe('search', () => { const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened'; diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js new file mode 100644 index 00000000000..d8ba6de5f45 --- /dev/null +++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js @@ -0,0 +1,31 @@ +import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; +import * as vueSrc from 'vue'; + +describe('RecentSearchesRoot', () => { + describe('render', () => { + let recentSearchesRoot; + let data; + let template; + + beforeEach(() => { + recentSearchesRoot = { + store: { + state: 'state', + }, + }; + + spyOn(vueSrc, 'default').and.callFake((options) => { + data = options.data; + template = options.template; + }); + + RecentSearchesRoot.prototype.render.call(recentSearchesRoot); + }); + + it('should instantiate Vue', () => { + expect(vueSrc.default).toHaveBeenCalled(); + expect(data()).toBe(recentSearchesRoot.store.state); + expect(template).toContain(':is-local-storage-available="isLocalStorageAvailable"'); + }); + }); +}); diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js new file mode 100644 index 00000000000..ea7c146fa4f --- /dev/null +++ b/spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js @@ -0,0 +1,18 @@ +import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; + +describe('RecentSearchesServiceError', () => { + let recentSearchesServiceError; + + beforeEach(() => { + recentSearchesServiceError = new RecentSearchesServiceError(); + }); + + it('instantiates an instance of RecentSearchesServiceError and not an Error', () => { + expect(recentSearchesServiceError).toEqual(jasmine.any(RecentSearchesServiceError)); + expect(recentSearchesServiceError.name).toBe('RecentSearchesServiceError'); + }); + + it('should set a default message', () => { + expect(recentSearchesServiceError.message).toBe('Recent Searches Service is unavailable'); + }); +}); diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js index c255bf7c939..31fa478804a 100644 --- a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js +++ b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js @@ -1,6 +1,7 @@ /* eslint-disable promise/catch-or-return */ import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; +import AccessorUtilities from '~/lib/utils/accessor'; describe('RecentSearchesService', () => { let service; @@ -11,6 +12,10 @@ describe('RecentSearchesService', () => { }); describe('fetch', () => { + beforeEach(() => { + spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true); + }); + it('should default to empty array', (done) => { const fetchItemsPromise = service.fetch(); @@ -29,11 +34,21 @@ describe('RecentSearchesService', () => { const fetchItemsPromise = service.fetch(); fetchItemsPromise - .catch(() => { + .catch((error) => { + expect(error).toEqual(jasmine.any(SyntaxError)); done(); }); }); + it('should reject when service is unavailable', (done) => { + RecentSearchesService.isAvailable.and.returnValue(false); + + service.fetch().catch((error) => { + expect(error).toEqual(jasmine.any(Error)); + done(); + }); + }); + it('should return items from localStorage', (done) => { window.localStorage.setItem(service.localStorageKey, '["foo", "bar"]'); const fetchItemsPromise = service.fetch(); @@ -44,15 +59,89 @@ describe('RecentSearchesService', () => { done(); }); }); + + describe('if .isAvailable returns `false`', () => { + beforeEach(() => { + RecentSearchesService.isAvailable.and.returnValue(false); + + spyOn(window.localStorage, 'getItem'); + + RecentSearchesService.prototype.fetch(); + }); + + it('should not call .getItem', () => { + expect(window.localStorage.getItem).not.toHaveBeenCalled(); + }); + }); }); describe('setRecentSearches', () => { + beforeEach(() => { + spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true); + }); + it('should save things in localStorage', () => { const items = ['foo', 'bar']; service.save(items); - const newLocalStorageValue = - window.localStorage.getItem(service.localStorageKey); + const newLocalStorageValue = window.localStorage.getItem(service.localStorageKey); expect(JSON.parse(newLocalStorageValue)).toEqual(items); }); }); + + describe('save', () => { + beforeEach(() => { + spyOn(window.localStorage, 'setItem'); + spyOn(RecentSearchesService, 'isAvailable'); + }); + + describe('if .isAvailable returns `true`', () => { + const searchesString = 'searchesString'; + const localStorageKey = 'localStorageKey'; + const recentSearchesService = { + localStorageKey, + }; + + beforeEach(() => { + RecentSearchesService.isAvailable.and.returnValue(true); + + spyOn(JSON, 'stringify').and.returnValue(searchesString); + + RecentSearchesService.prototype.save.call(recentSearchesService); + }); + + it('should call .setItem', () => { + expect(window.localStorage.setItem).toHaveBeenCalledWith(localStorageKey, searchesString); + }); + }); + + describe('if .isAvailable returns `false`', () => { + beforeEach(() => { + RecentSearchesService.isAvailable.and.returnValue(false); + + RecentSearchesService.prototype.save(); + }); + + it('should not call .setItem', () => { + expect(window.localStorage.setItem).not.toHaveBeenCalled(); + }); + }); + }); + + describe('isAvailable', () => { + let isAvailable; + + beforeEach(() => { + spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.callThrough(); + + isAvailable = RecentSearchesService.isAvailable(); + }); + + it('should call .isLocalStorageAccessSafe', () => { + expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); + }); + + it('should return a boolean', () => { + expect(typeof isAvailable).toBe('boolean'); + }); + }); }); diff --git a/spec/javascripts/lib/utils/accessor_spec.js b/spec/javascripts/lib/utils/accessor_spec.js new file mode 100644 index 00000000000..b768d6f2a68 --- /dev/null +++ b/spec/javascripts/lib/utils/accessor_spec.js @@ -0,0 +1,78 @@ +import AccessorUtilities from '~/lib/utils/accessor'; + +describe('AccessorUtilities', () => { + const testError = new Error('test error'); + + describe('isPropertyAccessSafe', () => { + let base; + + it('should return `true` if access is safe', () => { + base = { testProp: 'testProp' }; + + expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(true); + }); + + it('should return `false` if access throws an error', () => { + base = { get testProp() { throw testError; } }; + + expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(false); + }); + + it('should return `false` if property is undefined', () => { + base = {}; + + expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(false); + }); + }); + + describe('isFunctionCallSafe', () => { + const base = {}; + + it('should return `true` if calling is safe', () => { + base.func = () => {}; + + expect(AccessorUtilities.isFunctionCallSafe(base, 'func')).toBe(true); + }); + + it('should return `false` if calling throws an error', () => { + base.func = () => { throw new Error('test error'); }; + + expect(AccessorUtilities.isFunctionCallSafe(base, 'func')).toBe(false); + }); + + it('should return `false` if function is undefined', () => { + base.func = undefined; + + expect(AccessorUtilities.isFunctionCallSafe(base, 'func')).toBe(false); + }); + }); + + describe('isLocalStorageAccessSafe', () => { + beforeEach(() => { + spyOn(window.localStorage, 'setItem'); + spyOn(window.localStorage, 'removeItem'); + }); + + it('should return `true` if access is safe', () => { + expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(true); + }); + + it('should return `false` if access to .setItem isnt safe', () => { + window.localStorage.setItem.and.callFake(() => { throw testError; }); + + expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(false); + }); + + it('should set a test item if access is safe', () => { + AccessorUtilities.isLocalStorageAccessSafe(); + + expect(window.localStorage.setItem).toHaveBeenCalledWith('isLocalStorageAccessSafe', 'true'); + }); + + it('should remove the test item if access is safe', () => { + AccessorUtilities.isLocalStorageAccessSafe(); + + expect(window.localStorage.removeItem).toHaveBeenCalledWith('isLocalStorageAccessSafe'); + }); + }); +}); diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js index d83d9a57b42..5b4f5933b34 100644 --- a/spec/javascripts/signin_tabs_memoizer_spec.js +++ b/spec/javascripts/signin_tabs_memoizer_spec.js @@ -1,3 +1,5 @@ +import AccessorUtilities from '~/lib/utils/accessor'; + require('~/signin_tabs_memoizer'); ((global) => { @@ -19,6 +21,8 @@ require('~/signin_tabs_memoizer'); beforeEach(() => { loadFixtures(fixtureTemplate); + + spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true); }); it('does nothing if no tab was previously selected', () => { @@ -49,5 +53,91 @@ require('~/signin_tabs_memoizer'); expect(memo.readData()).toEqual('#standard'); }); + + describe('class constructor', () => { + beforeEach(() => { + memo = createMemoizer(); + }); + + it('should set .isLocalStorageAvailable', () => { + expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); + expect(memo.isLocalStorageAvailable).toBe(true); + }); + }); + + describe('saveData', () => { + beforeEach(() => { + memo = { + currentTabKey, + }; + + spyOn(localStorage, 'setItem'); + }); + + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(function () { + memo.isLocalStorageAvailable = false; + + global.ActiveTabMemoizer.prototype.saveData.call(memo); + }); + + it('should not call .setItem', () => { + expect(localStorage.setItem).not.toHaveBeenCalled(); + }); + }); + + describe('if .isLocalStorageAvailable is `true`', () => { + const value = 'value'; + + beforeEach(function () { + memo.isLocalStorageAvailable = true; + + global.ActiveTabMemoizer.prototype.saveData.call(memo, value); + }); + + it('should call .setItem', () => { + expect(localStorage.setItem).toHaveBeenCalledWith(currentTabKey, value); + }); + }); + }); + + describe('readData', () => { + const itemValue = 'itemValue'; + let readData; + + beforeEach(() => { + memo = { + currentTabKey, + }; + + spyOn(localStorage, 'getItem').and.returnValue(itemValue); + }); + + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(function () { + memo.isLocalStorageAvailable = false; + + readData = global.ActiveTabMemoizer.prototype.readData.call(memo); + }); + + it('should not call .getItem and should return `null`', () => { + expect(localStorage.getItem).not.toHaveBeenCalled(); + expect(readData).toBe(null); + }); + }); + + describe('if .isLocalStorageAvailable is `true`', () => { + beforeEach(function () { + memo.isLocalStorageAvailable = true; + + readData = global.ActiveTabMemoizer.prototype.readData.call(memo); + }); + + it('should call .getItem and return the localStorage value', () => { + expect(window.localStorage.getItem).toHaveBeenCalledWith(currentTabKey); + expect(readData).toBe(itemValue); + }); + }); + }); }); })(window); |