Jest: Невозможно изменить window.location с помощью Object.defineProperty

Созданный на 19 дек. 2017  ·  78Комментарии  ·  Источник: facebook/jest

Вы хотите запросить _функцию_ или сообщить _ об ошибке? Сообщить об ошибке

Каково текущее поведение?

Вызов следующего из набора тестов:

Object.defineProperty(location, "hostname", {
  value: "example.com",
  writable: true
});

выдает следующую ошибку:

    TypeError: Cannot redefine property: hostname
        at Function.defineProperty (<anonymous>)

Какое ожидаемое поведение?

Код не должен вызывать исключения, а window.location.hostname === "example.com" должен оценивать значение true.

Судя по всему , jsdom теперь делает window.location не подделываемым . Единственный способ изменить значения в window.location - использовать reconfigure , но (согласно # 2460) Jest не предоставляет jsdom для тестирования.

Укажите точную конфигурацию Jest и укажите свой Jest, узел,версия пряжи / npm и операционная система.

Версия Jest : 22.0.1
Версия узла : 8.6.0
Версия пряжи : 1.2.0
ОС : macOS High Sierra 10.13.2

Discussion Wontfix

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

Я опубликовал на npm новый пакет под названием jest-environment-jsdom-global , который может помочь с проблемами, которые возникают у некоторых людей с Object.defineProperty .

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

У меня похожая проблема. Вы можете создать свое собственное окружение JSDOME и выставить объект jsdom в глобальном масштабе следующим образом.

const JSDOMEnvironment = require('jest-environment-jsdom');

module.exports = class CustomizedJSDomEnvironment extends JSDOMEnvironment {
  constructor(config) {
    super(config);
    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;
    return super.teardown();
  }
};

И затем вы можете вызвать jsdom.reconfigure в своем тестовом примере, как вам нравится

Это хороший обходной путь, спасибо, что поделились!

Вы должны вернуть super.teardown(); поскольку это обещание, кстати

Отлично, @oliverzy - я попробую. Спасибо!

Есть ли подходящее место, чтобы это задокументировать? Кажется, этот вопрос возникает довольно часто; надеюсь, будущие проблемы можно было бы сократить, если бы это было интегрировано в документацию?

Это решение не совсем сработало.

Внутри наших тестовых файлов кажется, что global установлен как объект window JSDom.

Другими словами, внутри набора тестов global совпадает с window , но внутри класса, расширяющего JSDOMEnvironment , global поступает из среды Node.

В результате, имея это:

describe("test suite", () => {
  it("should not fail", () => {
    global.jsdom.reconfigure({
      url: "https://www.example.com/"
    });
  });
});

не выполняется, потому что global.jsdom не определено.

Я обошел это, сделав это, но я не особо беспокоюсь об этом.

const JSDOMEnvironment = require("jest-environment-jsdom");

module.exports = class JSDOMEnvironmentGlobal extends JSDOMEnvironment {
  constructor(config) {
    super(config);

    this.dom.window.jsdom = this.dom;
  }
};

В этой среде global.jsdom внутри набора тестов равно this.dom , и приведенный выше набор тестов работает.

Мне кажется, что установка jsdom в качестве собственного свойства window объект неизбежно развалится - есть ли более чистый способ сделать это?

вам нужно написать jsdom вместо global.jsdom в ваших тестах.

@oliverzy Как это?

describe("test suite", () => {
  it("should not fail", () => {
    jsdom.reconfigure({
      url: "https://www.example.com/"
    });
  });
});

Это вызывает jsdom is not defined , но я могу неправильно интерпретировать.

@ simon360 Настройте testEnvironment с кодом из @oliverzy , см. https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

@danielbayerlein в моей конфигурации Jest есть следующее:

"testEnvironment": "@wel-ui/jest-environment-jsdom-global"

где @wel-ui/jest-environment-jsdom-global - имя пакета в нашем монорепозитории. Однако среда используется правильно, потому что решение, устанавливающее jsdom на window работает должным образом.

Кстати, кто-нибудь знает, почему исходное решение не работает в новой версии?
Вот этот:

Object.defineProperty(location, "hostname", {
  value: "example.com",
  writable: true
});

@modestfake, который мы обновили с JSDOM @ 9 до JSDOM @ 11 , я предполагаю, что они изменили способ определения переменной

@SimenB Понятно . Только что нашел описание метода jsdom reconfigure .

Верхнее свойство окна помечено в спецификации [Unforgeable], что означает, что это не настраиваемое собственное свойство и, следовательно, не может быть переопределено или затенено обычным кодом, выполняющимся внутри jsdom, даже с использованием Object.defineProperty.

Я добавил новый репозиторий, чтобы продемонстрировать это поведение. Кто-нибудь может воспроизвести его путем клонирования локально?

https://github.com/simon360/test-environment-for-jest

@ simon360 воспроизведено
image

@ simon360 Я нашел. Вы пропустили ключевое слово this при определении global.jsdom :

const JSDOMEnvironment = require("jest-environment-jsdom");

module.exports = class JSDOMEnvironmentGlobal extends JSDOMEnvironment {
  constructor(config) {
    super(config);

    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;

    return super.teardown();
  }
};

А как насчет location.search ? Я не нашел упоминания об этом https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha Что насчет этого?

jsdom.reconfigure({
  url: 'https://www.example.com/endpoint?queryparam1=15&queryparam2=test'
});

Спасибо @modestfake - извините за

Хорошо, теперь я вижу - this.global в объекте среды Jest устанавливается как global в тестовом файле Jest. В этом есть смысл - спасибо, что помогли мне пройти через это! Если есть достаточный интерес, я мог бы упаковать исправленную версию этого репо и поместить ее в npm как jest-environment-jsdom-global .

Однако я надеюсь, что в будущем в Jest появится более чистый способ сделать это. Это не простой способ изменить window.location -

Может ли быть новый докблок, например, за @jest-environment ? Например...

/**
 * @jest-url https://www.example.com/
 */

Или, может быть, JSDom может быть выставлен в специальной части объекта jest - что-то вроде:

jest.environment.jsdom.reconfigure({
  url: "https://www.example.com/"
});

(что будет иметь дополнительное преимущество в виде возможности изменить window.top )

Мы объединили # 5003. Не уверен, что возможность добавить его как докблок имеет смысл. @cpojer? Мы также можем отказаться от testUrl , поскольку это может быть предоставлено с помощью этой новой опции.

Если будет достаточно интереса, я могу упаковать исправленную версию этого репо и поместить ее в npm как jest-environment-jsdom-global .

Я думаю, что это имеет смысл в любом случае, поскольку он делает больше, чем просто позволяет вам устанавливать URL-адрес - он предоставляет среде полный JSDOM.

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); выдает ту же ошибку, что и window.location . Спасибо за предложение.

