最近将jest
(在后台使用 jsdom)从版本 21.2.0 升级到 22.0.6 后,我开始收到错误: "Error: Not implemented:" navigation
我的代码依赖于window.location
并且我在测试中使用:
beforeEach(() => {
window.location.href = `/ms/submission/?mybib`;
window.location.search = '?mybib';
});
有没有办法使用新版本的 jsdom 定义window.location.search
的值?
我也收到这个错误
Error: Not implemented: navigation (except hash changes)
at module.exports (...\node_modules\jsdom\lib\jsdom\browser\not-implemented.js:9:17)
at navigateFetch (...\node_modules\jsdom\lib\jsdom\living\window\navigation.js:74:3)
jsdom 不支持导航,因此设置 window.location.href 或类似的将给出此消息。 我不确定 Jest 之前是否只是压制了这些消息,或者是什么。
这可能是您应该在测试中修复的问题,因为这意味着如果您在浏览器中运行这些测试,当您将页面导航到新 URL 时,测试运行器会完全被震撼,并且您将永远看不到任何测试结果。 在 jsdom 中,我们只是向控制台输出一条消息,您可以根据需要忽略该消息,或者您可以修复您的测试以使其在更多环境中更好地工作。
无论如何,我想为人们添加更多关于此的文档,因此我将保留此问题以跟踪这样做。
完全明白你在说什么。 最近的 Jest 22 更新从 JSDOM 9 到 11 IIRC,因此 9.x 中的行为可能完全不同。
除此之外,我希望看到在 JSDOM 中实现的导航带有某种标志,以使其在加载不同页面时无操作(与 HTML5 pushstate 的精神类似。)该库非常常用于测试目的因此,虽然可能是一个古怪的请求,但它会经常使用。
我认为我们不应该添加一个标志,使您的测试在 jsdom 中与在浏览器中运行的不同。 然后你的东西可能会在浏览器中被破坏(例如,它可能将用户重定向到其他页面,而不是执行测试看到的操作),你甚至不会注意到!
那么在这种情况下,除了不卸载当前页面上下文之外,它不会做任何不同的事情。 我仍然希望window.location.href
被更新,等等。
@domenic我遇到了同样的问题,我想知道是否有某种最佳实践可以使用设置window.location
的应用程序设置 JSDOM。 据我所知,JSDOM 在尝试设置window.location
时会抛出错误,并在尝试设置window.location.href
时记录错误 - 但是我在 mdn 上读到这两个应该是同义词。 我应该以另一种更容易存根的方式更新位置吗?
感谢帮助😅😅
请允许我发布我自己问题的答案😁
我只是将window.location = url;
和window.location.href = url;
的用法替换为
window.location.assign(url);
然后在我的测试中我做了:
sinon.stub(window.location, 'assign');
expect(window.location.assign).to.have.been.calledWith(url);
像魅力一样工作 - 希望它可以帮助别人👍
同意这应该是开箱即用的。 我们在 FB 模拟window.location
,但这与 jsdom 的History
实现相冲突。
作为一个小团队,我们当然会感谢依赖我们在 jsdom 中正确实现导航的大型项目的帮助。
如果有人感兴趣, https://github.com/jsdom/jsdom/pull/1913可能是一个不错的起点。
可能的解决方案是在单元测试中依赖于window
对象的依赖注入/模拟。
就像是:
it('can test', () => {
const mockWindow = {location: {href: null}};
fn({window: mockWindow});
expect(mockWindow.href).toEqual('something');
});
这并不理想,但正如@domenic 所说:
这可能是您应该在测试中修复的问题,因为这意味着如果您在浏览器中运行这些测试,那么当您将页面导航到新 URL 时,测试运行器会完全被震撼
现在我们接受这个,是的,我们更改了测试的实现代码,这被认为是不好的做法,但我们晚上也睡得很好!
快乐测试
@hontas的解决方案有帮助:
我确实在我的代码中使用了window.location.assign(Config.BASE_URL);
。
这是测试:
jest.spyOn(window.location, 'assign').mockImplementation( l => {
expect(l).toEqual(Config.BASE_URL);
})
window.location.assign.mockClear();
同样的问题,我在我的代码中使用了window.location.search = foo;
,我想使用 jsdom(和 jest)来测试它🤔
更新到 jsdom 12.2.0 后出现错误:
类型错误:无法重新定义属性:分配
在const assign = sinon.stub(document.location, 'assign')
如何解决?
@yuri-sakharov
更新到 jsdom 12.2.0 后出现错误:
类型错误:无法重新定义属性:分配
在const assign = sinon.stub(document.location, 'assign')
如何解决?
sinon.stub(document.location, 'assign')
需要是:
sinon.stub(window.location, 'assign')
你需要用window
替换document
window
我有以下功能
export const isLocalHost = () => Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
为了能够测试它是否有效,我直接注入了hostname
it('#isLocalHost should return true for all the cases of localhost', () => {
window.location.hostname = 'localhost';
expect(isLocalHost()).toBeTruthy();
window.location.hostname = '[::1]';
expect(isLocalHost()).toBeTruthy();
window.location.hostname = '127.0.0.1';
expect(isLocalHost()).toBeTruthy();
// Reset back the hostname to avoid issues with it
window.location.hostname = '';
});
但我收到此错误。
我不希望jsdom
完全实现导航,但至少添加键并模拟功能。
我对为什么不断收到此错误感到困惑,我只想能够设置该值。
@尼克哈尔夫
我像你写的那样将它替换为window.location
但结果相同TypeError: Cannot redefine property: assign
有任何想法吗?
与@yuri-sakharov 运行 mocha 的问题相同。
我似乎无法用window.location.*
替换/更新/模拟或做任何事情。 我看到的唯一方法是创建我自己的自定义window.location
模拟并更改整个代码库以依赖它。
@hontas的解决方案有帮助:
我确实在我的代码中使用了
window.location.assign(Config.BASE_URL);
。这是测试:
jest.spyOn(window.location, 'assign').mockImplementation( l => { expect(l).toEqual(Config.BASE_URL); }) window.location.assign.mockClear();
@zxest的@hontas解决方案的 Jest 版本对我不起作用,但这样做了:
window.location.assign = jest.fn();
expect(window.location.assign).toHaveBeenCalledWith('https://correct-uri.com');
window.location.assign.mockRestore();
如果您不想更改代码以使用location.assign
- 这似乎适用于 JSDom 11 和 13(尽管 JSDom 将来可能会破坏它......)
delete window.location;
window.location = {}; // or stub/spy etc.
最后一个答案对我有用,但我必须定义replace
:
delete window.location
window.location = { replace: jest.fn() }
希望能帮助到你。
我收到 TypeError:无法重新定义属性:使用 sinon 7.2.3 和 jsdom 13.2.0 进行分配。 不知道为什么这适用于某些人而不适用于其他人?
这对我有用:
global.window = Object.create(window);
const url = 'http://localhost';
Object.defineProperty(window, 'location', {
value: {
href: url,
},
writable: true,
});
我们使用pushState
使其工作
window.history.pushState(
{},
'',
'http://localhost/something/123?order=asc'
);
这是一个困难的局面。 JSDOM 不完全支持导航(除了面包屑)并且 JSDOM 不允许我们模拟导航。 最终结果是我无法编写最终尝试触发 navigation 的测试。
如果 JSDOM 确实学会了导航(我什至不确定这意味着什么),也许我可以断言在适当的页面上。 但是,对于我的测试用例,断言导航是触发的,而不是实际执行的,更清晰/更快。 这是我过去在使用 jsdom 进行测试时所做的,现在它已经坏了。
请允许我发布我自己问题的答案
我只是将window.location = url;
和window.location.href = url;
的用法替换为window.location.assign(url);
然后在我的测试中我做了:
sinon.stub(window.location, 'assign'); expect(window.location.assign).to.have.been.calledWith(url);
像魅力一样工作 - 希望它可以对其他人有所帮助
如果您可以控制目标网址,但如果您正在加载 google 或 Instagram 网站,该怎么办。 或任何网站? 你怎么能解决这个问题?
根据@chrisbateman 的回答,我设法
describe('', () => {
const originalLocation = window.location;
beforeEach(() => {
delete window.location;
window.location = {
href: '',
};
});
afterEach(() => {
window.location = originalLocation;
});
it('', () => {
// test here
});
});
我使用这个配置解决了这个问题。 我想测试哈希 url 的重定向:
beforeEach(() => {
delete global.window;
global.window = {
location: { replace: jest.fn(url => ({ href: url })) },
};
});
it('should redirect hash url', () => {
window.location.hash = '#/contrat?id=8171675304';
global.window.location.href =
'http://localhost:3000/#/contrat?id=8171675304';
redirectHashUrl();
expect(window.location.replace).toHaveBeenCalled();
});
@chrisbateman @hamzahamidi谢谢,该解决方案运作良好。
也许这不是一个好的做法,但我们有一些测试,这些测试依赖于位置/主机/主机名和其他位置属性。 所以我们想要的模拟位置和之后恢复对我有用。
const realLocation = window.location;
describe('bla bla', () => {
afterEach(() => {
window.location = realLocation;
});
it('test where I want to use hostname', () => {
delete window.location;
window.location = {
hostname: 'my-url-i-expect.com'
};
// check my function that uses hostname
});
});
这对我有用:
global.window = Object.create(window); const url = 'http://localhost'; Object.defineProperty(window, 'location', { value: { href: url, }, writable: true, });
它不适用于CI
类的 circleci
@hontas的解决方案有帮助:
我确实在我的代码中使用了
window.location.assign(Config.BASE_URL);
。这是测试:
jest.spyOn(window.location, 'assign').mockImplementation( l => { expect(l).toEqual(Config.BASE_URL); }) window.location.assign.mockClear();
谢谢伙计,你救了我一天! :)
就我而言,我测试的查询字符串,并且因为我的规范的主体是查询字符串本身,我不想绕过它,但我很高兴存根出来。 就我而言,这很有效:
let name = "utm_content"
window.history.pushState({}, 'Test Title', '/test.html?utm_content=abc');
expect(ParseUrlUtils.getParam(name)).toBe("abc")
请注意,这里窗口的标题是什么并不重要,它是/test.html
(不是真实的)也无关紧要,重要的是查询字符串被正确获取(这通过)
示例的问题在于没有使用方法和 getter。 我所做的基本上是用 URL 对象替换 Location 对象。 URL 具有位置的所有属性(搜索、主机、哈希等)。
const realLocation = window.location;
describe('My test', () => {
afterEach(() => {
window.location = realLocation;
});
test('My test func', () => {
// @ts-ignore
delete window.location;
// @ts-ignore
window.location = new URL('http://google.com');
// ...
});
});
我也收到这个错误
Error: Not implemented: navigation (except hash changes) at module.exports (...\node_modules\jsdom\lib\jsdom\browser\not-implemented.js:9:17) at navigateFetch (...\node_modules\jsdom\lib\jsdom\living\window\navigation.js:74:3)
@hontas的解决方案有帮助:
我确实在我的代码中使用了
window.location.assign(Config.BASE_URL);
。这是测试:
jest.spyOn(window.location, 'assign').mockImplementation( l => { expect(l).toEqual(Config.BASE_URL); }) window.location.assign.mockClear();
这对我有用,谢谢! 但是,我必须使用 jest test() 中的done
参数,否则我无法确定期望是否已评估,并且无论如何测试可能已成功结束:
it('should', (done) => { jest.spyOn(window.location, 'assign').mockImplementation( l => { expect(l).toEqual(Config.BASE_URL); done(); }) window.location.assign.mockClear(); }
@hontas对我不起作用:( 无法重写分配/替换属性
有什么方法可以找出哪个测试触发了“未实现:导航”消息? 我有一套 43 个测试 - 错误只显示一次,并且一直在跳来跳去。 我不知道要修复哪个测试!!! 堆栈跟踪没有给我任何罪魁祸首的迹象:
console.error
Error: Not implemented: navigation (except hash changes)
at module.exports (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at navigateFetch (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/window/navigation.js:76:3)
at exports.navigate (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/window/navigation.js:54:3)
at Timeout._onTimeout (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
at listOnTimeout (internal/timers.js:531:17)
at processTimers (internal/timers.js:475:7) undefined
at VirtualConsole.<anonymous> (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
at module.exports (node_modules/jsdom/lib/jsdom/browser/not-implemented.js:12:26)
at navigateFetch (node_modules/jsdom/lib/jsdom/living/window/navigation.js:76:3)
at exports.navigate (node_modules/jsdom/lib/jsdom/living/window/navigation.js:54:3)
at Timeout._onTimeout (node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
@hontas对我不起作用:( 无法重写分配/替换属性
据我所知,jest 在新版本中改变了一些东西(“jest”:“^26.0.1”)所以现在可以正常工作:
// Mock
Object.defineProperty(window, 'location', {
value: {
pathname: '/terminals',
assign: jest.fn(),
},
});
// Then test
expect(window.location.assign).toBeCalledWith('/auth');
有什么方法可以找出哪个测试触发了“未实现:导航”消息? 我有一套 43 个测试 - 错误只显示一次,并且一直在跳来跳去。 我不知道要修复哪个测试!!! 堆栈跟踪没有给我任何罪魁祸首的迹象:
console.error Error: Not implemented: navigation (except hash changes) at module.exports (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17) at navigateFetch (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/window/navigation.js:76:3) at exports.navigate (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/window/navigation.js:54:3) at Timeout._onTimeout (/Users/naresh/projects/mobx-state-router/node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7) at listOnTimeout (internal/timers.js:531:17) at processTimers (internal/timers.js:475:7) undefined at VirtualConsole.<anonymous> (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45) at module.exports (node_modules/jsdom/lib/jsdom/browser/not-implemented.js:12:26) at navigateFetch (node_modules/jsdom/lib/jsdom/living/window/navigation.js:76:3) at exports.navigate (node_modules/jsdom/lib/jsdom/living/window/navigation.js:54:3) at Timeout._onTimeout (node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
很难判断哪个测试未实现错误来自。
我在node_modules/jsdom/lib/jsdom/browser/not-implemented.js:12
处添加一个断点,运行调试会话并等待达到断点。 只有这样我才能看到我应该改进哪个测试来摆脱这个消息。
有时需要 2-3 分钟,直到跑步者遇到问题才能进行测试。
PS:在我当前的项目中有 176 个 jsdom 相关测试
我目前正在使用 Jest 26.0.1
并且以下对我有用:
使用window.location.assign(url)
更改窗口位置。
模拟位置对象如下(请注意,不首先删除和重建对象仍然会导致在 Jest 的注释版本和更早版本中未实现的错误,我认为Object.defineProperty
也不起作用,仍然导致错误):
delete window.location;
window.location = {
href: '',
hostname: '',
pathname: '',
protocol: '',
assign: jest.fn()
};
expect(window.location.assign).toBeCalledWith(url);
如果 jest 允许我们轻松地模拟位置而没有所有这些不断出现的问题和变化,那就太好了。 我以前只使用window.location.assign = jest.fn()
很长一段时间没有任何问题,然后我从 v24 升级,现在这个。
Window API 被锁定/冻结是否有充分的理由?
这似乎是人们试图解决未实施导航的障碍。
如果没有,我们可以不冷冻它们吗? 我认为即使是浏览器也不会严格阻止您更改窗口对象。
如果您有一个 jsdom 行为与浏览器不同的示例,请按照问题模板提交问题(包括 jsbin 或类似的显示浏览器行为)。
在 Jest 测试期间,是否有其他人遇到相同的Error: Not implemented: navigation (except hash changes)
使用href
触发锚元素上的点击事件? 对于我的测试,我正在监视一个名为onClick
的函数并对此进行断言,因此我需要在锚元素上实际触发 click 事件。
上面与模拟window.location
相关的解决方案适用于我,我明确调用window.location.replace
或window.location.assign
,但对于导航源自锚元素的这种情况没有帮助被点击。
关于解决方案的任何想法? 谢谢!
您将如何在浏览器中测试该代码? 请记住,在浏览器中单击该链接会吹走您的整个页面并丢弃任何测试结果。 因此,无论您采取什么措施来防止这种情况,也将防止 jsdom 输出到控制台的不太明显的警告消息。
在我的情况下,项目将 Jest 从 23 升级到 26 版本。
我对location.search
。 有同样的错误Error: Not implemented: navigation
。
我测试的模块获取搜索查询参数的值。
下一个实现对我有用:
beforeAll(() => {
delete window.location;
window.location = new URL('your URL');
})
afterAll(() => {
window.location.search = '';
})
@mattcphillips你知道如何解决这个问题吗? 我点击的时候也有问题
@Sabrinovsky不,我最终只是在我的 jest 配置中向setupFiles
添加了一个脚本,它会吞下来自 jsdom 导航的控制台错误,这样它们就不会干扰我们的测试。 比其他任何东西都更像是创可贴,但听起来这些错误在我们的测试设置中是可以预料的。
这是我在测试之前运行的脚本:
// There should be a single listener which simply prints to the
// console. We will wrap that listener in our own listener.
const listeners = window._virtualConsole.listeners('jsdomError');
const originalListener = listeners && listeners[0];
window._virtualConsole.removeAllListeners('jsdomError');
// Add a new listener to swallow JSDOM errors that orginate from clicks on anchor tags.
window._virtualConsole.addListener('jsdomError', error => {
if (
error.type !== 'not implemented' &&
error.message !== 'Not implemented: navigation (except hash changes)' &&
originalListener
) {
originalListener(error);
}
// swallow error
});
这对我有用。
delete global.window.location
global.window.location = { href: 'https://test.com' }
这对我有用
delete window.location
window.location = { assign: jest.fn() }
如果你得到TypeError: Cannot assign to read only property 'assign' of object '[object Location]'
,那么在你的jest.setup.ts
使用类似的东西:
``
global.window = Object.create(window);
Object.defineProperty(window, 'location', {
价值: {
...窗口.位置,
},
可写:真实,
});
````
最有用的评论
请允许我发布我自己问题的答案😁
我只是将
window.location = url;
和window.location.href = url;
的用法替换为然后在我的测试中我做了:
像魅力一样工作 - 希望它可以帮助别人👍