Привет @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#change-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 я начинаю в FB 1 мая.... ;P
Хороший!
Я пытаюсь перенести наши тесты с Mocha+Chai+Sinon.js на Jest и не могу понять, как изменить местоположение для конкретного теста.
Jest 19.x использует JSDom 9.12 , который не позволяет менять местоположение с помощью трюка Object.defineProperty
. Также я не могу использовать jsdom.changeURL()
по причинам, описанным в tmpvar/jsdom#1700.
@cpojer как насчет реализации какого-то прокси-метода для jsdom.changeURL()
в Jest?
@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,
});
});
};
Извиняюсь за дальнейшее затягивание этой темы, но я попытался смоделировать функцию push, как было предложено...
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 какой у тебя URL? у вас есть testURL, установленный в вашем jest-config.json
, или он инициализируется как about:blank
?
@ianlyons Да, я установил для этого значение "https://test.com/"
в package.json, и ни один из путей не отображается как blank
.
@th3fallen th3fallen Если я правильно вас понял, я не думаю, что это работает для моего варианта использования. Вы передаете URL-адрес как значение контекста, которое вызывает запуск assign? Я пытаюсь собрать элементарный интеграционный тест, поэтому хочу проверить, как маршрутизатор реагирует на начальную загрузку данных. Я издевался над ответом API, а затем мне нужно, чтобы изменение URL-адреса происходило с использованием логики приложения (т.е. я не хочу запускать его извне самостоятельно).
Например, Object.defineProperty
, кажется, помогает тестировать функциональность, основанную на window.location.search
. При этом он мутирует window.location.search
, поэтому другие тесты могут быть затронуты. Есть ли способ «отменить» изменения, которые вы сделали на window.location.search
через Object.defineProperty
, вроде того, как функции шутливого макета имеют функцию 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
Я попытался назначить базовый URL-адрес jsdom
, безуспешно потратил на это 4 часа (я знаю, как это сделать, просто вставьте <base href='your_base_url' />
в дом, но дом создается jest
, не мной, поэтому я сдался.
решение Object.defineProperty
работает только со старой версией jsdom
(вы получаете ошибку «невозможно переопределить свойство с более поздней версией jsdom
»);
если вы используете jsdom
ver> 10, как упоминал @th3fallen , это правильное решение.
используйте window.location.assign
это правильный путь
Если вам просто нужен другой URL, отличный от 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-адреса (это эквивалентно вводу «/login» в строке URL-адреса браузера без базового адреса).
с jsdom
, обычно мы можем настроить базовый URL-адрес через
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.
Вкратце: вы можете издеваться над window.location.assign()
или использовать jest-environment-jsdom-global
, что позволит вам перенастроить jsdom в полете.
спасибо @simon360
я так и сделал ;-)
Я использовал jsdom.reconfigure
для настройки разных начальных urls
в своих тестах, и всякий раз, когда мне нужно изменить URL-адрес в коде (не в тесте), я использую window.location.assign
и издевался над ним. который работал для меня.
только для людей, которые могут столкнуться с той же проблемой, чтобы установить URL-адрес для вашего jsdom
// jest.config.js
module.exorts={
testURL: 'http://localhost:3000',
// or :
testEnvironmentOptions: {
url: "http://localhost:3000/",
referrer: "https://example.com/",
}
}
обратите внимание, что это установит URL для всех ваших тестов;
если вам нужен другой URL-адрес в некоторых конкретных тестах, используйте API jsdom.reconfigure
;
если вам нужно изменить URL-адрес на лету за пределами кода модульного теста (т.е. производственного кода), вам нужно использовать window.location.assign
и издеваться над ним.
Выложил в другой тикет, но выложу сюда:
Нашел хорошее решение для Jest 21.2.1
Хорошо, пока самое простое решение:
Перейдите в настройки Jest (например, я буду использовать package.json):
"jest": {
"testURL": "http://localhost"
}
Теперь у вас будет доступ к объекту окна, а затем вы можете установить любой 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);
}
};
Напишите функцию перенаправления и используйте ее вместо этого. Таким образом, при тестировании env он будет полагаться на URL-адрес jsdom.reconfigure для изменения части 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;
Исправлено, установив «testURL»: « http://localhost/ » в конфигурации Jest (я использую последнюю версию). По умолчанию это « about:blank », и это вызывало ошибку JSDOM (вы не можете изменить URL-адрес «about:blank» на что-то другое).
Ресурсы:
http://jestjs.io/docs/en/configuration#testurl -строка
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.
@ Майк-Тран Это работает! Спасибо! Однако мне не нужны были 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
немного избыточны, потому что вы уже полностью протестировали утилиту mockWindow
в src/testUtils.test.ts. Итак, в тестах для 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
И в моей ситуации может работать поле testURL
в файле jest.config.js
. Но что, если я хочу изменить 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 года, мне пришлось сделать следующее:
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);
}
Этот подход работает с 27 сентября 2019 г.: 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
:
testURL
в jest.config.js
независимо от значения:// 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;
global.window.location = "";
Этот подход работает с 27 сентября 2019 г.: 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, но, похоже, это больше не работает.
это работает для меня на шутке 24.9.0
window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');
это работает для меня на шутке 24.9.0
window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');
Мне пришлось сделать код асинхронным, чтобы заставить его работать, потому что я запускал код внутри промиса.
так сейчас работает 😃
Как проверить местоположение смены в действии vuex?
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 }
это создает Location со всеми исходными функциями, но его можно издеваться:
beforeAll(() => {
const location = window.location
delete global.window.location
global.window.location = Object.assign({}, location)
})
Самый полезный комментарий
Вы правы, это действительно проблема jsdom. В Facebook, чтобы обойти это, мы использовали это:
это работает для нас, однако внутри мы все еще используем jsdom 7.
Я закрою это, так как я считаю, что способ делать вещи
Object.defineProperty
в порядке. Если это не работает для вас в jsdom 8, я буду рад снова открыть его.