Jest: Window.location kann nicht mit Object.defineProperty geändert werden

Erstellt am 19. Dez. 2017  ·  78Kommentare  ·  Quelle: facebook/jest

Möchten Sie ein _Feature_ anfordern oder einen _Bug_ melden? Melde einen technischen Fehler

Wie ist das aktuelle Verhalten?

Aufrufen des Folgenden aus einer Testsuite heraus:

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

wirft folgenden Fehler aus:

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

Was ist das erwartete Verhalten?

Der Code sollte keine Ausnahme auslösen und window.location.hostname === "example.com" sollte als wahr ausgewertet werden.

So wie es aussieht, setzt jsdom window.location jetzt window.location zu ändern, besteht darin, reconfigure , aber (gemäß #2460) stellt Jest jsdom für Tests zum Herumspielen bereit.

Bitte geben Sie Ihre genaue Jest-Konfiguration an und erwähnen Sie Ihren Jest, Knoten,Garn/npm-Version und Betriebssystem.

Jest Version: 22.0.1
Node -
Garnversion : 1.2.0
Betriebssystem : macOS High Sierra 10.13.2

Discussion Wontfix

Hilfreichster Kommentar

Ich habe auf npm ein neues Paket namens jest-environment-jsdom-global , das bei den Problemen helfen könnte, die manche Leute mit Object.defineProperty .

Alle 78 Kommentare

Ich habe das ähnliche Problem. Sie können Ihr eigenes JSDOMEnvironment erstellen und das jsdom-Objekt so für das Globale verfügbar machen.

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();
  }
};

Und dann kannst du jsdom.reconfigure in deinem Testfall nach Belieben aufrufen

Das ist ein guter Workaround, danke fürs Teilen!

Sie sollten super.teardown(); da es ein Versprechen ist, übrigens

Perfekt, @oliverzy - das probiere ich mal aus. Danke!

Gibt es eine geeignete Stelle, um dies zu dokumentieren? Es scheint eine Frage zu sein, die ziemlich oft auftaucht; hoffentlich könnten zukünftige Ausgaben reduziert werden, wenn dies in die Dokumentation integriert würde?

Diese Lösung hat nicht ganz funktioniert.

In unseren Testdateien scheint global das window Objekt von JSDom zu sein.

Mit anderen Worten, innerhalb einer Testsuite ist global dasselbe wie window , aber innerhalb der Klasse, die JSDOMEnvironment , kommt global aus der Node-Umgebung.

Als Ergebnis haben Sie dies:

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

schlägt fehl, weil global.jsdom definiert ist.

Ich habe es damit umgangen, aber ich bin nicht so aufgeregt.

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

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

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

In dieser Umgebung entspricht global.jsdom innerhalb einer Testsuite this.dom , und die obige Testsuite funktioniert.

Für mich fühlt es sich so an, als ob jsdom als Eigenschaft eines eigenen window Objekts festgelegt wird, das irgendwann auseinanderfallen wird - gibt es einen saubereren Weg, dies zu tun?

Sie müssen in Ihren Tests jsdom statt global.jsdom schreiben.

@oliverzy Gefällt

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

Das wirft jsdom is not defined , aber ich kann es falsch interpretieren.

@simon360 Bitte konfiguriere das testEnvironment mit dem Code von @oliverzy , siehe https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

@danielbayerlein meine Jest-Konfiguration hat das:

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

wobei @wel-ui/jest-environment-jsdom-global der Name eines Pakets in unserem Monorepo ist. Die Umgebung wird jedoch richtig verwendet, da die Lösung, die jsdom auf window setzt, wie erwartet funktioniert.

Übrigens, weiß jemand, warum die ursprüngliche Lösung in der neuen Version nicht funktioniert?
Dieses:

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

@modestfake wir haben von JSDOM@9 auf JSDOM@11 aktualisiert , ich vermute, dass sie die Definition der Variablen geändert haben

