嗨@cpojer ,
这实际上更像是一个jsdom@8
问题...请参阅 tmpvar/jsdom#1388,但我也想在此处固定,以便 Jest 选择 jsdom 提出的任何解决方案。
以前使用[email protected]/[email protected]
您可以编写如下测试:
jest.autoMockOff()
jest.setMock('../lib/window', window)
jest.mock('cookies-js')
jest.mock('query-string')
jest.mock('superagent')
describe(['@utils/auth - When an AJAX response returns:'
].join('')
, function () {
beforeEach(function () {
window.location.href = 'http://quux.default.com'
var queryString = require('query-string')
queryString.__setMockParseReturns([{
'access_token': '1234foo',
'expires_in': '9999'
}])
})
it(['should set a redirect token and goto platform ',
'when the AJAX request returns 401.'
].join('')
, function () {
var superagent = require('superagent')
superagent.__setMockAjaxResponses([
[null, { 'status': 401 }]
])
var href = window.location.href
var auth = require('../index.js')
auth.login(function (res) {})
var Cookies = require('cookies-js')
var config = require.requireActual('../config')
expect(decodeURIComponent(window.location.href)).toBe([
config.loginUrl,
config.loginServiceLogin,
'?client_id=',
config.clientId,
'&response_type=token',
'&redirect_uri=',
config.clientRedirectUri
].join(''))
expect(Cookies.__getMockCookieData()[config.clientId + '_state_locationAfterLogin']).toBe(escape(href))
})
并且该测试将通过。 由于jsdom@8
这不再可能并且这些测试失败。
似乎 jsdom 正在寻找某种类型的功能,只是想确保 Jest 会在可用时获取该功能。
你是对的,这确实是一个 jsdom 问题。 在 Facebook,我们为解决这个问题所做的工作是使用这个:
Object.defineProperty(window.location, 'href', {
writable: true,
value: 'some url'
});
这对我们有用,但是我们仍然在内部使用 jsdom 7。
我会关闭这个,因为我相信Object.defineProperty
的做事方式很好。 如果这在 jsdom 8 中对您不起作用,我很乐意重新打开它。
酷,谢谢,我会试试这个。
@cpojer ,我似乎无法弄清楚我需要点击什么来重新打开这个问题......
无论如何,在jest
环境中是否有人可以调用jsdom.changeUrl(window, url)
,如此处所述https://github.com/tmpvar/jsdom#sharing -the-url-of-an-existing-jsdom- [email protected]
中的窗口实例?
旧票,但对于那些仍然有这个问题的人,我们已经开始使用window.location.assign()
代替所以在我们的测试中我们可以像这样模拟assign
函数..
it('will redirect with a bad route', () => {
window.location.assign = jest.fn();
const redirection = shallow(<Redirection />, {
context: {
router: {
location: {
pathname: '/wubbalubbadubdub',
},
},
},
});
expect(window.location.assign).toBeCalledWith(`${CONFIG.APP_LEGACY_ROOT}`);
});
谢谢@th3fallen 。 这很酷!
顺便说一句@cpojer我从 5 月 1 日开始在 FB.... ;P
好的!
我正在尝试将我们的测试从 Mocha+Chai+Sinon.js 迁移到 Jest,但不知道如何更改特定测试的位置。
Jest 19.x使用JSDom 9.12 ,它不允许使用Object.defineProperty
技巧更改位置。 另外,由于 tmpvar/jsdom#1700 中描述的原因,我不能使用jsdom.changeURL()
。
@cpojer在 Jest 中为jsdom.changeURL()
实现一些代理方法怎么样?
@okovpashko我们计划将 jsdom 暴露给环境: https ://github.com/facebook/jest/issues/2460
Object.defineProperty
在 FB 为我们工作。
@thymikee我看到了这个问题,但认为这个提议被拒绝了。
@cpojer我误读了您的示例,并将其与其他与此问题相关的示例混在一起,人们建议使用Object.defineProperty(window, 'location', {value: 'url'});
。 谢谢!
我不仅需要更改 href,因此我编写了简单的方法,这可能对阅读此线程的人有用:
const setURL = (url) => {
const parser = document.createElement('a');
parser.href = url;
['href', 'protocol', 'host', 'hostname', 'origin', 'port', 'pathname', 'search', 'hash'].forEach(prop => {
Object.defineProperty(window.location, prop, {
value: parser[prop],
writable: true,
});
});
};
很抱歉进一步拖出这个线程,但我已经尝试按照建议模拟推送功能......
reactRouterReduxMock.push = (url) => {
Object.defineProperty(window.location, 'href', {
writable: true,
value: url
})
})
但我仍然遇到一个似乎无法解决的 jsdom 错误:
TypeError: Cannot read property '_location' of null
at Window.location (/Users/user/projects/app/client/node_modules/jsdom/lib/jsdom/browser/Window.js:148:79)
at value (/Users/user/projects/app/client/test/integration-tests/initialSetup.js:122:32) //this is the defineProperty line above
我意识到这是一个 jsdom 错误,但是对于那些已经解决了这个问题的人,您是否可以分享更多设置上下文来让我解决这个问题?
谢谢
@matt-dalton 在https://github.com/facebook/jest/issues/890#issuecomment -295939071 中尝试我的建议对我来说效果很好
@matt-dalton 你的网址是什么? 您是否在jest-config.json
中设置了 testURL 或者它是否初始化为about:blank
?
@ianlyons是的,我在 package.json 中为此设置了"https://test.com/"
的值,并且没有任何路径显示为blank
。
@th3fallen如果我理解正确,我认为这不适用于我的用例。 您是否将 url 作为导致触发分配的上下文值传递? 我正在尝试进行基本的集成测试,因此我想检查路由器如何响应初始数据负载。 我已经模拟了 API 响应,然后需要使用应用程序逻辑进行 URL 更改(即我不想自己在外部触发它)。
例如, Object.defineProperty
似乎可以测试依赖于window.location.search
的功能。 话虽如此,它会使window.location.search
发生变异,因此其他测试可能会受到影响。 有没有办法通过Object.defineProperty
“撤消”您对window.location.search
所做的更改,有点像开玩笑的模拟函数有mockReset
函数?
@msholty-fd 你可以试试这个方法:
const origLocation = document.location.href;
let location = origLocation;
beforeAll(() => {
const parser = document.createElement('a');
['href', 'protocol', 'host', 'hostname', 'origin', 'port', 'pathname', 'search', 'hash'].forEach(prop => {
Object.defineProperty(window.location, prop, {
get: function() {
parser.href = location;
return parser[prop];
}
});
});
});
afterEach(() => {
location = origLocation;
});
test('location 1', () => {
location = "https://www.google.com/";
console.log(document.location.href); // https://www.google.com/
});
test('location 2', () => {
console.log(document.location.href); // about:blank
});
它在 Jest 22.0.1 中停止工作
Object.defineProperty(window.location, 'href', {
writable: true,
value: 'some url'
});
错误信息:
TypeError: Cannot redefine property: href
at Function.defineProperty (<anonymous>)
嗯,我们可能需要以某种方式允许人们调用reconfigure
。 https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings
打开一个与此相关的新问题,因为该问题已关闭:#5124
@SimenB我不相信 Jest 应该解决这个问题。 JSDOM 应该允许window.location.assign()
按预期工作并重新配置window.location.href
等的输出。
我得到TypeError: Could not parse "/upgrades/userlogin?hardwareSku=sku1351000490stgvha" as a URL
因为 jsdom 的基本 url 默认为about:blank
我试图为jsdom
分配一个基本 url,花了 4 个小时没有成功(我知道怎么做,只需将<base href='your_base_url' />
插入到 dom 中;但是,dom 是由jest
创建的
Object.defineProperty
解决方案仅适用于jsdom
的旧版本(使用更高版本的jsdom
会出现“无法重新定义属性错误”);
如果您使用的是jsdom
ver > 10,那么@th3fallen提到的是正确的解决方案。
使用window.location.assign
是正确的方法
如果您只想要about:blank
以外的其他网址,则可以使用testURL
配置。
感谢@SimenB的回复。
不,我说的是base url
而不是url
。 我有可以执行window.location.href="/login"
的代码,并且在运行jest
时, jsdom
抛出异常,抱怨/login
不是有效的 url
TypeError: Could not parse "/login" as a URL
我检查了jsdom
的源代码并意识到这是因为我没有基本 url 设置(这相当于在没有基本地址的浏览器 URL 栏中键入“/login”)。
使用jsdom
,通常我们可以通过
global.jsdom = new JSDOM('<html><head> <base href="base_url" /></head></html>')
但是因为jest
设置了jsdom
,所以我们无法控制。
--- 更新:我想我可以显式添加jsdom
作为依赖项并手动配置jsdom
。
然后我找到了一个解决方案,将window.location.href=
替换window.location.assign
并模拟assign
函数,它对我有用
@bochen2014这个问题有更多关于如何使用新版本 jsdom 的信息:#5124
tl;dr:您可以模拟window.location.assign()
,或者您可以使用jest-environment-jsdom-global
,这将允许您在飞行中重新配置 jsdom。
谢谢@simon360
这就是我所做的;-)
我使用jsdom.reconfigure
$ 在我的测试中设置不同的初始urls
,并且每当我需要更改代码中的 url(不是测试)时,我都会使用window.location.assign
并对其进行模拟。 这对我有用。
仅适用于可能/将遇到相同问题的人,为您的 jsdom 设置 url
// jest.config.js
module.exorts={
testURL: 'http://localhost:3000',
// or :
testEnvironmentOptions: {
url: "http://localhost:3000/",
referrer: "https://example.com/",
}
}
请注意,这将为您的所有测试设置 url;
如果您想在某些特定测试中使用不同的 url,请使用jsdom.reconfigure
api;
如果您需要在单元测试代码(即生产代码)之外即时更改 url,您需要使用window.location.assign
并模拟它。
把它贴在其他票上,但我会把它贴在这里:
为 Jest 21.2.1 找到了不错的解决方案
好的,到目前为止,最简单的解决方案是:
进入您的 Jest 设置(例如,我将使用 package.json):
"jest": {
"testURL": "http://localhost"
}
现在您将可以访问 window 对象,然后您可以在测试期间将 URL 设置为您喜欢的任何内容。
it('Should set href url to testURL', () => {
// Here I set href to my needs, opinionated stuff bellow
const newUrl = 'http://localhost/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
Object.defineProperty(window.location, 'href', {
writable: true,
value: newUrl
});
console.log(window.location.href);
});
it('Should set pathname url to testURL', () => {
// Here I set href to my needs, opinionated stuff bellow
const newUrl = '/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
Object.defineProperty(window.location, 'pathname', {
writable: true,
value: newUrl
});
console.log(window.location.pathname);
});
希望这可以帮助某人。
@petar-prog91 这很有帮助。 你有一个错字 - 它应该是testURL
而不是TestURL
@BarthesSimpson感谢您的通知,更新了评论。
停止发布此内容,它不适用于开玩笑”:“^22.4.2”
你好,
我在测试中使用了它,我删除了全局状态并使用 jsdom 创建了一个新状态...:
describe('componentDidMount', () => {
delete global.window
const window = (new JSDOM(``, {url: 'https://example.org/'})).window
global.window = window
describe('When window is defined', () => {
const spy = jest.spyOn(Utils, 'extractTokenFromUrl')
it('should call extract token function with window location', () => {
mount(<Header />)
expect(spy).toHaveBeenCalledWith('https://example.org/')
})
})
})
@UserNT确认 - 它给出TypeError: Cannot redefine property: href
@annemarie35无效 — ReferenceError: JSDOM is not defined
我不知道这是否会对某人有所帮助,但这就是我目前正在做的事情。
const redirectTo = (url: string): void => {
if (process.env.NODE_ENV === "test") {
global.jsdom.reconfigure({ url: `${getBaseUrl()}${url}` });
} else {
window.location.replace(url);
}
};
编写一个重定向函数并使用它。 所以在测试环境中,它会依赖 jsdom.reconfigure url 来改变 url 部分。
我这样用
export const clientFetchData = (
history: Object,
routes: Object,
store: Object
) => {
const callback = location =>
match({ routes, location }, (error, redirectLocation, renderProps) => {
if (error) {
redirectTo("/500.html");
} else if (redirectLocation) {
redirectTo(redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
if (!isEmpty(window.prerenderData)) {
// Delete initial data so that subsequent data fetches can occur
window.prerenderData = undefined;
} else {
// Fetch mandatory data dependencies for 2nd route change onwards
trigger(
FETCH_DATA_HOOK,
renderProps.components,
getDefaultParams(store, renderProps)
);
}
trigger(
UPDATE_HEADER_HOOK,
renderProps.components,
getDefaultParams(store, renderProps)
);
} else {
redirectTo("/404.html");
}
});
history.listen(callback);
callback(history.getCurrentLocation());
};
之后,在你的测试中,它可以是这样的
describe("# match route", () => {
it("should navigate to error page", () => {
fetchData.clientFetchData(history, components, store);
reactRouter.match.mock.calls[0][1](true);
expect(window.location.href).toEqual(`${SERVER_URL}/500.html`);
});
it("should redirect to /hello-world.html page", () => {
fetchData.clientFetchData(history, components, store);
reactRouter.match.mock.calls[0][1](undefined, {
pathname: "/hello-world.html",
search: ""
});
expect(window.location.href).toEqual(`${SERVER_URL}/hello-world.html`);
});
...
我最终这样做了:
global.window = new jsdom.JSDOM('', {
url: 'http://www.test.com/test?foo=1&bar=2&fizz=3'
}).window;
我在 JSDOM 设置文件的顶部有这个:
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
url: "http://test.com"
});
const { window } = jsdom;
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
global.document = window.document;
global.window = window;
global.navigator = {
userAgent: 'node.js',
};
global.HTMLElement = window.HTMLElement;
通过在 Jest 配置中设置“testURL”:“ http://localhost/ ”来修复它(我使用的是最新版本)。 默认情况下它是“ about:blank ”,它会导致 JSDOM 错误(您不能将“about:blank”url 更改为其他内容)。
资源:
http://jestjs.io/docs/en/configuration#testurl -string
https://github.com/jsdom/jsdom/issues/1372
我发现这篇文章很有帮助: https ://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking
“在您的 Jest 配置中,请确保设置以下内容:
"testURL": "https://www.somthing.com/test.html"
然后在测试的 beforeEach() 部分中,根据需要使用更改路径
历史.pushState()。
window.history.pushState({}, 'Test Title', '/test.html?query=true');
瞧! 现在,您可以更改任何测试的路径,而不必像其他人在上面提到的线程中建议的那样覆盖任何 jsdom 配置。 不确定我在哪个线程上找到了这个解决方案,但对发布它的开发人员表示敬意!”
@Mike-Tran 你摇滚! 这完全有效,如此简单。 我什至不必使用 testURL 设置。
@Mike-Tran 那行得通! 谢谢! 但是,我不需要testURL
或beforeEach
。 我已经做了:
window.history.pushState({}, 'Test Title', '/test.html?query=true');
现在我不必再使用Object.defineProperty
了😅
@jcmcneal谢谢你帮我做的! (开玩笑 23.0.0)
如果您的目标是模拟window
对象,这是我的(不是那么优雅,但它有效)解决方案:
创建一个接口(不确定接口是否是正确的词,但我希望你明白这一点)类:
// window.js
export const { location } = window;
在您的实际代码中,将window
替换为接口方法,例如win
// myFile.js
import * as win from './window';
export function doSomethingThatRedirectsPage() {
win.location.href = 'google.com';
}
然后,在你的笑话测试中,你只是将它们模拟出来,这样 jsdom 就不会抱怨了。 你甚至可以断言它们:
// myFile.test.js
import * as myFile from './myFile';
import * as win from './window';
it('should redirect', () => {
win.location = { href: 'original-url' };
expect(win.location.href).toBe('original-url');
myFile.doSomethingThatRedirectsPage();
expect(win.location.href).toBe('google.com');
});
@Mike-Tran,@ jcmcneal谢谢! 所有的作品都一样!
类 SSOtestComponent 扩展 React.Component {
componentDidMount() {
let isSuccess = this.props.location.pathname === '/sso/test/success' ? true : false
window.opener.postMessage({ type: "sso_test", isSuccess,...this.props.location.query}, window.location.origin)
}
onSsoAuthenticate() {
}
componentWillUnmount() {
}
render() {
return (<Loader />);
}
}
module.exports = SSOtestComponent;
我正在使用 enjyme 和 jest 编写单元测试用例,如何编写条件 window.location ...请给出答案
这对我有用
const location = JSON.stringify(window.location);
delete window.location;
Object.defineProperty(window, 'location', {
value: JSON.parse(location)
});
Object.defineProperty(global.location, 'href', {
value: 'http://localhost/newURL',
configurable: true
});
在玩笑版本 23.6.0
这对我有用。
delete global.window.location
global.window.location = { href: 'https://test-domain.com.br', ...anyOptions }
@FelipeBohnertPaetzold谢谢
谢谢@FelipeBohnertPaetzold。 我在我的代码中使用location.host
,所以发现我需要一个完整的位置对象,所以以下对我来说效果更好,而不必手动传递每个位置属性:
delete global.window.location;
global.window.location = new URL("https://www.ediblecode.com/");
请注意,这适用于 Node 6.13+(请参阅URL 类文档)并且我使用的是 Jest 24。
另请注意,这不适用于相对 URL,请参阅https://url.spec.whatwg.org/#example -url-parsing。
这个TypeScript在Jest 24.0.0和 Node 10.15.0上为我工作:
import { mockWindow } from './testUtils';
mockWindow(window, 'http://localhost');
describe('setup tests', () => {
describe('window.location', () => {
const saveLocation = window.location;
afterAll(() => {
delete window.location;
window.location = saveLocation;
});
it('location.assign assigns a location', () => {
window.location.assign('http://foo.com');
expect(window.location.href).toBe('http://foo.com/');
(window.location.assign as jest.Mock<void, [string]>).mockClear();
});
it('location.replace replaces a location', () => {
window.location.replace('http://bar.com');
expect(window.location.href).toBe('http://bar.com/');
(window.location.replace as jest.Mock<void, [string]>).mockClear();
});
it('location.reload is a spy', () => {
window.location.reload();
expect(window.location.reload).toHaveBeenCalledTimes(1);
(window.location.reload as jest.Mock).mockClear();
});
});
});
interface MockedLocation extends Location {
assign: jest.Mock<void, [string]>;
reload: jest.Mock;
replace: jest.Mock<void, [string]>;
}
interface MockedWindow extends Window {
location: MockedLocation;
}
export function mockWindow(win: Window = window, href = win.location.href) {
const locationMocks: Partial<MockedLocation> = {
assign: jest.fn().mockImplementation(replaceLocation),
reload: jest.fn(),
replace: jest.fn().mockImplementation(replaceLocation),
};
return replaceLocation(href);
function replaceLocation(url: string) {
delete win.location;
// tslint:disable-next-line:no-any
win.location = Object.assign(new URL(url), locationMocks) as any;
return win as MockedWindow;
}
}
import { mockWindow } from './testUtils';
describe('test utils', () => {
describe('mockWindow', () => {
const saveLocation = window.location;
afterAll(() => {
delete window.location;
window.location = saveLocation;
});
it('location.assign assigns a location', () => {
const { assign } = mockWindow().location;
assign('http://foo.com');
expect(window.location.href).toBe('http://foo.com/');
assign.mockClear();
});
it('location.replace replaces a location', () => {
const { replace } = mockWindow().location;
replace('http://bar.com');
expect(window.location.href).toBe('http://bar.com/');
replace.mockClear();
});
it('location.reload is a spy', () => {
const { reload } = mockWindow().location;
reload();
expect(window.location.reload).toHaveBeenCalledTimes(1);
reload.mockClear();
});
});
});
@jedmao
嘿,伙计)很棒的工具!
对我来说, src/setupTests.test.ts
中的测试有点多余,因为您已经在 src/testUtils.test.ts 中完全测试mockWindow
util。 因此,在src/setupTests.ts
的测试中,只需使用正确的参数调用mockWindow
$ 就足够了。
谢谢)
@tzvipm @jup-iter 感谢 👍。 我刚刚发布@jedmao/storage
和@jedmao/location
,它们都与 Jest 完全无关。 您应该能够spyOn
适当的方法而无需编写任何额外的测试,因为 npm 包已经过完全测试。
如果您在使用 Vue 时遇到此错误,只需使用this.$router.push({...})
而不是this.$router.go({...})
将下面的代码放在第 1 行:
delete global.window.location;
global.window.location = "";
现在可以捕获正在更改 window.location 的单击事件。
“笑话”:“^23.6.0”
这有效:
delete window.location;
window.location = Object.assign({}, window.location);
const url = Object.assign({}, new URL('http://google.com'));
Object.keys(url).forEach(prop => (window.location[prop] = url[prop]));
或者更好...
delete (global as any).window;
(global as any).window = new JSDOM(undefined, { url: 'http://google.com' }).window;
你是对的,这确实是一个 jsdom 问题。 在 Facebook,我们为解决这个问题所做的工作是使用这个:
Object.defineProperty(window.location, 'href', { writable: true, value: 'some url' });
这对我们有用,但是我们仍然在内部使用 jsdom 7。
我会关闭这个,因为我相信
Object.defineProperty
的做事方式很好。 如果这在 jsdom 8 中对您不起作用,我很乐意重新打开它。
是的,我有一些函数处理location.search
和location.hash
,我想用你提到的defineProperty
来测试它。 它不会工作!
当我关闭jest
静音模式时,我发现: Error: Not implemented: navigation (except hash changes)
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Not implemented: navigation (except hash changes)
at module.exports (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at navigateFetch (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:74:3)
at exports.navigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:52:3)
at LocationImpl._locationObjectNavigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:29:5)
at LocationImpl.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:213:10)
at Location.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/generated/Location.js:93:25)
at Object.assign (/home/ghlandy/projects/wdph-utils/src/__tests__/url.test.js:6:14)
at Object.asyncJestLifecycle (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
at resolve (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>) undefined
现在我不知道如何测试我的功能。
任何人都有办法改变测试url
你是对的,这确实是一个 jsdom 问题。 在 Facebook,我们为解决这个问题所做的工作是使用这个:
Object.defineProperty(window.location, 'href', { writable: true, value: 'some url' });
这对我们有用,但是我们仍然在内部使用 jsdom 7。
我会关闭这个,因为我相信Object.defineProperty
的做事方式很好。 如果这在 jsdom 8 中对您不起作用,我很乐意重新打开它。是的,我有一些函数处理
location.search
和location.hash
,我想用你提到的defineProperty
来测试它。 它不会工作!当我关闭
jest
静音模式时,我发现:Error: Not implemented: navigation (except hash changes)
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29 Error: Not implemented: navigation (except hash changes) at module.exports (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17) at navigateFetch (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:74:3) at exports.navigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:52:3) at LocationImpl._locationObjectNavigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:29:5) at LocationImpl.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:213:10) at Location.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/generated/Location.js:93:25) at Object.assign (/home/ghlandy/projects/wdph-utils/src/__tests__/url.test.js:6:14) at Object.asyncJestLifecycle (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37) at resolve (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/queueRunner.js:43:12) at new Promise (<anonymous>) undefined
现在我不知道如何测试我的功能。
任何人都有办法改变测试
url
在我的情况下,在jest.config.js
文件中有一个字段testURL
可能有效。 但是如果我想在每次测试之前更改 testURL 怎么办。
我发现这篇文章很有帮助: https ://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking
“在您的 Jest 配置中,请确保设置以下内容:
"testURL": "https://www.somthing.com/test.html"
然后在测试的 beforeEach() 部分中,根据需要使用更改路径
历史.pushState()。window.history.pushState({}, 'Test Title', '/test.html?query=true');
瞧! 现在,您可以更改任何测试的路径,而不必像其他人在上面提到的线程中建议的那样覆盖任何 jsdom 配置。 不确定我在哪个线程上找到了这个解决方案,但对发布它的开发人员表示敬意!”
优秀的解决方案!!! 非常感谢你! @迈克-特兰
我想要一个像这样的简短且非侵入性的解决方案!
为了在 2019 年 6 月开始工作,我必须这样做:
delete global.window.location;
global.window = Object.create(window);
global.window.location = {
port: '123',
protocol: 'http:',
hostname: 'localhost',
};
我用这个……
window.history.pushState({}, '', `${url}/`);
可能我的 JSDOMTestWrapper 的一部分可以帮助某人
/** <strong i="6">@type</strong> {Window} */
this.testWindowObject = Object.create(window);
const wnd = this.testWindowObject;
this.testWindowObject.history = {
state: null,
prev: { /** <strong i="7">@todo</strong> immutable stack with the go(step) method emulation */
state: null,
pathname: null,
search: null,
},
go(step) {
logger.special('history go called', step);
logger.warn('history go has not supported yet');
},
back() {
this.state = this.prev.state;
wnd.location.pathname = this.prev.pathname;
wnd.location.search = this.prev.search;
const eventData = this.state ? { url: this.state.displayURL, newState: this.state, type: 'push' } : null;
wnd.sm.eventsService.triggerEvent(ROUTER_EVENTS.ROUTE_PUSH, eventData);
wnd.sm.urlService.simpleRouteTo(`${ wnd.location.pathname || '' }${ wnd.location.search || '' }`);
logger.special('history back emulated');
},
pushState(state, title, url) {
this.prev.state = Object.assign({}, this.state);
this.prev.pathname = '' + wnd.location.pathname;
this.prev.search = '' + wnd.location.search;
this.state = state;
if (title) wnd.document.title = title;
const [p, s] = url.split('?');
wnd.location.pathname = p;
wnd.location.search = s ? `?${ s }` : '';
logger.special('push state emulated', { state, title, url });
},
replaceState(state, title, url) {
this.prev.state = Object.assign({}, this.state);
this.prev.pathname = '' + wnd.location.pathname;
this.prev.search = '' + wnd.location.search;
this.state = state;
if (title) wnd.document.title = title;
const [p, s] = url.split('?');
wnd.location.pathname = p;
wnd.location.search = s ? `?${ s }` : '';
logger.special('replace state emulated', { state, title, url });
logger.special('test: urlService.getPathName()', wnd.sm.urlService.getPathName());
},
};
this.testWindowObject.innerWidth = WND_WIDTH;
this.testWindowObject.innerHeight = WND_HEIGHT;
this.testWindowObject.fetch = fetchFn;
this.testWindowObject.localStorage = lstMock;
this.testWindowObject.scrollTo = (x, y) => {
/** not implemented yet https://github.com/jsdom/jsdom/issues/1422 */
if (typeof x !== 'number' && (x.left || x.top)) {
y = x.top;
x = x.left;
}
// logger.info(`window.scrollTo(${ x }, ${ y })`);
};
if (fetchFn === JSDOMTestWrapper.FETCH_FN.DEV_MOCK) {
global.Request = RequestMock;
this.testWindowObject.Request = RequestMock;
}
if (href) {
this.testWindowObject.location = Object.assign({}, this.testWindowObject.location, urlapi.parse(href));
}
else {
this.testWindowObject.location = Object.assign({}, this.testWindowObject.location);
}
(function(ELEMENT) {
ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector;
ELEMENT.closest = ELEMENT.closest || function closest(selector) {
if (!this) return null;
if (this.matches(selector)) return this;
if (!this.parentElement) {return null}
else return this.parentElement.closest(selector)
};
ELEMENT.getBoundingClientRect = ELEMENT.getBoundingClientRect || (() =>
({ bottom: WND_HEIGHT, height: WND_HEIGHT, left: 0, right: WND_WIDTH, top: 0, width: WND_WIDTH, x: 0, y: 0 }));
}(Element.prototype));
this.testWindowObject.getBoundingClientRect = () =>
({ bottom: WND_HEIGHT, height: WND_HEIGHT, left: 0, right: WND_WIDTH, top: 0, width: WND_WIDTH, x: 0, y: 0 });
this.testWindowObject.__resizeListeners__ = [];
this.testWindowObject.__resizeTriggers__ = {};
this.testWindowObject._detectElementResize = {
removeResizeListener: () => {},
};
this.testWindowObject.matchMedia = jest.fn().mockImplementation(query => {
return {
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
};
});
this.rftpr = () => {};
this.mode = mode;
this.renderFirstTimePromise = new Promise((resolve) => {
this.rftpr = resolve;
});
this.marpr = () => {};
this.mobileAppReadyPromise = new Promise((resolve) => {
this.marpr = resolve;
});
if (mode === JSDOMTestWrapper.MODE.MOBILE_APP) {
this.testWindowObject.navigator = Object.assign({}, this.testWindowObject.navigator, {
language: storeKey,
appVersion: '5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36',
vendor: 'Google Inc.',
});
global.intercom = {
registerUnidentifiedUser: jest.fn(),
registerForPush: jest.fn(),
};
}
const XApp = mode ? MobileApp : App;
const app = <XApp window={ this.testWindowObject } rftResolve={ this.rftpr } storeKey={ storeKey } apiHost={ apiVersion } forceMobileDetection={ mode } />;
render(app, this.testWindowObject.document.body);
if (mode === JSDOMTestWrapper.MODE.MOBILE_APP) {
setTimeout(() => {
this.testWindowObject.sm.deviceService.appRestorePathHasInit = this.marpr;
this.testWindowObject.sm.deviceService.fireEvent(this.testWindowObject.document, 'deviceready');
}, 200);
}
此方法自 2019 年 9 月 27 日起有效: https ://stackoverflow.com/a/54034379/1344144
global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
value: {
href: url
},
writable: true
});
另一个解决方案目前对我有用,而无需编写jsdom
:
jest.config.js
中设置testURL
,无论值如何:// jest.config.js
'testURL': 'https://someurl.com'
在您的测试文件中:
window.history.pushState({}, 'Mocked page title', 'www.yoururl.com');
学习自: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking。 感谢瑞恩!
不适合我:
TypeError:在严格模式下不允许分配给只读属性
it("should save hash when history is not found", () => {
const historyBkp = global.window.history;
delete global.window.history;
global.window.history = false;
externalLoader.savePageURL(urlTraining);
expect(window.location.hash).to.be.equal(`#page=${urlTraining}`);
global.window.history = historyBkp;
window.location.hash = "";
});
不适合我:
TypeError:在严格模式下不允许分配给只读属性
it("should save hash when history is not found", () => { const historyBkp = global.window.history; delete global.window.history; global.window.history = false; externalLoader.savePageURL(urlTraining); expect(window.location.hash).to.be.equal(`#page=${urlTraining}`); global.window.history = historyBkp; window.location.hash = ""; });
将此添加到全局文件中。
删除 global.window.location;
全局.window.location = "";
此方法自 2019 年 9 月 27 日起有效: https ://stackoverflow.com/a/54034379/1344144
global.window = Object.create(window); const url = "http://dummy.com"; Object.defineProperty(window, "location", { value: { href: url }, writable: true });
我正在尝试使用 location.assign 做类似的事情,但似乎这不再起作用了。
这在 jest 24.9.0 上对我有用
window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');
这在 jest 24.9.0 上对我有用
window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');
我必须使代码异步才能使其正常工作,因为我在 Promise 中运行代码。
所以现在工作😃
如何在 vuex 操作中测试 chenge 位置?
async setForm({ rootState, state, commit, dispatch }, formData) {
....
switch (answ.result.type) {
....
case 'redirect':
console.log(answ.data.url);
window.location = answ.data.url;
console.log({ location: window.location.href });
break;
default:
break;
it('setForm - success, redirect', async done => {
expect(window.location.href).toBe('https://www.google.ru/');
我有错误:
expect(received).toBe(expected) // Object.is equality
Expected: "https://www.google.ru/"
Received: "http://localhost/"
这对我有用
const location = JSON.stringify(window.location); delete window.location; Object.defineProperty(window, 'location', { value: JSON.parse(location) }); Object.defineProperty(global.location, 'href', { value: 'http://localhost/newURL', configurable: true });
在玩笑版本 23.6.0
什么是全局?
全局定义在哪里?
这对我有用。
delete global.window.location global.window.location = { href: 'https://test-domain.com.br', ...anyOptions }
这将创建一个具有所有原始功能的位置,但它是可模拟的:
beforeAll(() => {
const location = window.location
delete global.window.location
global.window.location = Object.assign({}, location)
})
最有用的评论
你是对的,这确实是一个 jsdom 问题。 在 Facebook,我们为解决这个问题所做的工作是使用这个:
这对我们有用,但是我们仍然在内部使用 jsdom 7。
我会关闭这个,因为我相信
Object.defineProperty
的做事方式很好。 如果这在 jsdom 8 中对您不起作用,我很乐意重新打开它。