Object.defineProperty (window.location, 'href', {
установить: newValue => {currentUrl = newValue; },
});
У меня было это в предыдущих версиях, и теперь выдает ошибку.
Если я добавлю возможность записи: true
выдает другое исключение, которое я не могу указать как для доступа, так и для записи

Я опубликовал на npm новый пакет под названием jest-environment-jsdom-global , который может помочь с проблемами, которые возникают у некоторых людей с Object.defineProperty .

Есть ли у кого-нибудь обходной путь для { writable: true } ?

Например:

Object.defineProperty(window.location, 'href', { writable: true })

...

Object.defineProperty(window.location, 'hash', { writable: true })

...

Object.defineProperty(window.location, 'search', { writable: true })

@danielbayerlein прочитал эту

@modestfake Я уже читал эту ветку, и https://github.com/facebook/jest/issues/5124#issuecomment -352749005 отлично работает. Но у меня есть другой вариант использования. В Jest 21.xx я установил Object.defineProperty(window.location, 'href', { writable: true }) без URL - только { writable: true } . Если я выставлю URL, то в тесте нет смысла.

@danielbayerlein какой

У меня есть функция, которая меняет URL-адрес.

routing.js

...

export function redirectToErrorPage () {
  window.location.href = '/error.html'
}

...

routing.test.js

test('redirect to the error page', () => {
  ...
  expect(window.location.href).toBe('/error.html')
  ...
})