@SimenB Verstanden . Habe gerade eine Beschreibung der Methode jsdom reconfigure .

Die oberste Eigenschaft im Fenster ist in der Spezifikation als [Unforgeable] markiert, was bedeutet, dass sie eine nicht konfigurierbare eigene Eigenschaft ist und daher nicht von normalem Code, der innerhalb des jsdom läuft, überschrieben oder überschattet werden kann, selbst wenn Object.defineProperty verwendet wird.

Ich habe ein neues Repository hinzugefügt, um dieses Verhalten zu demonstrieren. Kann es jemand durch lokales Klonen reproduzieren?

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

@simon360 reproduziert
image

@simon360 habe ich gefunden. Sie haben das Schlüsselwort this bei der Definition von global.jsdom verpasst:

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();
  }
};

Was ist mit location.search ? Ich habe keine Erwähnung darüber gefunden https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha Was ist damit?

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

Danke @modestfake - sorry für den dummen Fehler!

Ok, ich sehe es jetzt - this.global für ein Jest-Umgebungsobjekt wird in einer Jest-Testdatei als global . Das macht Sinn - danke, dass du mir dabei geholfen hast! Wenn genügend Interesse besteht, könnte ich die reparierte Version dieses Repositorys verpacken und auf npm als jest-environment-jsdom-global .

Ich hoffe jedoch, dass es in Zukunft einen saubereren Weg gibt, dies in Jest zu tun. Dies ist kein reibungsarmer Weg, um window.location zu ändern -

Könnte es einen neuen Docblock geben, wie es für @jest-environment Fall ist? Beispielsweise...

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

Oder vielleicht kann JSDom in einem speziellen Teil des jest -Objekts verfügbar gemacht werden - etwa so:

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

(was den zusätzlichen Vorteil hätte, window.top ändern zu können)

Wir haben #5003 jetzt zusammengeführt. in der Lage zu sein, es als Docblock hinzuzufügen, kann sinnvoll sein, bin mir nicht sicher. @cpojer? Wir könnten auch testUrl verwerfen, da es über diese neue Option bereitgestellt werden kann.

Wenn genügend Interesse besteht, könnte ich die reparierte Version dieses Repositorys verpacken und als jest-environment-jsdom-global auf npm jest-environment-jsdom-global .

Ich denke, das macht auf jeden Fall Sinn, da es mehr ermöglicht, als nur die URL zu setzen - es stellt das gesamte JSDOM der Umgebung zur Verfügung

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); wirft den gleichen Fehler wie window.location . Danke aber für den Vorschlag.

Object.defineProperty(window.location, 'href', {
set: newValue => { currentUrl = newValue; },
});
Ich hatte dies in früheren Versionen und wirft jetzt einen Fehler.
Wenn ich beschreibbar hinzufüge: true
löst eine weitere Ausnahme aus, die ich nicht sowohl als Accessor als auch als beschreibbar angeben kann

Ich habe auf npm ein neues Paket namens jest-environment-jsdom-global , das bei den Problemen helfen könnte, die manche Leute mit Object.defineProperty .

Hat jemand einen Workaround für { writable: true } ?

Beispielsweise:

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

...

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

...

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

@danielbayerlein habe diesen Thread gelesen. Sie müssen eine benutzerdefinierte Umgebung erstellen. Vorherige Nachricht enthält URL mit Beispiel

@modestfake Ich habe diesen Thread bereits gelesen und https://github.com/facebook/jest/issues/5124#issuecomment -352749005 funktioniert einwandfrei. Aber ich habe noch einen anderen Anwendungsfall. Bei Jest 21.xx habe ich Object.defineProperty(window.location, 'href', { writable: true }) ohne die URL gesetzt - nur { writable: true } . Wenn ich die URL setze, dann macht der Test keinen Sinn.

