diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/frontend/helpers | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) | |
download | gitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/frontend/helpers')
-rw-r--r-- | spec/frontend/helpers/dom_shims/element_scroll_to.js | 6 | ||||
-rw-r--r-- | spec/frontend/helpers/dom_shims/image_element_properties.js | 2 | ||||
-rw-r--r-- | spec/frontend/helpers/dom_shims/index.js | 2 | ||||
-rw-r--r-- | spec/frontend/helpers/dom_shims/mutation_observer.js | 7 | ||||
-rw-r--r-- | spec/frontend/helpers/local_storage_helper.js | 20 | ||||
-rw-r--r-- | spec/frontend/helpers/local_storage_helper_spec.js | 21 | ||||
-rw-r--r-- | spec/frontend/helpers/mock_dom_observer.js | 94 | ||||
-rw-r--r-- | spec/frontend/helpers/mock_window_location_helper.js | 43 | ||||
-rw-r--r-- | spec/frontend/helpers/scroll_into_view_promise.js | 28 | ||||
-rw-r--r-- | spec/frontend/helpers/set_window_location_helper_spec.js | 2 | ||||
-rw-r--r-- | spec/frontend/helpers/vue_mock_directive.js | 17 | ||||
-rw-r--r-- | spec/frontend/helpers/wait_for_attribute_change.js | 16 |
12 files changed, 206 insertions, 52 deletions
diff --git a/spec/frontend/helpers/dom_shims/element_scroll_to.js b/spec/frontend/helpers/dom_shims/element_scroll_to.js new file mode 100644 index 00000000000..68f8a115865 --- /dev/null +++ b/spec/frontend/helpers/dom_shims/element_scroll_to.js @@ -0,0 +1,6 @@ +Element.prototype.scrollTo = jest.fn().mockImplementation(function scrollTo(x, y) { + this.scrollLeft = x; + this.scrollTop = y; + + this.dispatchEvent(new Event('scroll')); +}); diff --git a/spec/frontend/helpers/dom_shims/image_element_properties.js b/spec/frontend/helpers/dom_shims/image_element_properties.js index 525246e6ade..d94c157e44d 100644 --- a/spec/frontend/helpers/dom_shims/image_element_properties.js +++ b/spec/frontend/helpers/dom_shims/image_element_properties.js @@ -1,6 +1,6 @@ Object.defineProperty(global.HTMLImageElement.prototype, 'src', { get() { - return this.$_jest_src; + return this.$_jest_src || this.getAttribute('src'); }, set(val) { this.$_jest_src = val; diff --git a/spec/frontend/helpers/dom_shims/index.js b/spec/frontend/helpers/dom_shims/index.js index 17a2090d2f1..d18bb94c107 100644 --- a/spec/frontend/helpers/dom_shims/index.js +++ b/spec/frontend/helpers/dom_shims/index.js @@ -1,8 +1,10 @@ import './element_scroll_into_view'; import './element_scroll_by'; +import './element_scroll_to'; import './form_element'; import './get_client_rects'; import './inner_text'; +import './mutation_observer'; import './window_scroll_to'; import './scroll_by'; import './size_properties'; diff --git a/spec/frontend/helpers/dom_shims/mutation_observer.js b/spec/frontend/helpers/dom_shims/mutation_observer.js new file mode 100644 index 00000000000..68c494f19ea --- /dev/null +++ b/spec/frontend/helpers/dom_shims/mutation_observer.js @@ -0,0 +1,7 @@ +/* eslint-disable class-methods-use-this */ +class MutationObserverStub { + disconnect() {} + observe() {} +} + +global.MutationObserver = MutationObserverStub; diff --git a/spec/frontend/helpers/local_storage_helper.js b/spec/frontend/helpers/local_storage_helper.js index 48e66b11767..a66c31d1353 100644 --- a/spec/frontend/helpers/local_storage_helper.js +++ b/spec/frontend/helpers/local_storage_helper.js @@ -28,12 +28,20 @@ const useLocalStorage = fn => { /** * Create an object with the localStorage interface but `jest.fn()` implementations. */ -export const createLocalStorageSpy = () => ({ - clear: jest.fn(), - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), -}); +export const createLocalStorageSpy = () => { + let storage = {}; + + return { + clear: jest.fn(() => { + storage = {}; + }), + getItem: jest.fn(key => storage[key]), + setItem: jest.fn((key, value) => { + storage[key] = value; + }), + removeItem: jest.fn(key => delete storage[key]), + }; +}; /** * Before each test, overwrite `window.localStorage` with a spy implementation. diff --git a/spec/frontend/helpers/local_storage_helper_spec.js b/spec/frontend/helpers/local_storage_helper_spec.js new file mode 100644 index 00000000000..18aec0f329a --- /dev/null +++ b/spec/frontend/helpers/local_storage_helper_spec.js @@ -0,0 +1,21 @@ +import { useLocalStorageSpy } from './local_storage_helper'; + +useLocalStorageSpy(); + +describe('localStorage helper', () => { + it('mocks localStorage but works exactly like original localStorage', () => { + localStorage.setItem('test', 'testing'); + localStorage.setItem('test2', 'testing'); + + expect(localStorage.getItem('test')).toBe('testing'); + + localStorage.removeItem('test', 'testing'); + + expect(localStorage.getItem('test')).toBeUndefined(); + expect(localStorage.getItem('test2')).toBe('testing'); + + localStorage.clear(); + + expect(localStorage.getItem('test2')).toBeUndefined(); + }); +}); diff --git a/spec/frontend/helpers/mock_dom_observer.js b/spec/frontend/helpers/mock_dom_observer.js new file mode 100644 index 00000000000..7aac51f6264 --- /dev/null +++ b/spec/frontend/helpers/mock_dom_observer.js @@ -0,0 +1,94 @@ +/* eslint-disable class-methods-use-this, max-classes-per-file */ +import { isMatch } from 'lodash'; + +/** + * This class gives us a JSDom friendly DOM observer which we can manually trigger in tests + * + * Use this in place of MutationObserver or IntersectionObserver + */ +class MockObserver { + constructor(cb) { + this.$_cb = cb; + this.$_observers = []; + } + + observe(node, options = {}) { + this.$_observers.push([node, options]); + } + + disconnect() { + this.$_observers = []; + } + + takeRecords() {} + + // eslint-disable-next-line babel/camelcase + $_triggerObserve(node, { entry = {}, options = {} } = {}) { + if (this.$_hasObserver(node, options)) { + this.$_cb([{ target: node, ...entry }]); + } + } + + // eslint-disable-next-line babel/camelcase + $_hasObserver(node, options = {}) { + return this.$_observers.some( + ([obvNode, obvOptions]) => node === obvNode && isMatch(options, obvOptions), + ); + } +} + +class MockIntersectionObserver extends MockObserver { + unobserve(node) { + this.$_observers = this.$_observers.filter(([obvNode]) => node === obvNode); + } +} + +/** + * Use this function to setup a mock observer instance in place of the given DOM Observer + * + * Example: + * ``` + * describe('', () => { + * const { trigger: triggerMutate } = useMockMutationObserver(); + * + * it('test', () => { + * trigger(el, { options: { childList: true }, entry: { } }); + * }); + * }) + * ``` + * + * @param {String} key + */ +const useMockObserver = (key, createMock) => { + let mockObserver; + let origObserver; + + beforeEach(() => { + origObserver = global[key]; + global[key] = jest.fn().mockImplementation((...args) => { + mockObserver = createMock(...args); + return mockObserver; + }); + }); + + afterEach(() => { + mockObserver = null; + global[key] = origObserver; + }); + + const trigger = (...args) => { + if (!mockObserver) { + return; + } + + mockObserver.$_triggerObserve(...args); + }; + + return { trigger }; +}; + +export const useMockIntersectionObserver = () => + useMockObserver('IntersectionObserver', (...args) => new MockIntersectionObserver(...args)); + +export const useMockMutationObserver = () => + useMockObserver('MutationObserver', (...args) => new MockObserver(...args)); diff --git a/spec/frontend/helpers/mock_window_location_helper.js b/spec/frontend/helpers/mock_window_location_helper.js new file mode 100644 index 00000000000..175044d1fce --- /dev/null +++ b/spec/frontend/helpers/mock_window_location_helper.js @@ -0,0 +1,43 @@ +/** + * Manage the instance of a custom `window.location` + * + * This only encapsulates the setup / teardown logic so that it can easily be + * reused with different implementations (i.e. a spy or a [fake][1]) + * + * [1]: https://stackoverflow.com/a/41434763/1708147 + * + * @param {() => any} fn Function that returns the object to use for window.location + */ +const useMockLocation = fn => { + const origWindowLocation = window.location; + let currentWindowLocation; + + Object.defineProperty(window, 'location', { + get: () => currentWindowLocation, + }); + + beforeEach(() => { + currentWindowLocation = fn(); + }); + + afterEach(() => { + currentWindowLocation = origWindowLocation; + }); +}; + +/** + * Create an object with the location interface but `jest.fn()` implementations. + */ +export const createWindowLocationSpy = () => { + return { + assign: jest.fn(), + reload: jest.fn(), + replace: jest.fn(), + toString: jest.fn(), + }; +}; + +/** + * Before each test, overwrite `window.location` with a spy implementation. + */ +export const useMockLocationHelper = () => useMockLocation(createWindowLocationSpy); diff --git a/spec/frontend/helpers/scroll_into_view_promise.js b/spec/frontend/helpers/scroll_into_view_promise.js deleted file mode 100644 index 0edea2103da..00000000000 --- a/spec/frontend/helpers/scroll_into_view_promise.js +++ /dev/null @@ -1,28 +0,0 @@ -export default function scrollIntoViewPromise(intersectionTarget, timeout = 100, maxTries = 5) { - return new Promise((resolve, reject) => { - let intersectionObserver; - let retry = 0; - - const intervalId = setInterval(() => { - if (retry >= maxTries) { - intersectionObserver.disconnect(); - clearInterval(intervalId); - reject(new Error(`Could not scroll target into viewPort within ${timeout * maxTries} ms`)); - } - retry += 1; - intersectionTarget.scrollIntoView(); - }, timeout); - - intersectionObserver = new IntersectionObserver(entries => { - if (entries[0].isIntersecting) { - intersectionObserver.disconnect(); - clearInterval(intervalId); - resolve(); - } - }); - - intersectionObserver.observe(intersectionTarget); - - intersectionTarget.scrollIntoView(); - }); -} diff --git a/spec/frontend/helpers/set_window_location_helper_spec.js b/spec/frontend/helpers/set_window_location_helper_spec.js index 2a2c024c824..da609b6bbf0 100644 --- a/spec/frontend/helpers/set_window_location_helper_spec.js +++ b/spec/frontend/helpers/set_window_location_helper_spec.js @@ -33,7 +33,7 @@ describe('setWindowLocation', () => { it.each([null, 1, undefined, false, '', 'gitlab.com'])( 'throws an error when called with an invalid url: "%s"', invalidUrl => { - expect(() => setWindowLocation(invalidUrl)).toThrow(new TypeError('Invalid URL')); + expect(() => setWindowLocation(invalidUrl)).toThrow(/Invalid URL/); expect(window.location).toBe(originalLocation); }, ); diff --git a/spec/frontend/helpers/vue_mock_directive.js b/spec/frontend/helpers/vue_mock_directive.js new file mode 100644 index 00000000000..699fe3eab26 --- /dev/null +++ b/spec/frontend/helpers/vue_mock_directive.js @@ -0,0 +1,17 @@ +export const getKey = name => `$_gl_jest_${name}`; + +export const getBinding = (el, name) => el[getKey(name)]; + +export const createMockDirective = () => ({ + bind(el, { name, value, arg, modifiers }) { + el[getKey(name)] = { + value, + arg, + modifiers, + }; + }, + + unbind(el, { name }) { + delete el[getKey(name)]; + }, +}); diff --git a/spec/frontend/helpers/wait_for_attribute_change.js b/spec/frontend/helpers/wait_for_attribute_change.js deleted file mode 100644 index 8f22d569222..00000000000 --- a/spec/frontend/helpers/wait_for_attribute_change.js +++ /dev/null @@ -1,16 +0,0 @@ -export default (domElement, attributes, timeout = 1500) => - new Promise((resolve, reject) => { - let observer; - const timeoutId = setTimeout(() => { - observer.disconnect(); - reject(new Error(`Could not see an attribute update within ${timeout} ms`)); - }, timeout); - - observer = new MutationObserver(() => { - clearTimeout(timeoutId); - observer.disconnect(); - resolve(); - }); - - observer.observe(domElement, { attributes: true, attributeFilter: attributes }); - }); |