В Jest 21.xx я установил Object.defineProperty(window.location, 'href', { writable: true })

Я бы порекомендовал переключиться на window.location.assign , чтобы вы могли имитировать функцию.

@ simon360 Работает как шарм! Спасибо. 🤝

я использовал

history.pushState({}, "page 2", "/bar.html");

вместе с testURL в конфигурации jest

Часть местоположения - не единственная проблема. Я не могу импортировать jsdom отдельно для вызова jsdom.reconfigureWindow (потому что эта функция больше не существует в последней версии jsdom). Я делал это, чтобы протестировать код, который работает иначе, если window !== top . В последней версии jest больше нет способа добиться этого, в которой используется более новая версия jsdom.

@andyearnshaw метод jsdom.reconfigure имеет в своем объекте элемент windowTop .

Если вы используете среду JSDOM ( jest-environment-jsdom-global ), которую я указал выше, вы можете сделать:

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

в ваших тестах, чтобы выслушать высшую ценность.

Я использую это, и он отлично работает, спасибо!

Вт, 30 января 2018 г., 13:40 simon360, [email protected] написал:

@andyearnshaw https://github.com/andyearnshaw файл jsdom.reconfigure
Метод имеет в своем объекте член windowTop.

Если вы используете среду JSDOM (jest-environment-jsdom-global), я
по ссылке выше, вы можете:

jsdom.reconfigure ({
windowTop: YOUR_VALUE
});

в ваших тестах, чтобы выслушать высшую ценность.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

Использование window.history.pushState и testUrl сработало для меня

https://github.com/facebook/jest/issues/5124#issuecomment -359411593

Собираюсь закрыть это, так как со стороны Jest нечего делать, и существуют обходные пути (не стесняйтесь продолжать обсуждение!)

Обновление с jsdom v8.5.0 до v11.6.2 решило проблему для меня. Итак, мой package.json включает:

"jest": "^21.2.1",
"jest-cli": "^21.2.1",
"jsdom": "^11.6.2",

Он сломается, если я обновлю jest и jest-cli до v22.2.2.

@ andr-3-w какая-то конкретная причина, по которой у вас есть jsdom в вашем package.json? Он идет в комплекте с Jest.

@SimenB , хороший вопрос, нет необходимости в jsdom в нашем package.json . Спасибо!

Итак, без использования jsdom напрямую, вот решение, которое я придумал для своих вещей:

Работает для Jest 21.2.1 (я тестировал на этом):

Зайдите в настройки Jest (например, я буду использовать package.json):

"jest": { "testURL": "http://localhost" }

Теперь вы можете изменить объект window.location, а затем вы можете установить 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);
});

Надеюсь, это кому-то поможет.

Прекратите публиковать это, это не работает на jest ":" ^ 22.4.2 "

@UserNT Я отметил версию, на которой он работает, и широко использую ее в производственных тестах. Если это не работает в более новых версиях, извините, придумайте собственное решение, а не просто случайную трепку.

Просто для полноты, поскольку решение застряло в середине этой цепочки ...

Решение @ petar-prog91 будет работать с Jest 21, но не с Jest 22 с обновленным jsdom .

Чтобы запустить последнюю версию Jest, используйте что-то вроде jest-environment-jsdom-global (полное раскрытие, это мой пакет), чтобы открыть объект jsdom и использовать jsdom.reconfigure , который будет иметь такой же (или, по крайней мере, похожий) эффект.

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 также работает для меня с шуткой 22

@ simon360 Привет, что мне делать, если мне нужно взломать сеттер location.href ?
В моих предыдущих тестах было много подобных тестов, а теперь все провалились ...

const setHrefMockFn = jest.fn();
beforeAll(() => {
  Object.defineProperty(location, "href", {
    get: () => "https://xxxxx",
    set: setHrefMockFn
  });
});
it("xxx", () => {
  //...
  expect(setHrefMockFn.mock.calls[0][0]).toBe(xxx);
});

@ simon360 не могли бы вы привести мне пример, как обновить тест, как показано ниже, с вашей библиотекой