@danielbayerlein, was ist der Anwendungsfall, um es beschreibbar zu machen, aber nicht zu überschreiben? Vielleicht kann mir das Verständnis helfen, einen Workaround zu finden

Ich habe eine Funktion, die die URL ändert.

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')
  ...
})

Mit Jest 21.xx habe ich Object.defineProperty(window.location, 'href', { writable: true })

Ich würde empfehlen, zu window.location.assign wechseln, damit Sie die Funktion verspotten können.

@simon360 Funktioniert wie ein Zauber! Danke schön. 🤝.

ich benutzte

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

zusammen mit testURL in der Scherzkonfiguration

Der Standortteil ist nicht das einzige Problem. Ich kann nicht importieren jsdom separat anrufen jsdom.reconfigureWindow (da diese Funktion nicht mehr existiert in der aktuellen Version von jsdom). Ich habe dies getan, um Code zu testen, der anders läuft, wenn window !== top . In der neuesten Version von jest, die eine neuere Version von jsdom verwendet, ist dies nicht mehr möglich.

@andyearnshaw Die Methode jsdom.reconfigure hat ein Member windowTop in ihrem Objekt.

Wenn Sie die oben verlinkte JSDOM-Umgebung ( jest-environment-jsdom-global ) verwenden, können Sie Folgendes tun:

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

in Ihren Tests einen Spitzenwert zu verspotten.

Ich benutze das und es funktioniert super, danke!

Am Di, 30.01.2018, 13:40 Uhr schrieb simon360, [email protected] :

@andyearnshaw https://github.com/andyearnshaw die jsdom.reconfigure
-Methode hat ein WindowTop-Member in ihrem Objekt.

Wenn Sie die JSDOM-Umgebung verwenden (jest-environment-jsdom-global)
oben verlinkt, könnten Sie tun:

jsdom.reconfigure({
FensterTop: YOUR_VALUE
});

in Ihren Tests einen Spitzenwert zu verspotten.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

Die Verwendung von window.history.pushState und testUrl hat bei mir funktioniert

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

Ich werde dies schließen, da auf Jests Seite nichts zu tun ist und es Problemumgehungen gibt (Sie können die Diskussion gerne fortsetzen!)

Ein Upgrade von jsdom v8.5.0 auf v11.6.2 hat das Problem für mich gelöst. Mein package.json beinhaltet also:

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

Es bricht ab, wenn ich jest und jest-cli auf v22.2.2 upgrade.

@andr-3-w einen bestimmten Grund, warum Sie jsdom in Ihrem package.json haben? Es kommt mit Jest gebündelt.

@SimenB , gute Frage, es gibt keine Notwendigkeit für jsdom in unserem package.json . Danke!

Also, ohne jsdom direkt zu verwenden, ist hier die Lösung, die ich für meine Sachen gefunden habe:

Funktioniert für Jest 21.2.1 (ich habe das getestet):

Gehen Sie in Ihre Jest-Einstellungen (zum Beispiel verwende ich package.json):

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

Jetzt können Sie das window.location-Objekt ändern und dann die URL während der Tests beliebig einstellen.

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);
});

Hoffentlich hilft das jemandem.

Hör auf, das zu posten, es funktioniert nicht bei Scherz: "^22.4.2"

@UserNT Ich habe die Version notiert, auf der es hier funktioniert, und verwende sie ausgiebig für Produktionstestanzüge. Wenn es auf einer neueren Version nicht funktioniert, entschuldige bitte, überlege dir deine eigene Lösung, anstatt nur zufälliges Bashing.

Nur der Vollständigkeit halber, da die Lösung mitten in diesem Thread gestrandet ist...

Die Lösung von @petar-prog91 funktioniert auf Jest 21, aber nicht auf Jest 22 mit dem aktualisierten jsdom .

