Jsdom: Ошибка: не реализовано: навигация

Созданный на 12 янв. 2018  ·  49Комментарии  ·  Источник: jsdom/jsdom

После недавнего обновления 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?

Самый полезный комментарий

Разрешите опубликовать ответ на свой вопрос 😁
Я просто заменяю использование 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);

Работает как амулет - надеюсь, это может помочь кому-то другому 👍

Все 49 Комментарий

Я тоже получаю эту ошибку

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 , запускаю сеанс отладки и жду, пока точка останова не будет достигнута. Только после этого я вижу, какой тест мне следует улучшить, чтобы избавиться от этого сообщения.
image

Иногда бегун достигает теста с проблемой через 2-3 минуты.

PS: В моем текущем проекте 176 тестов, связанных с jsdom.

В настоящее время я использую Jest 26.0.1 и у меня работает следующее:

  1. Используйте window.location.assign(url) чтобы изменить расположение окна.

  2. Смоделируйте объект местоположения следующим образом (обратите внимание, что не удаление и восстановление объекта сначала по-прежнему приводит к нереализованной ошибке в указанной версии Jest и ранее, я думаю, также Object.defineProperty не работает и по-прежнему приводит к ошибка):

delete window.location;
window.location = {
    href: '',
    hostname: '',
    pathname: '',
    protocol: '',
    assign: jest.fn()
};
  1. Утвердить с помощью:
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,
},
записываемый: правда,
});
`` ''

Была ли эта страница полезной?
0 / 5 - 0 рейтинги