beforeAll(() => {
  =======

  Object.defineProperty(window.location, 'href', {
    writable: true
  });
});

@abhijeetNmishra вы видели документацию ? Думаю, это ответит на ваш вопрос.

@ simon360 Да, исходя из моего понимания документации, требуется около

jsdom.reconfigure({
      url: "https://www.example.com/"
    });

который переопределяет URL-адрес глобально, а не для каждого теста. Пожалуйста помоги!

@abhijeetNmishra Я не уверен, что этот вопрос - лучшее место для обсуждения. Не могли бы вы открыть проблему в репозитории jest-environment-jsdom-global где мы сможем ее решить? Спасибо!

@SimenB указанный обходной путь («используйте jest-environment-jsdom-global ») кажется крайне неоптимальным решением, очевидно, очень распространенной проблемы. Любой, кто обновляется до Jest 22, теперь должен добавить зависимость к этому стороннему пакету и (с точки зрения пользователя) переписать некоторые из своих тестов. Это регресс в поведении Джеста.

Есть ли решение, которое мы могли бы встроить в jest-environment-jsdom по умолчанию? Рад сделать пиар под вашим руководством.

Очень жаль, что кому-то приходится прыгать через это множество обручей только для того, чтобы изменить window.location.href. Я только начал использовать Jest и собираюсь пересмотреть свой выбор фреймворка для тестирования с учетом этой проблемы. Неужели нет лучшего решения, чем предложенные выше уродливые хаки?

@ydogandjiev не стесняйтесь вносить свой вклад в проект, чтобы решить эту проблему. Помните, что это открытый исходный код, поэтому неистовство с комментариями типа «неприемлемо» и «смешно» никому не поможет.

@ msholty-fd Я хотел бы помочь, если смогу. Поскольку я только начинаю работать с jest и jsdom, я не уверен, что имею достаточно глубокое понимание этих библиотек, чтобы точно знать, как лучше всего улучшить этот опыт. Это что-то лучше всего в шутку или в jsdom? Похоже, что одна из этих библиотек в какой-то момент внесла изменения, которые нарушили подход Object.defineProperty; это изменение было сделано в jest или jsdom?

Итак, вот все варианты, которые я считаю предпочтительными в зависимости от количества строк, необходимых для изменения window.location; ни один из них в настоящее время не работает:

  1. Установка href напрямую не работает, потому что jsdom выдает исключение с сообщением «Ошибка: не реализовано: навигация (кроме изменений хэша)»:
    window.location.href = "https://www.example.com";
  1. Использование Object.defineProperty не работает, потому что JSDOM сделал свойство местоположения окна [Unforgeable] :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. Создание экземпляра jsdom и его настройка не работают, потому что jest, похоже, использует свой собственный экземпляр, который недоступен для легкого доступа:

    import { JSDOM } from "jsdom";
    ...
    const dom = new JSDOM();
    dom.reconfigure({ url: "https://www.example.com" });
    

Чтобы заставить работать варианты 1 или 2, jsdom должен отказаться от своих текущих целей и вести себя как настоящий браузер. Следовательно, похоже, что единственный вариант, который у нас есть, - это упростить перенастройку экземпляра jsdom, который использует jest. Имеет ли смысл выставлять этот экземпляр непосредственно на глобальном объекте jest; т.е. разрешая что-то вроде этого:

jest.dom.reconfigure({ url: "https://www.example.com" });

Я все еще думаю, что делать location.assign('some-url') лучше, чем location.href = 'some-url' . Я считаю, что вызов функции более явный, чем назначение, и вы можете издеваться над функцией

@SimenB в тех случаях, когда код пытается _set_ location.href , да, location.assign() лучше. Но если вы тестируете поведение, которое _reads_ location.href , location.assign() не решает проблему, тем более что location.assign() в JSDOM фактически ничего не делает.

Идея использования reconfigure состоит в том, чтобы активировать путь кода, который доступен только тогда, когда location.href сформирован определенным образом. В нашем случае у нас был некоторый код, который изменялся в зависимости от текущего домена - код вонючий, да, но он также необходим, и лучший способ смягчить неприятный запах кода - иметь тестовые инструменты, которые фиксируют поведение и гарантируют, что оно остается. на месте.