Um auf dem neuesten Jest zu laufen, verwenden Sie etwas wie jest-environment-jsdom-global (vollständige Offenlegung, dies ist mein Paket), um das jsdom Objekt verfügbar zu machen, und verwenden Sie jsdom.reconfigure , das die gleiche (oder zumindest ähnliche) Wirkung.

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 funktioniert bei mir auch auf jest 22

@simon360 Hallo, was soll ich tun, wenn ich den Setter von location.href hacken muss?
Meine vorherigen Tests haben viele Tests wie diese und jetzt sind alle fehlgeschlagen...

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 können Sie mir ein Beispiel geben, wie Sie den Test wie unten mit Ihrer Bibliothek ?

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

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

@abhijeetNmishra hast du die Dokumentation gesehen ? Ich glaube, das würde deine Frage beantworten.

@simon360 Ja, nach meinem Verständnis von Dokumentation dauert es ungefähr

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

was die URL global überschreibt und nicht pro Test. Bitte helfen Sie!

@abhijeetNmishra Ich bin mir nicht sicher, ob dieses Thema der beste Ort ist, um darüber zu diskutieren. Würde es Ihnen etwas ausmachen, ein Problem im jest-environment-jsdom-global Repository zu öffnen, in dem wir es bearbeiten können? Danke!

@SimenB die angegebene jest-environment-jsdom-global ") fühlt sich wie eine extrem suboptimale Lösung für ein offensichtlich sehr häufiges Problem an. Jeder, der auf Jest 22 aktualisiert, muss jetzt eine Abhängigkeit zu diesem Drittanbieterpaket hinzufügen und (aus der Sicht eines Benutzers) einige seiner Tests neu schreiben. Dies ist eine Regression in Jests Verhalten.

Gibt es eine Lösung dafür, die wir in die Standardeinstellung jest-environment-jsdom einbauen könnten? Gerne eine PR mit Ihrer Anleitung.

Es ist sehr bedauerlich, dass jemand durch so viele Reifen springen muss, nur um window.location.href zu ändern. Ich habe gerade angefangen, Jest zu verwenden, und bin dabei, meine Wahl des Testframeworks angesichts dieses Problems zu überdenken. Gibt es wirklich keine bessere Lösung als die oben vorgeschlagenen hässlichen Hacks?

@ydogandjiev können Sie gerne zum Projekt beitragen, um dieses Problem zu lösen. Denken Sie daran, dass dies Open Source ist. Wenn Sie also mit Kommentaren wie "inakzeptabel" und "lächerlich" randalieren, hilft niemandem.

@msholty-fd Ich würde gerne helfen, wenn ich kann. Da ich gerade erst mit jest und jsdom anfing, bin ich mir nicht sicher, ob ich ein tiefes Verständnis dieser Bibliotheken habe, um genau zu wissen, was der beste Weg zur Verbesserung dieser Erfahrung ist. Ist das am besten im Scherz oder im Jsdom angesprochen? Anscheinend hat eine dieser Bibliotheken irgendwann eine Änderung vorgenommen, die den Object.defineProperty-Ansatz durchbrochen hat; War das eine Änderung im Scherz oder im Jsdom?

Hier sind also alle Optionen, die ich basierend auf der Anzahl der Zeilen, die zum Ändern von window.location erforderlich sind, für besser halten würde; keines davon funktioniert derzeit:

  1. Das direkte Festlegen von href funktioniert nicht, da jsdom eine Ausnahme mit der Meldung "Fehler: Nicht implementiert: Navigation (außer Hash-Änderungen)" auslöst:
    window.location.href = "https://www.example.com";
  1. Die Verwendung von Object.defineProperty funktioniert nicht, da JSDOM die Positionseigenschaft des Fensters zu [Unforgeable] gemacht hat :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. Das Erstellen einer Instanz von jsdom und deren Konfiguration funktioniert nicht, da jest eine eigene Instanz zu verwenden scheint, die nicht für den einfachen Zugriff verfügbar gemacht wird:

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

