diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
commit | b76ae638462ab0f673e5915986070518dd3f9ad3 (patch) | |
tree | bdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/frontend/__helpers__ | |
parent | 434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff) |
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/frontend/__helpers__')
4 files changed, 182 insertions, 78 deletions
diff --git a/spec/frontend/__helpers__/mock_dom_observer.js b/spec/frontend/__helpers__/mock_dom_observer.js index 1b93b81535d..dd26b594ad9 100644 --- a/spec/frontend/__helpers__/mock_dom_observer.js +++ b/spec/frontend/__helpers__/mock_dom_observer.js @@ -52,7 +52,7 @@ class MockIntersectionObserver extends MockObserver { * const { trigger: triggerMutate } = useMockMutationObserver(); * * it('test', () => { - * trigger(el, { options: { childList: true }, entry: { } }); + * triggerMutate(el, { options: { childList: true }, entry: { } }); * }); * }) * ``` @@ -60,33 +60,31 @@ class MockIntersectionObserver extends MockObserver { * @param {String} key */ const useMockObserver = (key, createMock) => { - let mockObserver; + let mockObservers = []; let origObserver; beforeEach(() => { origObserver = global[key]; global[key] = jest.fn().mockImplementation((...args) => { - mockObserver = createMock(...args); + const mockObserver = createMock(...args); + mockObservers.push(mockObserver); return mockObserver; }); }); afterEach(() => { - mockObserver = null; + mockObservers.forEach((x) => x.disconnect()); + mockObservers = []; global[key] = origObserver; }); const trigger = (...args) => { - if (!mockObserver) { - return; - } - - mockObserver.$_triggerObserve(...args); + mockObservers.forEach((observer) => { + observer.$_triggerObserve(...args); + }); }; - const observersCount = () => mockObserver.$_observers.length; - - return { trigger, observersCount }; + return { trigger }; }; export const useMockIntersectionObserver = () => diff --git a/spec/frontend/__helpers__/mock_window_location_helper.js b/spec/frontend/__helpers__/mock_window_location_helper.js index 08a28fbbbd6..3755778e5c1 100644 --- a/spec/frontend/__helpers__/mock_window_location_helper.js +++ b/spec/frontend/__helpers__/mock_window_location_helper.js @@ -10,7 +10,7 @@ */ const useMockLocation = (fn) => { const origWindowLocation = window.location; - let currentWindowLocation; + let currentWindowLocation = origWindowLocation; Object.defineProperty(window, 'location', { get: () => currentWindowLocation, diff --git a/spec/frontend/__helpers__/set_window_location_helper.js b/spec/frontend/__helpers__/set_window_location_helper.js index a94e73762c9..573a089f111 100644 --- a/spec/frontend/__helpers__/set_window_location_helper.js +++ b/spec/frontend/__helpers__/set_window_location_helper.js @@ -1,40 +1,53 @@ /** - * setWindowLocation allows for setting `window.location` - * (doing so directly is causing an error in jsdom) + * setWindowLocation allows for setting `window.location` within Jest. * - * Example usage: - * assert(window.location.hash === undefined); - * setWindowLocation('http://example.com#foo') - * assert(window.location.hash === '#foo'); + * The jsdom environment at the time of writing does not support changing the + * current location (see + * https://github.com/jsdom/jsdom/blob/16.4.0/lib/jsdom/living/window/navigation.js#L76), + * hence this helper. * - * More information: - * https://github.com/facebook/jest/issues/890 + * This helper mutates the current `window.location` very similarly to how + * a direct assignment to `window.location.href` would in a browser (but + * without the navigation/reload behaviour). For instance: * - * @param url + * - Set the full href by passing an absolute URL, e.g.: + * + * setWindowLocation('https://gdk.test'); + * // window.location.href is now 'https://gdk.test' + * + * - Set the path, search and/or hash components by passing a relative URL: + * + * setWindowLocation('/foo/bar'); + * // window.location.href is now 'http://test.host/foo/bar' + * + * setWindowLocation('?foo=bar'); + * // window.location.href is now 'http://test.host/?foo=bar' + * + * setWindowLocation('#foo'); + * // window.location.href is now 'http://test.host/#foo' + * + * setWindowLocation('/a/b/foo.html?bar=1#qux'); + * // window.location.href is now 'http://test.host/a/b/foo.html?bar=1#qux + * + * Both approaches also automatically update the rest of the properties on + * `window.locaton`. For instance: + * + * setWindowLocation('http://test.host/a/b/foo.html?bar=1#qux'); + * // window.location.origin is now 'http://test.host' + * // window.location.pathname is now '/a/b/foo.html' + * // window.location.search is now '?bar=1' + * // window.location.searchParams is now { bar: 1 } + * // window.location.hash is now '#qux' + * + * @param {string} url A string representing an absolute or relative URL. + * @returns {undefined} */ export default function setWindowLocation(url) { - const parsedUrl = new URL(url); + if (typeof url !== 'string') { + throw new TypeError(`Expected string; got ${url} (${typeof url})`); + } - const newLocationValue = [ - 'hash', - 'host', - 'hostname', - 'href', - 'origin', - 'pathname', - 'port', - 'protocol', - 'search', - ].reduce( - (location, prop) => ({ - ...location, - [prop]: parsedUrl[prop], - }), - {}, - ); + const newUrl = new URL(url, window.location.href); - Object.defineProperty(window, 'location', { - value: newLocationValue, - writable: true, - }); + global.jsdom.reconfigure({ url: newUrl.href }); } diff --git a/spec/frontend/__helpers__/set_window_location_helper_spec.js b/spec/frontend/__helpers__/set_window_location_helper_spec.js index 98f26854822..c0f3debddbc 100644 --- a/spec/frontend/__helpers__/set_window_location_helper_spec.js +++ b/spec/frontend/__helpers__/set_window_location_helper_spec.js @@ -1,40 +1,133 @@ import setWindowLocation from './set_window_location_helper'; +import { TEST_HOST } from './test_constants'; -describe('setWindowLocation', () => { - const originalLocation = window.location; +describe('helpers/set_window_location_helper', () => { + const originalLocation = window.location.href; - afterEach(() => { - window.location = originalLocation; + beforeEach(() => { + setWindowLocation(originalLocation); }); - it.each` - url | property | value - ${'https://gitlab.com#foo'} | ${'hash'} | ${'#foo'} - ${'http://gitlab.com'} | ${'host'} | ${'gitlab.com'} - ${'http://gitlab.org'} | ${'hostname'} | ${'gitlab.org'} - ${'http://gitlab.org/foo#bar'} | ${'href'} | ${'http://gitlab.org/foo#bar'} - ${'http://gitlab.com'} | ${'origin'} | ${'http://gitlab.com'} - ${'http://gitlab.com/foo/bar/baz'} | ${'pathname'} | ${'/foo/bar/baz'} - ${'https://gitlab.com'} | ${'protocol'} | ${'https:'} - ${'http://gitlab.com#foo'} | ${'protocol'} | ${'http:'} - ${'http://gitlab.com:8080'} | ${'port'} | ${'8080'} - ${'http://gitlab.com?foo=bar&bar=foo'} | ${'search'} | ${'?foo=bar&bar=foo'} - `( - 'sets "window.location.$property" to be "$value" when called with: "$url"', - ({ url, property, value }) => { - expect(window.location).toBe(originalLocation); - - setWindowLocation(url); - - expect(window.location[property]).toBe(value); - }, - ); - - it.each([null, 1, undefined, false, '', 'gitlab.com'])( - 'throws an error when called with an invalid url: "%s"', - (invalidUrl) => { - expect(() => setWindowLocation(invalidUrl)).toThrow(/Invalid URL/); - expect(window.location).toBe(originalLocation); - }, - ); + describe('setWindowLocation', () => { + describe('given a complete URL', () => { + it.each` + url | property | value + ${'https://gitlab.com#foo'} | ${'hash'} | ${'#foo'} + ${'http://gitlab.com'} | ${'host'} | ${'gitlab.com'} + ${'http://gitlab.org'} | ${'hostname'} | ${'gitlab.org'} + ${'http://gitlab.org/foo#bar'} | ${'href'} | ${'http://gitlab.org/foo#bar'} + ${'http://gitlab.com'} | ${'origin'} | ${'http://gitlab.com'} + ${'http://gitlab.com/foo/bar/baz'} | ${'pathname'} | ${'/foo/bar/baz'} + ${'https://gitlab.com'} | ${'protocol'} | ${'https:'} + ${'ftp://gitlab.com#foo'} | ${'protocol'} | ${'ftp:'} + ${'http://gitlab.com:8080'} | ${'port'} | ${'8080'} + ${'http://gitlab.com?foo=bar&bar=foo'} | ${'search'} | ${'?foo=bar&bar=foo'} + `( + 'sets "window.location.$property" to be "$value" when called with: "$url"', + ({ url, property, value }) => { + expect(window.location.href).toBe(originalLocation); + + setWindowLocation(url); + + expect(window.location[property]).toBe(value); + }, + ); + }); + + describe('given a partial URL', () => { + it.each` + partialURL | href + ${'//foo.test:3000/'} | ${'http://foo.test:3000/'} + ${'/foo/bar'} | ${`${originalLocation}foo/bar`} + ${'foo/bar'} | ${`${originalLocation}foo/bar`} + ${'?foo=bar'} | ${`${originalLocation}?foo=bar`} + ${'#a-thing'} | ${`${originalLocation}#a-thing`} + `('$partialURL sets location.href to $href', ({ partialURL, href }) => { + expect(window.location.href).toBe(originalLocation); + + setWindowLocation(partialURL); + + expect(window.location.href).toBe(href); + }); + }); + + describe('relative path', () => { + describe.each` + initialHref | path | newHref + ${'https://gdk.test/foo/bar'} | ${'/qux'} | ${'https://gdk.test/qux'} + ${'https://gdk.test/foo/bar/'} | ${'/qux'} | ${'https://gdk.test/qux'} + ${'https://gdk.test/foo/bar'} | ${'qux'} | ${'https://gdk.test/foo/qux'} + ${'https://gdk.test/foo/bar/'} | ${'qux'} | ${'https://gdk.test/foo/bar/qux'} + ${'https://gdk.test/foo/bar'} | ${'../qux'} | ${'https://gdk.test/qux'} + ${'https://gdk.test/foo/bar/'} | ${'../qux'} | ${'https://gdk.test/foo/qux'} + `('when location is $initialHref', ({ initialHref, path, newHref }) => { + beforeEach(() => { + setWindowLocation(initialHref); + }); + + it(`${path} sets window.location.href to ${newHref}`, () => { + expect(window.location.href).toBe(initialHref); + + setWindowLocation(path); + + expect(window.location.href).toBe(newHref); + }); + }); + }); + + it.each([null, 1, undefined, false, 'https://', 'https:', { foo: 1 }, []])( + 'throws an error when called with an invalid url: "%s"', + (invalidUrl) => { + expect(() => setWindowLocation(invalidUrl)).toThrow(); + expect(window.location.href).toBe(originalLocation); + }, + ); + + describe('affects links', () => { + it.each` + url | hrefAttr | expectedHref + ${'http://gitlab.com/'} | ${'foo'} | ${'http://gitlab.com/foo'} + ${'http://gitlab.com/bar/'} | ${'foo'} | ${'http://gitlab.com/bar/foo'} + ${'http://gitlab.com/bar/'} | ${'/foo'} | ${'http://gitlab.com/foo'} + ${'http://gdk.test:3000/?foo=bar'} | ${'?qux=1'} | ${'http://gdk.test:3000/?qux=1'} + ${'https://gdk.test:3000/?foo=bar'} | ${'//other.test'} | ${'https://other.test/'} + `( + 'given $url, <a href="$hrefAttr"> points to $expectedHref', + ({ url, hrefAttr, expectedHref }) => { + setWindowLocation(url); + + const link = document.createElement('a'); + link.setAttribute('href', hrefAttr); + + expect(link.href).toBe(expectedHref); + }, + ); + }); + }); + + // This set of tests relies on Jest executing tests in source order, which is + // at the time of writing the only order they will execute, by design. + // See https://github.com/facebook/jest/issues/4386 for more details. + describe('window.location resetting by global beforeEach', () => { + const overridden = 'https://gdk.test:1234/'; + const initial = `${TEST_HOST}/`; + + it('works before an override', () => { + expect(window.location.href).toBe(initial); + }); + + describe('overriding', () => { + beforeEach(() => { + setWindowLocation(overridden); + }); + + it('works', () => { + expect(window.location.href).toBe(overridden); + }); + }); + + it('works after an override', () => { + expect(window.location.href).toBe(initial); + }); + }); }); |