Commit 4d1389e6 authored by Lukas Eipert's avatar Lukas Eipert

Add a helper to spy on window.location

The window.location interface is read only in newer versions of jest.
This breaks our manual mocks / spys on that interface.

With a helper which replaces `window.location` with a mocked interface
we are able to circumvent that problem.
parent 5383adc7
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
...@@ -628,13 +629,13 @@ describe('Subscriptions Actions', () => { ...@@ -628,13 +629,13 @@ describe('Subscriptions Actions', () => {
}); });
describe('confirmOrderSuccess', () => { describe('confirmOrderSuccess', () => {
useMockLocationHelper();
const params = { location: 'http://example.com', plan_id: 'x', quantity: 10 }; const params = { location: 'http://example.com', plan_id: 'x', quantity: 10 };
it('changes the window location', done => { it('changes the window location', done => {
const spy = jest.spyOn(window.location, 'assign').mockImplementation();
testAction(actions.confirmOrderSuccess, params, {}, [], [], () => { testAction(actions.confirmOrderSuccess, params, {}, [], [], () => {
expect(spy).toHaveBeenCalledWith('http://example.com'); expect(window.location.assign).toHaveBeenCalledWith('http://example.com');
done(); done();
}); });
}); });
......
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import * as actions from '~/create_cluster/eks_cluster/store/actions'; import * as actions from '~/create_cluster/eks_cluster/store/actions';
...@@ -251,12 +252,7 @@ describe('EKS Cluster Store Actions', () => { ...@@ -251,12 +252,7 @@ describe('EKS Cluster Store Actions', () => {
}); });
describe('createClusterSuccess', () => { describe('createClusterSuccess', () => {
beforeEach(() => { useMockLocationHelper();
jest.spyOn(window.location, 'assign').mockImplementation(() => {});
});
afterEach(() => {
window.location.assign.mockRestore();
});
it('redirects to the new cluster URL', () => { it('redirects to the new cluster URL', () => {
actions.createClusterSuccess(null, newClusterUrl); actions.createClusterSuccess(null, newClusterUrl);
......
/**
* 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);
...@@ -33,7 +33,7 @@ describe('setWindowLocation', () => { ...@@ -33,7 +33,7 @@ describe('setWindowLocation', () => {
it.each([null, 1, undefined, false, '', 'gitlab.com'])( it.each([null, 1, undefined, false, '', 'gitlab.com'])(
'throws an error when called with an invalid url: "%s"', 'throws an error when called with an invalid url: "%s"',
invalidUrl => { invalidUrl => {
expect(() => setWindowLocation(invalidUrl)).toThrow(new TypeError('Invalid URL')); expect(() => setWindowLocation(invalidUrl)).toThrow(/Invalid URL/);
expect(window.location).toBe(originalLocation); expect(window.location).toBe(originalLocation);
}, },
); );
......
...@@ -12,7 +12,8 @@ import { ...@@ -12,7 +12,8 @@ import {
} from '~/ide/stores/actions'; } from '~/ide/stores/actions';
import service from '~/ide/services'; import service from '~/ide/services';
import api from '~/api'; import api from '~/api';
import testAction from '../../../helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
const TEST_PROJECT_ID = 'abc/def'; const TEST_PROJECT_ID = 'abc/def';
...@@ -116,6 +117,8 @@ describe('IDE store project actions', () => { ...@@ -116,6 +117,8 @@ describe('IDE store project actions', () => {
}); });
describe('createNewBranchFromDefault', () => { describe('createNewBranchFromDefault', () => {
useMockLocationHelper();
beforeEach(() => { beforeEach(() => {
jest.spyOn(api, 'createBranch').mockResolvedValue(); jest.spyOn(api, 'createBranch').mockResolvedValue();
}); });
...@@ -170,8 +173,6 @@ describe('IDE store project actions', () => { ...@@ -170,8 +173,6 @@ describe('IDE store project actions', () => {
}); });
it('reloads window', done => { it('reloads window', done => {
jest.spyOn(window.location, 'reload').mockImplementation();
createNewBranchFromDefault( createNewBranchFromDefault(
{ {
state: { state: {
......
...@@ -6,11 +6,14 @@ import SidebarService from '~/sidebar/services/sidebar_service'; ...@@ -6,11 +6,14 @@ import SidebarService from '~/sidebar/services/sidebar_service';
import createFlash from '~/flash'; import createFlash from '~/flash';
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue'; import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/sidebar/services/sidebar_service'); jest.mock('~/sidebar/services/sidebar_service');
describe('Confidential Issue Sidebar Block', () => { describe('Confidential Issue Sidebar Block', () => {
useMockLocationHelper();
let wrapper; let wrapper;
const findRecaptchaModal = () => wrapper.find(RecaptchaModal); const findRecaptchaModal = () => wrapper.find(RecaptchaModal);
...@@ -43,10 +46,6 @@ describe('Confidential Issue Sidebar Block', () => { ...@@ -43,10 +46,6 @@ describe('Confidential Issue Sidebar Block', () => {
}); });
}; };
beforeEach(() => {
jest.spyOn(window.location, 'reload').mockImplementation();
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment