How to fix Error: Not implemented: navigation (except hash changes)
Solution 1
I faced a similar issue in one of my unit tests. Here's what I did to resolve it.
- Replace
window.location.href
withwindow.location.assign(url)
ORwindow.location.replace(url)
-
JSDOM will still complain about
window.location.assign
not implemented.Error: Not implemented: navigation (except hash changes)
-
Then, in one of your unit tests for the above component / function containing
window.assign(url)
orwindow.replace(url)
define the following-
sinon.stub(window.location, 'assign');
sinon.stub(window.location, 'replace');
- Make sure you import sinon
import sinon from 'sinon';
-
Hopefully, this should fix the issue for you as it did for me.
The reason JSDOM complains about the Error: Not implemented: navigation (except hash changes)
is because JSDOM does not implement methods like window.alert
, window.location.assign
, etc.
References:
- http://xxd3vin.github.io/2018/03/13/error-not-implemented-navigation-except-hash-changes.html
- https://www.npmjs.com/package/jsdom#virtual-consoles
Solution 2
Alternative version that worked for me with jest
only:
let assignMock = jest.fn();
delete window.location;
window.location = { assign: assignMock };
afterEach(() => {
assignMock.mockClear();
});
Reference: https://remarkablemark.org/blog/2018/11/17/mock-window-location/
Solution 3
Alternate solution: You could mock the location object
const mockResponse = jest.fn();
Object.defineProperty(window, "location", {
value: {
hash: {
endsWith: mockResponse,
includes: mockResponse
},
assign: mockResponse
},
writable: true
});
Solution 4
You can use jest-location-mock package for that
Usage example with CRA (create-react-app)
// src/setupTests.ts
import "jest-location-mock";
Solution 5
I found a good reference that explain and solve the problem: https://remarkablemark.org/blog/2018/11/17/mock-window-location/
Because the tests are running under Node, it can't understand window.location, so we need to mock the function:
Ex:
delete window.location;
window.location = { reload: jest.fn() }
Related videos on Youtube
Comments
-
Tran Son Hoang almost 2 years
I am implementing unit test for a file that contain
window.location.href
and I need to check it.My jest version is
22.0.4
. Everything is fine when I run my test on node version >=10But I get this error when I run it on
v8.9.3
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29 Error: Not implemented: navigation (except hash changes)
I have no idea about it. I have searched on many page to find out the solution or any hint about this to figure out what happened here.
[UPDATE] - I took a look deep to source code and I think this error is from jsdom.
at module.exports (webapp/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17) at navigateFetch (webapp/node_modules/jsdom/lib/jsdom/living/window/navigation.js:74:3)
navigation.js file
exports.evaluateJavaScriptURL = (window, urlRecord) => { const urlString = whatwgURL.serializeURL(urlRecord); const scriptSource = whatwgURL.percentDecode(Buffer.from(urlString)).toString(); if (window._runScripts === "dangerously") { try { return window.eval(scriptSource); } catch (e) { reportException(window, e, urlString); } } return undefined; }; exports.navigate = (window, newURL, flags) => { // This is NOT a spec-compliant implementation of navigation in any way. It implements a few selective steps that // are nice for jsdom users, regarding hash changes and JavaScript URLs. Full navigation support is being worked on // and will likely require some additional hooks to be implemented. const document = idlUtils.implForWrapper(window._document); const currentURL = document._URL; if (!flags.reloadTriggered && urlEquals(currentURL, newURL, { excludeFragments: true })) { if (newURL.fragment !== currentURL.fragment) { navigateToFragment(window, newURL, flags); } return; } // NOT IMPLEMENTED: Prompt to unload the active document of browsingContext. // NOT IMPLEMENTED: form submission algorithm // const navigationType = 'other'; // NOT IMPLEMENTED: if resource is a response... if (newURL.scheme === "javascript") { window.setTimeout(() => { const result = exports.evaluateJavaScriptURL(window, newURL); if (typeof result === "string") { notImplemented("string results from 'javascript:' URLs", window); } }, 0); return; } navigateFetch(window); };
not-implemented.js
module.exports = function (nameForErrorMessage, window) { if (!window) { // Do nothing for window-less documents. return; } const error = new Error(`Not implemented: ${nameForErrorMessage}`); error.type = "not implemented"; window._virtualConsole.emit("jsdomError", error); };
I see some weird logics in these file.
const scriptSource = whatwgURL.percentDecode(Buffer.from(urlString)).toString();
- then check string and return error
-
Tran Son Hoang over 5 yearsYes. I applied this solution by replacing
href
byassign
and it works well. -
Bennybear almost 4 yearsFor Ionic React users. You can place this code in your setupTests.ts file.
-
Jabinator1 over 2 yearsjust to add to this, I personally had to include all of the keys of the window.location object in order for this to work, assigning all the functions to assignMock and everything else to a string, except for ancestorOrigins, which I just set to null. This may be due to my eslint settings getting mad at me, so this might not be necessary for everyone. Other than that, this worked for me in 2021!
-
Viraj Singh over 2 yearsGetting this error in typescript TypeError: Descriptor for property assign is non-configurable and non-writable 9 | > 10 | sinon.stub(window.location, 'assign'); | ^ 11 | sinon.stub(window.location, 'replace');
-
Logan Cundiff about 2 yearsFor Typescript:
window.location = ({ assign: assignMock as any }) as Location;