Есть ли способ использовать это с Enzyme и установленным компонентом для проверки перенаправления?

Перед обновлением Jest прошел тест ниже:

`` ''
it ('маршруты к правильному маршруту', () => {

Object.defineProperty(window.location, 'href', {
  writable: true,
  value: 'https://mysuperawesomesite.com/',
});

const component = mount(
  <App {...props} />
);

const link = component.find('.class');

link.simulate('click');

expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');

});

After upgrading Jest and implementing [jest-environment-jsdom-global](https://www.npmjs.com/package/jest-environment-jsdom-global), I tried the following to no avail:

  ```
it('routes to correct route', () => {

    jsdom.reconfigure({
      url: 'https://mysuperawesomesite.com/',
    });

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');
  });

(window.location.href по-прежнему равно https://mysuperawesomesite.com/ , не был изменен на (https://mysuperawesomesite.com/new).

Событие щелчка на элементе не перенаправляет при использовании этого метода, а перенаправление происходит путем установки window.location.href.

Непонятно, как правильно это проверить, или если тесты, в которых ранее использовался Object.defineProperty, изначально были плохо построены. Спасибо заранее за любую помощь.

РЕДАКТИРОВАТЬ: РЕШЕНО

Удалось решить эту проблему, используя window.location.assign (url) вместо window.location.href = href. Это позволило мне отключить метод assign и проверить, правильно ли он вызывается. Увидеть ниже:

it('routes to correct route', () => {
    window.location.assign = jest.fn();

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.assign).toBeCalledWith('https://mysuperawesomesite.com/new');
    window.location.assign.mockRestore();
  });

@SimenB Существует большая разница между .assign и .href вы можете прочитать на MDN. Первый имеет серьезное междоменное ограничение. В моем коде я хочу перенаправить родительскую страницу из iframe, в котором выполняется мой код. Это кросс-домены. И единственный способ сделать это - изменить href .
Мне бы очень хотелось, чтобы эта проблема была открыта повторно, так как я не вижу текущего обходного пути, и мне придется просто не тестировать эту функцию. Что явно отстой.

Я в одной лодке с @soswow. Было бы здорово, если бы вы могли предоставить механизм для переопределения URL-адреса, так как я также удаляю несколько модульных тестов, пока эта функциональность не будет восстановлена.

Мне бы очень хотелось, чтобы эта проблема была открыта повторно, так как я не вижу текущего обходного пути, и мне придется просто не тестировать эту функцию. Что явно отстой.

Мы ничего не можем сделать со стороны шутки. Я уверен, что jsdom хотел бы, чтобы его поддержали пиарщиком. https://github.com/jsdom/jsdom/issues/2112

Вот простое решение, которое работает.

describe('changing location', () => {
  const testURL = location.href;

  beforeEach(() => history.replaceState({}, 'Login', '/login'));
  afterEach(() => history.replaceState({}, 'Home', '/'));

  it('works', () => {
    expect(location.pathname).toBe('/login');
  });
});

@vastus Как я прямо указал - проблема в кросс-доменах . History API не позволяет переключиться на другой домен, насколько я помню.

Поскольку location не может быть переопределено непосредственно на объекте jsdom window , один из возможных подходов - переопределить его на производном объекте:

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

Я использую это для решения проблемы:

const windowLocation = JSON.stringify(window.location);
delete window.location;
Object.defineProperty(window, 'location', {
  value: JSON.parse(windowLocation)
});

В духе @RubenVerborgh & annemarie35

Добавляем пример тестирования location.search на основе решения @vastus :

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

@RubenVerborgh Прекрасно работает.

пытаться:

window.history.pushState({}, null, '/pathname?k=v');

Решение, подобное @sahalsaad :
`` javascript
const oldWindow = window.location;
удалить window.location;
window.location = {
... oldWindow,
// включаем любые пользовательские перезаписи, такие как следующая заглушка sinon
заменить: sinon.stub (),
};

// творим чудеса

window.location = oldWindow;
`` ''

@sahalsaad, спасибо! Я использовал вариант вашего решения для имитации window.location.search:

const location = {
    ...window.location,
    search: queryString,
};
Object.defineProperty(window, 'location', {
    writable: true,
    value: location,
});

возможно лучшее решение:

import { URL } from 'whatwg-url';

const location = new URL(window.location.href);
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()

delete window.location
window.location = location

Решил мою проблему, используя решение, данное @kdelmonte , мне пришлось издеваться над переменной window.location.search . так что я использовал

window.history.pushState({}, null, '?skuId=1234')

Поскольку location не может быть переопределено непосредственно на объекте jsdom window , один из возможных подходов - переопределить его на производном объекте:

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

Ваш ответ - единственный, который работает в моей ситуации, спасибо!

Поскольку location не может быть переопределено непосредственно на объекте jsdom window , один из возможных подходов - переопределить его на производном объекте:

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

Ваш ответ - единственный, который работает в моей ситуации, спасибо!

Это больше не работает 😢

Поскольку location не может быть переопределено непосредственно на объекте jsdom window , один из возможных подходов - переопределить его на производном объекте:

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

Ваш ответ - единственный, который работает в моей ситуации, спасибо!

Это больше не работает 😢

У меня тоже не работает

Я решил это сделать:

delete window.location
window.location = {
  href: 'http://example.org/,
}

Я использую следующий макет в качестве утилиты для работы с Location

export class MockLocation extends URL implements Location {
  ancestorOrigins: any = []
  toString = jest.fn().mockImplementation(() => this.toString())
  assign = jest.fn(href => this.href = href)
  replace = jest.fn(href => this.href = href)
  reload = jest.fn()

  constructor(
    url: string = 'http://mock.localhost',
  ) {
    super(url)
  }

  onWindow(window: Window) {
    Object.defineProperty(window, 'location', { 
      writable: true,
      value: this
    });
    return this
  }
}

Тогда в моих тестах

let location: MockLocation

beforeEach(() => {
    location = new MockLocation(MOCK_PARTNER_URL).onWindow(window)
})

Я обнаружил, что постоянно заглушаю такие хитрые объекты, и создал гибкую вспомогательную функцию:

export const safelyStubAndThenCleanup = (target, method, value) => {
  const original = target[method]
  beforeEach(() => {
    Object.defineProperty(target, method, { configurable: true, value })
  })
  afterEach(() => {
    Object.defineProperty(target, method, { configurable: true, value: original })
  })
}

А затем использование:

describe('when on /pages', () => {
  safelyStubAndThenCleanup(window, 'location', { pathname: '/pages' })

  it('should do something neat', () => { /* ... */ })
})

И вы можете заглушить все, что захотите: pathname , href и т. Д. Это дает вам дополнительное бесплатное преимущество очистки.

Ключ в том, что вы не можете связываться с самим location , поэтому просто замените location подделкой, а затем верните ее, когда тест будет завершен.

Как я вижу в своем сеансе отладки, global.location реализуется через геттер, а не через простое свойство. Разве не было бы безопаснее переопределить это так?

let originalLocationDescriptor;
beforeAll(() => {
  originalLocationDescriptor = Object.getOwnPropertyDescriptor(global, 'location');
  delete global.location;
  global.location = {};
});
afterAll(() => {
  Object.defineProperty(global, 'location', originalLocationDescriptor);
}):

Хотя трудно представить, зачем мне использовать исходный global.location , это кажется немного более правильным.
И этот код, конечно, мне подходит. Я просто обращаюсь к location.pathname , но этот объект можно легко расширить с помощью jest.fn() если это необходимо.

Это сработало для меня, используя jest 26.5

function stubLocation(location) {
  beforeEach(() => {
    jest.spyOn(window, "location", "get").mockReturnValue({
      ...window.location,
      ...location,
    });
  });
}

stubLocation({ pathname: "/facebook/jest/issues/5124" });

test("mocks location prop", () => {
  expect(window.location.pathname).toEqual("/facebook/jest/issues/5124");
});

Добавляем пример тестирования location.search на основе решения @vastus :

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

Это отлично сработало для моей проблемы.

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