После недавнего обновления 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';
});
Есть ли способ определить значение window.location.search
используя новую версию jsdom?
Я тоже получаю эту ошибку
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 на IIRC 11, поэтому поведение в 9.x могло быть совсем другим.
Помимо всего этого, мне бы хотелось, чтобы в JSDOM была реализована навигация с каким-то флагом, чтобы сделать ее бесполезной с точки зрения загрузки другой страницы (в духе pushstate HTML5). Библиотека очень часто используется для целей тестирования. так что, возможно, это причудливый запрос, но он будет использоваться часто.
Я не думаю, что мы должны добавлять флаг, который заставляет ваши тесты работать в jsdom иначе, чем в браузерах. Тогда ваш материал может быть поврежден в браузерах (например, он может перенаправлять пользователей на какую-либо другую страницу вместо того, чтобы выполнять действие, которое происходит в ваших тестах), и вы даже не заметите!
Что ж, в этом случае он не будет делать ничего другого, кроме как не выгружать текущий контекст страницы. Я бы все равно ожидал обновления window.location.href
и т. Д.
@domenic У меня такая же проблема, и мне было интересно, есть ли какая-то передовая практика для установки JSDOM с приложением, которое устанавливает window.location
. Из того, что я могу сказать, 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);
Работает как амулет - надеюсь, это может помочь кому-то другому 👍
Согласитесь, это должно работать из коробки. Мы издеваемся над window.location
в FB, но это конфликтует с реализацией History
jsdom.
Как небольшая команда, мы, безусловно, будем признательны за помощь более крупных проектов, которые зависят от нас, чтобы правильно реализовать навигацию в 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) 🤔
PS: Относится к https://github.com/facebook/jest/issues/5266
После обновления до jsdom 12.2.0 возникла ошибка:
TypeError: невозможно переопределить свойство: назначить
на const assign = sinon.stub(document.location, 'assign')
как это исправить?
@ yuri-sakharov
После обновления до jsdom 12.2.0 возникла ошибка:
TypeError: невозможно переопределить свойство: назначить
наconst assign = sinon.stub(document.location, 'assign')
как это исправить?
sinon.stub(document.location, 'assign')
должно быть:
sinon.stub(window.location, 'assign')
вам нужно заменить 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
полностью реализует навигацию, но, по крайней мере, добавит ключи и имитирует функции.
Я не понимаю, почему я продолжаю получать эту ошибку, я просто хочу иметь возможность установить значение.
@nickhallph
Я заменил его на window.location
как вы писали, но результат тот же
TypeError: Cannot redefine property: assign
Любые идеи?
Та же проблема, что и @ yuri-sakharov с мокко.
Я ни в коем случае не могу заменить / обновить / издеваться или сделать что-нибудь с 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();
@zxiest «s Jest версия @hontas» решение не работает для меня, но это сделал:
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: невозможно переопределить свойство: assign с помощью 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 не позволяет нам макетировать навигацию. Конечным результатом является то, что я не могу писать тесты, которые в конечном итоге пытаются запустить навигацию .
Если бы 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);
Работает как шарм - надеюсь, это может помочь кому-то другому
Это если у вас есть контроль над целевым URL, но что, если вы загружаете веб-сайт Google или Instagram. или какой-нибудь сайт? как можно решить эту проблему?
Основываясь на ответе @chrisbateman, мне удалось заставить это работать в среде Jest. Для тех, кто использует Jest, вот мой обходной путь:
describe('', () => {
const originalLocation = window.location;
beforeEach(() => {
delete window.location;
window.location = {
href: '',
};
});
afterEach(() => {
window.location = originalLocation;
});
it('', () => {
// test here
});
});
Я решил эту проблему с помощью этой конфигурации. Я хотел проверить перенаправление для хеш-адресов:
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();
Спасибо, дружище, ты спас мне день! :)
В моем случае я тестирую для строки запроса, и так как предмет моей спецификации IS сама строка запроса, я не хочу , чтобы обойти это, но я рад незавершенному его. в моем случае это сработало:
let name = "utm_content"
window.history.pushState({}, 'Test Title', '/test.html?utm_content=abc');
expect(ParseUrlUtils.getParam(name)).toBe("abc")
Обратите внимание, что здесь не имеет значения, каков заголовок окна, а также не имеет значения, что это /test.html
(что нереально), все, что имеет значение, это то, что строка запроса выбрана правильно ( это проходит)
Проблема с примерами в том, что методы и геттеры не используются. Я просто заменяю объект Location на объект URL. URL имеет все свойства Location (поиск, хост, хеш и т. Д.).
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();
У меня это сработало, спасибо! Однако мне пришлось использовать аргумент done
из jest test (), иначе я не был бы уверен, что ожидание было оценено и тест в любом случае мог завершиться успешно:
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": "^ 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);
Было бы здорово, если бы шутка позволила нам легко имитировать местоположение без всех этих постоянных проблем и изменений. Раньше я долгое время без проблем использовал только window.location.assign = jest.fn()
, потом я обновился с v24, а теперь это.
Есть ли веская причина, по которой API-интерфейсы Windows были заблокированы / заморожены?
Похоже, что это препятствие для людей, которые пытаются обойти навигацию, но не реализованы.
Если нет, то можно ли их просто не заморозить? Я не думаю, что даже браузеры настолько строги в предотвращении изменения оконных объектов.
Если у вас есть пример того, как jsdom ведет себя не так, как браузеры, сообщите о проблеме в соответствии с шаблоном задачи (включая jsbin или подобное поведение браузера).
Кто-нибудь еще сталкивается с тем же Error: Not implemented: navigation (except hash changes)
во время тестов Jest, которые запускают событие щелчка на элементе привязки с href
? В своих тестах я слежу за функцией, которая называется onClick
и делаю утверждения об этом, поэтому мне нужно фактически запустить событие щелчка на элементе привязки.
Приведенные выше решения, связанные с издевательством над 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 Нет, я закончил тем, что просто добавил скрипт в 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 (окно);
Object.defineProperty (окно, 'местоположение', {
ценить: {
... window.location,
},
записываемый: правда,
});
`` ''
Самый полезный комментарий
Разрешите опубликовать ответ на свой вопрос 😁
Я просто заменяю использование
window.location = url;
иwindow.location.href = url;
наа затем в своих тестах я сделал:
Работает как амулет - надеюсь, это может помочь кому-то другому 👍