Damit die Optionen 1 oder 2 funktionieren, müsste jsdom seine aktuellen Ziele, sich eher wie ein echter Browser zu verhalten, zurückverfolgen. Daher scheint es die einzige Option zu sein, die wir haben, die Neukonfiguration der von jest verwendeten Instanz von jsdom zu vereinfachen. Wäre es sinnvoll, diese Instanz direkt auf dem globalen Jest-Objekt verfügbar zu machen; dh so etwas zulassen:

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

Ich denke immer noch, dass location.assign('some-url') besser ist als location.href = 'some-url' . Ich finde einen Funktionsaufruf expliziter als eine Zuweisung, und Sie können die Funktion verspotten

@SimenB in Fällen, in denen Code versucht, location.href zu _set_, ja, location.assign() ist besser. Aber wenn Sie ein Verhalten testen, das location.href _liest, löst location.assign() das Problem nicht, zumal location.assign() in JSDOM eigentlich nichts tut.

Die Idee hinter der Verwendung von reconfigure besteht darin, einen Codepfad zu aktivieren, der nur aktiviert wird, wenn location.href auf eine bestimmte Weise gebildet wird. In unserem Fall hatten wir Code, der sich abhängig von der aktuellen Domäne geändert hat - der Code stinkt zwar, aber es ist auch notwendig, und der beste Weg, um stinkenden Code zu mildern, besteht darin, Testvorrichtungen zu verwenden, die das Verhalten erfassen und sicherstellen, dass es bleibt an Ort und Stelle.

Gibt es eine Möglichkeit, dies mit Enzyme und einer gemounteten Komponente zu verwenden, um auf Weiterleitungen zu testen?

Der folgende Test wurde vor dem Upgrade von Jest bestanden:

```
it('Routen zur korrekten Route', () => {

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 entspricht immer noch ' https://mysuperawesomesite.com/ ', wurde nicht geändert in ('https://mysuperawesomesite.com/new').

Das Click-Ereignis für das Element leitet bei Verwendung dieser Methode nicht um, und die Umleitung erfolgt durch Einstellen von window.location.href.

Unklar, wie dies richtig getestet wird oder ob die Tests, die zuvor Object.defineProperty verwendet hatten, von Anfang an schlecht konstruiert waren. Vielen Dank im Voraus für jede Hilfe.

EDIT: GELÖST

Konnte dies durch die Verwendung von window.location.assign(url) anstelle von window.location.href = href lösen. Dadurch konnte ich die Methode zuweisen und testen, ob sie richtig aufgerufen wurde. Siehe unten:

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 Es gibt einen großen Unterschied zwischen .assign und .href Sie können auf MDN lesen. Erstens hat man eine große domänenübergreifende Einschränkung. In meinem Code möchte ich die übergeordnete Seite von einem iframe umleiten, auf dem mein Code ausgeführt wird. Das sind domänenübergreifend. Und das kann ich nur tun, indem ich href .
Ich würde mich freuen, wenn dieses Problem erneut geöffnet wird, da ich keinen aktuellen Workaround sehe und ich diese Funktion einfach nicht testen muss. Was offensichtlich scheiße ist.

Ich sitze im selben Boot wie @soswow. Es wäre großartig, wenn Sie einen Mechanismus zum Überschreiben der URL bereitstellen könnten, da ich auch mehrere Komponententests entferne, bis diese Funktionalität wiederhergestellt ist.

Ich würde mich freuen, wenn dieses Problem erneut geöffnet wird, da ich keinen aktuellen Workaround sehe und ich diese Funktion einfach nicht testen muss. Was offensichtlich scheiße ist.

Es gibt nichts, was wir auf der Seite des Scherzes tun können. Ich bin sicher, jsdom würde sich über eine PR-Unterstützung freuen. https://github.com/jsdom/jsdom/issues/2112

Hier ist eine einfache Lösung, die funktioniert.

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

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

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

@vastus Wie ich ausdrücklich darauf hingewiesen - das Problem mit Cross-Domains ist. History API erlaubt es nicht, zu einer anderen Domain zu wechseln, soweit ich mich erinnere.

Da location nicht direkt auf dem jsdom window Objekt überschrieben werden kann, besteht ein möglicher Ansatz darin, es auf einem abgeleiteten Objekt zu überschreiben:

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

Ich benutze dies, um das Problem zu lösen:

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

Inspiriert von @RubenVerborgh & annemarie35

Ein Beispiel für das Testen von location.search basierend auf der Lösung von @vastus hinzufügen :

  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 Funktioniert wie ein Zauber.

Versuchen:

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

Eine ähnliche Lösung wie @sahalsaad :
```javascript
const oldWindow = window.location;
lösche window.location;
Fenster.Standort = {
...altesFenster,
// füge alle benutzerdefinierten Überschreibungen ein, wie den folgenden Sinon-Stub
ersetzen: sinon.stub(),
};

