我正在对包含
window.location.href
的文件实施单元测试,我需要检查它。
我的笑话版本是
22.0.4
。当我在节点版本 >=10 上运行测试时,一切都很好
但是当我在
v8.9.3
上运行时出现此错误
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Not implemented: navigation (except hash changes)
我对此一无所知。我已经在很多页面上搜索以找出解决方案或任何有关此问题的提示,以弄清楚这里发生了什么。
[更新] - 我深入查看了源代码,我认为这个错误来自 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)
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);
};
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);
};
我在这些文件中看到一些奇怪的逻辑。
const scriptSource = whatwgURL.percentDecode(Buffer.from(urlString)).toString();
仅对我有用的替代版本
jest
:
let assignMock = jest.fn();
delete window.location;
window.location = { assign: assignMock };
afterEach(() => {
assignMock.mockClear();
});
参考: https://remarkablemark.org/blog/2018/11/17/mock-window-location/
替代解决方案:您可以模拟位置对象
const mockResponse = jest.fn();
Object.defineProperty(window, 'location', {
value: {
hash: {
endsWith: mockResponse,
includes: mockResponse,
},
assign: mockResponse,
},
writable: true,
});
我在一个单元测试中遇到了类似的问题。这是我为解决该问题所做的事情。
将
window.location.href
替换为 window.location.assign(url)
或
window.location.replace(url)
JSDOM 将会仍然抱怨
window.location.assign
未实现。
Error: Not implemented: navigation (except hash changes)
然后,在包含
window.assign(url)
或 window.replace(url)
的上述组件/函数的单元测试之一中定义以下
sinon.stub(window.location, 'assign');
sinon.stub(window.location, 'replace');
确保导入 sinon
import sinon from 'sinon';
希望这能为您解决问题,就像为我解决问题一样。
参考资料:
您可以使用 jest-location-mock 包来实现
CRA 的使用示例(create-react-app)
// src/setupTests.ts
import "jest-location-mock";
我找到了一个很好的参考来解释和解决问题:https://remarkablemark.org/blog/2018/11/17/mock-window-location/
因为测试是在Node下运行的,它无法理解window.location,所以我们需要mock该函数:
例如:
delete window.location;
window.location = { reload: jest.fn() }
以下对我有用:
const originalHref = window.location.href;
afterEach(() => {
window.history.replaceState({}, "", decodeURIComponent(originalHref));
});
快乐编码:)
it('test', () => {
const { open } = window;
delete window.open;
window.open = jest.fn();
jest.spyOn(window, 'open');
// then call the function that's calling the window
expect(window.open).toHaveBeenCalled();
window.open = open;
});
我在每次测试后都使用 jest.restoreMocks,这对我有用:
beforeEach(() => {
Object.defineProperty(window, 'location', {
value: {assign: jest.fn()},
});
});
Makes the "Error: Not implemented: navigation (except hash changes)" warning to go away
您还可以通过在测试中使用以“#”开头的 url(例如 - “#/testUrl”)来解决此错误。