There may be a situation where you need to mock methods on window.location
in Jest:
it('mocks and calls window.location.reload', () => {
window.location.reload = jest.fn();
window.location.reload();
expect(window.location.reload).toHaveBeenCalled();
});
When you run the test, it fails.
Spy on method
Spying on window.location
does make the test pass:
// window.location.reload = jest.fn();
jest.spyOn(window.location, 'reload');
But you’ll get the console error:
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Not implemented: navigation (except hash changes)
This happens because jsdom is trying to make the method behave like its browser counterpart. However, the test is running in Node.js.
Mock method
To remove the error, location.reload
needs to be made configurable before being assigned to a mock:
Object.defineProperty(window.location, 'reload', {
configurable: true,
});
window.location.reload = jest.fn();
When you run the test this time, it should pass without errors.
Update for jsdom 14+
Unfortunately, this no longer works for jsdom >=14.
The new solution is to delete location
and recreate reload
as a mock:
delete window.location;
window.location = { reload: jest.fn() };
Credit goes to Josh for bringing this to my attention.
Solution
Here’s the refactored final solution:
describe('window.location', () => {
const { location } = window;
beforeAll(() => {
delete window.location;
window.location = { reload: jest.fn() };
});
afterAll(() => {
window.location = location;
});
it('mocks `reload`', () => {
expect(jest.isMockFunction(window.location.reload)).toBe(true);
});
it('calls `reload`', () => {
window.location.reload();
expect(window.location.reload).toHaveBeenCalled();
});
});
Gist
The gist that inspired this post:
Repl.it
Run the test in this repl.it: