Jest: Impossible de modifier window.location en utilisant Object.defineProperty

Créé le 19 déc. 2017  ·  78Commentaires  ·  Source: facebook/jest

Voulez-vous demander une _fonctionnalité_ ou signaler un _bug_ ? Signaler un bug

Quel est le comportement actuel ?

Appeler les éléments suivants depuis une suite de tests :

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

renvoie l'erreur suivante :

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

Quel est le comportement attendu ?

Le code ne doit pas lever d'exception et window.location.hostname === "example.com" doit évaluer vrai.

À première vue, jsdom définit maintenant window.location est d'utiliser reconfigure , mais (par #2460) Jest n'expose pas jsdom pour les tests avec lesquels jouer.

Veuillez fournir votre configuration Jest exacte et mentionner votre Jest, nœud,version et système d'exploitation de fil/npm.

Version du jeu : 22.0.1
Version du nœud : 8.6.0
Version du fil : 1.2.0
OS : macOS High Sierra 10.13.2

Discussion Wontfix

Commentaire le plus utile

J'ai publié un nouveau package sur npm appelé jest-environment-jsdom-global , qui peut aider à résoudre les problèmes que certaines personnes rencontrent avec Object.defineProperty .

Tous les 78 commentaires

J'ai le même problème. Vous pouvez créer votre propre environnement JSDOM et exposer l'objet jsdom au global comme ceci.

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

Et puis vous pouvez appeler jsdom.reconfigure dans votre cas de test comme vous le souhaitez

C'est une bonne solution de contournement, merci pour le partage!

Vous devriez retourner super.teardown(); car c'est une promesse, d'ailleurs

Parfait, @oliverzy - je vais essayer ça. Merci!

Existe-t-il un endroit approprié pour documenter cela? Cela semble être une question qui revient assez souvent ; avec un peu de chance, les problèmes futurs pourraient être réduits si cela était intégré dans la documentation ?

Cette solution n'a pas tout à fait fonctionné.

Dans nos fichiers de test, il semble que global soit défini comme l'objet window JSDom.

En d'autres termes, à l'intérieur d'une suite de tests, global est identique à window , mais à l'intérieur de la classe qui étend JSDOMEnvironment , global vient de l'environnement de Node.

En conséquence, avoir ceci :

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

échoue car global.jsdom n'est pas défini.

Je l'ai contourné en faisant cela, mais je ne suis pas très inquiet à ce sujet.

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

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

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

Avec cet environnement, global.jsdom dans une suite de tests est égal à this.dom , et la suite de tests ci-dessus fonctionne.

Pour moi, j'ai l'impression que définir jsdom comme propriété de son propre objet window finira par s'effondrer - existe-t-il un moyen plus propre de le faire ?

vous devez écrire jsdom plutôt que global.jsdom dans vos tests.

@oliverzy Vous aimez ça ?

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

Cela renvoie jsdom is not defined , mais je me trompe peut-être.

@simon360 Veuillez configurer le testEnvironment avec le code de @oliverzy , voir https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

@danielbayerlein ma configuration Jest a ceci:

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

@wel-ui/jest-environment-jsdom-global est le nom d'un package dans notre monorepo. L'environnement est utilisé correctement, cependant, car la solution qui définit jsdom sur window fonctionne comme prévu.

BTW, est-ce que quelqu'un sait pourquoi la solution d'origine ne fonctionne pas dans la nouvelle version ?
Celui-là:

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

@modestfake nous avons mis à niveau de JSDOM@9 à JSDOM@11 , je suppose qu'ils ont changé la façon dont la variable est définie

@SimenB Compris . Je viens de trouver une description de la méthode jsdom reconfigure .

La propriété supérieure sur la fenêtre est marquée [Unforgeable] dans la spécification, ce qui signifie qu'il s'agit d'une propriété propre non configurable et ne peut donc pas être remplacée ou masquée par le code normal exécuté à l'intérieur du jsdom, même en utilisant Object.defineProperty.

J'ai ajouté un nouveau référentiel pour démontrer ce comportement. Quelqu'un est-il capable de le reproduire en le clonant localement ?

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

@simon360 reproduit
image

@simon360 J'ai trouvé. Vous avez manqué le mot-clé this lors de la définition de 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();
  }
};

Qu'en est-il de location.search ? Je n'ai trouvé aucune mention à ce sujet https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha Et ça ?

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

Merci @modestfake - désolé pour l'erreur stupide !

Ok, je le vois maintenant - this.global sur un objet d'environnement Jest est défini comme global dans un fichier de test Jest. C'est logique - merci de m'avoir aidé à traverser ça ! S'il y a suffisamment d'intérêt, je pourrais empaqueter la version réparée de ce dépôt et la mettre sur npm tant que jest-environment-jsdom-global .

Cependant, j'espère qu'il y aura un moyen plus propre de le faire dans Jest à l'avenir. Ce n'est pas une façon à faible friction de changer window.location -

Pourrait-il y avoir un nouveau docblock, comme pour @jest-environment ? Par example...

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

Ou, peut-être que JSDom peut être exposé sur une partie spéciale de l'objet jest - quelque chose comme :

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

(ce qui aurait l'avantage supplémentaire de pouvoir changer window.top )

Nous avons fusionné #5003 maintenant. pouvoir l'ajouter en tant que docblock pourrait avoir du sens, pas sûr. @cpojer ? Nous pourrions également déprécier testUrl , car il peut être fourni via cette nouvelle option.

S'il y a suffisamment d'intérêt, je pourrais empaqueter la version réparée de ce dépôt et la mettre sur npm tant que jest-environment-jsdom-global .

Je pense que cela a du sens dans tous les cas, car cela fait plus que simplement vous permettre de définir l'URL - cela expose le JSDOM complet à l'environnement

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); renvoie la même erreur que window.location . Merci pour la suggestion cependant.

Object.defineProperty(window.location, 'href', {
set : newValue => { currentUrl = newValue ; },
});
J'avais cela dans les versions précédentes et je génère maintenant une erreur.
Si j'ajoute accessible en écriture : vrai
lève une autre exception que je ne peux pas spécifier à la fois accesseur et inscriptible

J'ai publié un nouveau package sur npm appelé jest-environment-jsdom-global , qui peut aider à résoudre les problèmes que certaines personnes rencontrent avec Object.defineProperty .

Quelqu'un a-t-il une solution de contournement pour { writable: true } ?

Par example:

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

...

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

...

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

@danielbayerlein a lu ce fil. Vous devez créer un environnement personnalisé. Le message précédent contient une URL avec un exemple

@modestfake J'ai déjà lu ce fil et https://github.com/facebook/jest/issues/5124#issuecomment -352749005 fonctionne bien. Mais j'ai un autre cas d'utilisation. Avec Jest 21.xx, j'ai défini Object.defineProperty(window.location, 'href', { writable: true }) sans l'URL - seulement { writable: true } . Si je définis l'URL, le test n'a aucun sens.

@danielbayerlein quel est le cas d'utilisation pour le rendre accessible en écriture mais pas le remplacer en fait? Peut-être que comprendre cela peut m'aider à trouver une solution de contournement

J'ai une fonction qui change l'URL.

routage.js

...

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

...

routage.test.js

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

Avec Jest 21.xx j'ai mis Object.defineProperty(window.location, 'href', { writable: true })

Je recommanderais de passer à window.location.assign , de cette façon, vous pouvez vous moquer de la fonction.

@simon360 Fonctionne comme un charme ! Merci. ??

j'ai utilisé

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

avec testURL dans la configuration de jest

La partie emplacement n'est pas le seul problème. Je ne peux pas importer jsdom séparément pour appeler jsdom.reconfigureWindow (car cette fonction n'existe plus dans la dernière version de jsdom). Je faisais cela pour tester le code qui s'exécute différemment si window !== top . Il n'y a plus moyen d'y parvenir dans la dernière version de jest, qui utilise une version plus récente de jsdom.

@andyearnshaw la jsdom.reconfigure a un membre windowTop dans son objet.

Si vous utilisez l'environnement JSDOM ( jest-environment-jsdom-global ) que j'ai lié ci-dessus, vous pouvez faire :

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

dans vos tests pour se moquer d'une valeur supérieure.

Je l'utilise et ça marche très bien, merci !

Le mardi 30 janvier 2018, 13:40 simon360, [email protected] a écrit :

@andyearnshaw https://github.com/andyearnshaw le jsdom.reconfigure
La méthode a un membre windowTop dans son objet.

Si vous utilisez l'environnement JSDOM (jest-environment-jsdom-global) je
lié ci-dessus, vous pouvez faire:

jsdom.reconfigure({
haut de la fenêtre : VOTRE_VALEUR
});

dans vos tests pour se moquer d'une valeur supérieure.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

Utiliser window.history.pushState et testUrl fonctionné pour moi

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

Je vais fermer ça car il n'y a rien à faire du côté de Jest, et des solutions de contournement existent (n'hésitez pas à continuer la discussion !)

La mise à niveau de jsdom v8.5.0 vers v11.6.2 a résolu le problème pour moi. Donc mon package.json comprend :

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

Il casse si je mets à niveau jest et jest-cli vers la v22.2.2.

@andr-3-w une raison particulière pour laquelle vous avez jsdom dans votre package.json ? Il est livré avec Jest.

@SimenB , bonne question, il n'y a pas besoin de jsdom dans notre package.json . Merci!

Donc, sans utiliser directement jsdom, voici la solution que j'ai trouvée pour mes affaires :

Fonctionne pour Jest 21.2.1 (j'ai testé sur celui-là):

Allez dans vos paramètres Jest (par exemple, j'utiliserai package.json) :

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

Maintenant, vous pourrez modifier l'objet window.location, puis vous pourrez définir l'URL sur ce que vous voulez pendant les tests.

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

Espérons que cela aide quelqu'un.

Arrête de poster ça, ça ne marche pas sur jest": "^22.4.2"

@UserNT J'ai noté la version sur laquelle cela fonctionne ici et je l'utilise abondamment sur des combinaisons de test de production. Si cela ne fonctionne pas sur une version plus récente, je suis désolé, proposez votre propre solution au lieu d'un simple dénigrement aléatoire.

Juste pour être complet, puisque la solution est bloquée au milieu de ce fil...

La solution de @ petar-prog91 fonctionnera sur Jest 21, mais pas sur Jest 22 avec le jsdom .

Afin de fonctionner sur le dernier Jest, utilisez quelque chose comme jest-environment-jsdom-global (divulgation complète, ceci est mon package) pour exposer l'objet jsdom et utilisez jsdom.reconfigure , qui aura le même (ou, au moins, un effet similaire).

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 fonctionne également sur jest 22 pour moi

@simon360 Salut, que dois-je faire si j'ai besoin de pirater le setter de location.href ?
Mes tests précédents ont beaucoup de tests comme celui-ci et maintenant ils ont tous échoué...

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 pouvez-vous me donner un exemple de mise à jour du test comme ci-dessous avec votre bibliothèque

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

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

@abhijeetNmishra avez-vous vu la documentation ? Je pense que cela répondrait à ta question.

@simon360 Oui, d'après ma compréhension de la documentation, cela prend environ

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

qui remplace l'url globalement et non par test. S'il vous plaît aider!

@abhijeetNmishra Je ne suis pas sûr que ce problème soit le meilleur endroit pour discuter. Cela vous dérangerait-il d'ouvrir un problème sur le référentiel jest-environment-jsdom-global où nous pouvons travailler dessus ? Merci!

@SimenB, la solution de contournement indiquée ("utiliser jest-environment-jsdom-global ") semble être une solution extrêmement sous-optimale à ce qui est évidemment un problème très courant. Toute personne effectuant une mise à niveau vers Jest 22 doit maintenant ajouter une dépendance à ce package tiers et (du point de vue de l'utilisateur) réécrire certains de ses tests. C'est une régression dans le comportement de Jest.

Existe-t-il une solution à cela que nous pourrions intégrer dans le jest-environment-jsdom par défaut ? Heureux de faire un PR avec vos conseils.

C'est très malheureux que quelqu'un doive sauter autant de cerceaux juste pour changer window.location.href. Je viens de commencer à utiliser Jest et je suis sur le point de reconsidérer mon choix de framework de test compte tenu de ce problème. N'y a-t-il vraiment pas de meilleure solution que les hacks laids suggérés ci-dessus ?

@ydogandjiev n'hésitez pas à contribuer au projet pour résoudre ce problème. N'oubliez pas qu'il s'agit d'un logiciel open source, alors vous déchaîner avec des commentaires comme « inacceptable » et « ridicule » ne fait rien pour aider qui que ce soit.

@msholty-fd J'aimerais aider si je peux. Étant donné que je viens de commencer avec jest et jsdom, je ne suis pas sûr d'avoir une compréhension suffisamment approfondie de ces bibliothèques pour savoir exactement quelle est la meilleure voie pour améliorer cette expérience. Est-ce que c'est quelque chose qu'il vaut mieux aborder en plaisantant ou en jsdom ? Il semble que l'une de ces bibliothèques ait apporté une modification à un moment donné qui a brisé l'approche Object.defineProperty ; était-ce un changement apporté à la plaisanterie ou au jsdom ?

Voici donc toutes les options que je considérerais préférables en fonction du nombre de lignes nécessaires pour changer window.location; aucun de ceux-ci ne fonctionne actuellement :

  1. La définition directe de href ne fonctionne pas car jsdom lève une exception avec le message "Erreur : Non implémenté : navigation (sauf modifications de hachage)" :
    window.location.href = "https://www.example.com";
  1. L'utilisation de Object.defineProperty ne fonctionne pas car JSDOM a rendu la propriété d'emplacement de la fenêtre [Unforgeable] :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. La création d'une instance de jsdom et sa configuration ne fonctionnent pas car jest semble utiliser sa propre instance qui n'est pas exposée pour un accès facile :

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

Pour que les options 1 ou 2 fonctionnent, il faudrait que jsdom revienne sur ses objectifs actuels de se comporter davantage comme un vrai navigateur. Par conséquent, il semble que la seule option dont nous disposions soit de faciliter la reconfiguration de l'instance de jsdom que jest utilise. Serait-il judicieux d'exposer cette instance directement sur l'objet global jest ? c'est-à-dire autorisant quelque chose comme ceci:

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

Je pense toujours que faire location.assign('some-url') est mieux que location.href = 'some-url' . Je trouve un appel de fonction plus explicite que l'affectation, et vous pouvez vous moquer de la fonction

@SimenB dans les cas où le code essaie de _set_ location.href , oui, location.assign() est mieux. Mais si vous testez un comportement qui _reads_ location.href , location.assign() ne résout pas le problème, d'autant plus que location.assign() dans JSDOM ne fait rien.

L'idée derrière l'utilisation de reconfigure est d'activer un chemin de code qui n'est activé que lorsque location.href est formé d'une manière particulière. Dans notre cas, nous avons eu du code qui a changé en fonction du domaine actuel - le code est malodorant, oui, mais c'est aussi nécessaire, et le meilleur moyen d'atténuer le code malodorant est d'avoir des appareils de test qui capturent le comportement et s'assurent qu'il reste en place.

Existe-t-il un moyen de l'utiliser avec Enzyme et un composant monté pour tester les redirections ?

Le test ci-dessous a réussi avant de mettre à niveau Jest :

```
it('routes pour corriger l'itinéraire', () => {

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 est toujours égal à ' https://mysuperawesomesite.com/ ', n'a pas été changé en ('https://mysuperawesomesite.com/new').

L'événement click sur l'élément ne redirige pas lors de l'utilisation de cette méthode, et la redirection se produit en définissant window.location.href.

On ne sait pas comment tester correctement cela ou si les tests qui utilisaient auparavant Object.defineProperty étaient mal construits au départ. Merci d'avance pour toute aide.

EDIT : RÉSOLU

A pu résoudre ce problème en utilisant window.location.assign(url) au lieu de window.location.href = href. Cela m'a permis de supprimer la méthode d'assignation et de tester si elle était correctement appelée. Voir ci-dessous:

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 Il y a une grosse différence entre .assign et .href Vous pouvez lire sur MDN. Le premier a une restriction majeure entre les domaines. Dans mon code, je souhaite rediriger la page parent à partir d'un iframe où mon code s'exécute. Ce sont des domaines croisés. Et la seule façon de le faire est de changer href .
J'adorerais que ce problème soit rouvert, car je ne vois pas de solution de contournement actuelle et je devrai tout simplement ne pas tester cette fonction. Ce qui est évidemment nul.

Je suis dans le même bateau que @soswow. Ce serait formidable si vous pouviez fournir un mécanisme pour remplacer l'URL, car je supprime également plusieurs tests unitaires jusqu'à ce que cette fonctionnalité soit restaurée.

J'adorerais que ce problème soit rouvert, car je ne vois pas de solution de contournement actuelle et je devrai tout simplement ne pas tester cette fonction. Ce qui est évidemment nul.

On ne peut rien faire du côté de Jest. Je suis sûr que jsdom adorerait un PR le soutenant. https://github.com/jsdom/jsdom/issues/2112

Voici une solution simple qui fonctionne.

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

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

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

@vastus Comme je l'ai explicitement souligné - le problème concerne les domaines croisés . L'API history ne permettra pas de passer à un domaine différent pour autant que je me souvienne.

Étant donné que location ne peut pas être remplacé directement sur l'objet jsdom window , une approche possible consiste à le remplacer sur un objet dérivé :

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

J'utilise ceci pour résoudre le problème:

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

Inspiré par @RubenVerborgh & annemarie35

Ajout d'un exemple de test de location.search basé sur la solution de @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 Fonctionne comme un charme.

essayer:

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

Une solution similaire à @sahalsaad :
```javascript
const oldWindow = window.location;
supprimer window.location;
window.location = {
...vieille fenêtre,
// inclure tous les écrasements personnalisés tels que le talon sinon suivant
remplacer : sinon.stub(),
} ;

// fais ta magie

window.location = oldWindow;
````

@sahalsaad merci ! J'ai utilisé une variante de votre solution pour me moquer de window.location.search :

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

probablement une meilleure solution:

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

Résolu mon problème en utilisant la solution donnée par @kdelmonte , j'ai dû me moquer de la variable window.location.search . donc j'ai utilisé

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

Étant donné que location ne peut pas être remplacé directement sur l'objet jsdom window , une approche possible consiste à le remplacer sur un objet dérivé :

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

votre réponse est la seule qui fonctionne pour ma situation, merci!

Étant donné que location ne peut pas être remplacé directement sur l'objet jsdom window , une approche possible consiste à le remplacer sur un objet dérivé :

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

votre réponse est la seule qui fonctionne pour ma situation, merci!

Cela ne fonctionne plus 😢

Étant donné que location ne peut pas être remplacé directement sur l'objet jsdom window , une approche possible consiste à le remplacer sur un objet dérivé :

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

votre réponse est la seule qui fonctionne pour ma situation, merci!

Cela ne fonctionne plus 😢

Ne fonctionne pas pour moi non plus

J'ai résolu ceci en faisant :

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

J'utilise la maquette suivante comme utilitaire pour travailler avec 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
  }
}

Puis dans mes tests

let location: MockLocation

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

Je me suis retrouvé à écraser des objets délicats comme ceux-ci tout le temps et j'ai créé une fonction d'aide flexible :

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

Et puis utilisation :

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

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

Et vous pouvez écraser ce que vous voulez : pathname , href , etc... Cela vous donne l'avantage gratuit supplémentaire du nettoyage.

La clé est que vous ne pouvez pas jouer avec location lui-même, alors remplacez simplement location par un faux, puis remettez-le lorsque le test est terminé.

Comme je peux le voir dans ma session de débogage, global.location est implémenté via getter mais pas une simple propriété. Ne serait-il pas plus prudent de le redéfinir ainsi ?

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

Bien qu'il soit difficile d'imaginer pourquoi je voudrais utiliser l'original global.location , cela semble juste un peu plus correct.
Et ce code fonctionne bien pour moi, bien sûr. Je viens d'accéder à location.pathname , mais cet objet peut être facilement étendu avec quelques jest.fn() si nécessaire.

Cela a fonctionné pour moi, en utilisant 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");
});

Ajout d'un exemple de test de location.search basé sur la solution de @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');
  });

Cela a parfaitement fonctionné pour le problème que j'avais

Cette page vous a été utile?
0 / 5 - 0 notes