// mach deine Magie

window.location = oldWindow;
````

@sahalsaad danke! Ich habe eine Variation Ihrer Lösung verwendet, um window.location.search zu verspotten:

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

wahrscheinlich eine bessere lösung:

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

Ich habe mein Problem mit der von @kdelmonte angegebenen Lösung window.location.search verspotten. also habe ich verwendet

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

Da location nicht direkt auf dem jsdom window Objekt überschrieben werden kann, besteht ein möglicher Ansatz darin, es auf einem abgeleiteten Objekt zu überschreiben:

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

Ihre Antwort ist die einzige, die für meine Situation funktioniert, danke!

Da location nicht direkt auf dem jsdom window Objekt überschrieben werden kann, besteht ein möglicher Ansatz darin, es auf einem abgeleiteten Objekt zu überschreiben:

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

Ihre Antwort ist die einzige, die für meine Situation funktioniert, danke!

Das geht nicht mehr 😢

Da location nicht direkt auf dem jsdom window Objekt überschrieben werden kann, besteht ein möglicher Ansatz darin, es auf einem abgeleiteten Objekt zu überschreiben:

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

Ihre Antwort ist die einzige, die für meine Situation funktioniert, danke!

Das geht nicht mehr 😢

Funktioniert bei mir auch nicht

Ich habe das so gelöst:

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

Ich verwende den folgenden Mock als Dienstprogramm für die Arbeit mit 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
  }
}

Dann in meinen Tests

let location: MockLocation

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

Ich ertappte mich dabei, wie ich die ganze Zeit knifflige Objekte wie diese stummte und eine flexible Hilfsfunktion erstellte:

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 })
  })
}

Und dann Verwendung:

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

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

Und Sie können stuben, was immer Sie wollen: pathname , href usw. Dadurch erhalten Sie den zusätzlichen kostenlosen Vorteil der Bereinigung.

Der Schlüssel ist, dass Sie sich nicht mit location selbst anlegen können, also tauschen Sie einfach location gegen eine Fälschung aus und legen Sie sie dann zurück, wenn der Test abgeschlossen ist.

Wie ich in meiner Debugging-Sitzung sehen kann, wird global.location über Getter implementiert, aber keine einfache Eigenschaft. Wäre es nicht sicherer, es so neu zu definieren?

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

Obwohl es schwer vorstellbar ist, warum ich das ursprüngliche global.location , scheint es nur ein bisschen richtiger zu sein.
Und dieser Code funktioniert natürlich gut für mich. Ich greife nur auf location.pathname , aber dieses Objekt kann bei Bedarf leicht um einige jest.fn() werden.

Das hat bei mir funktioniert, mit 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");
});

Ein Beispiel für das Testen von location.search basierend auf der Lösung von @vastus hinzufügen :

  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');
  });

Das hat bei meinem Problem genau funktioniert

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen