Jest: Não é possível alterar window.location usando Object.defineProperty

Criado em 19 dez. 2017  ·  78Comentários  ·  Fonte: facebook/jest

Você quer solicitar um _feature_ ou denunciar um _bug_? Reportar um erro

Qual é o comportamento atual?

Chamando o seguinte de dentro de um conjunto de testes:

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

lança o seguinte erro:

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

Qual é o comportamento esperado?

O código não deve lançar uma exceção e window.location.hostname === "example.com" deve ser avaliado como verdadeiro.

Ao que parece, jsdom agora define window.location como impraticável . A única maneira de alterar os valores dentro de window.location é usar reconfigure , mas (por # 2460) Jest não expõe jsdom para testes para brincar.

Forneça sua configuração exata de Jest e mencione seu Jest, nó,versão yarn / npm e sistema operacional.

Versão Jest : 22.0.1
Versão do nó : 8.6.0
Versão do fio : 1.2.0
SO : macOS High Sierra 10.13.2

Discussion Wontfix

Comentários muito úteis

Publiquei um novo pacote no npm chamado jest-environment-jsdom-global , que pode ajudar com os problemas que algumas pessoas estão tendo com Object.defineProperty .

Todos 78 comentários

Eu tenho um problema semelhante. Você pode criar seu próprio JSDOMEnvironment e expor o objeto jsdom ao global desta forma.

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

E então você pode chamar jsdom.reconfigure em seu caso de teste como quiser

Essa é uma boa solução alternativa, obrigado por compartilhar!

Você deve retornar super.teardown(); , pois é uma promessa, aliás

Perfeito, @oliverzy - vou tentar. Obrigado!

Existe um local apropriado para documentar isso? Parece ser uma pergunta que surge com razoável frequência; esperançosamente, problemas futuros poderiam ser eliminados se isso fosse integrado aos documentos.

Esta solução não funcionou muito bem .

Dentro de nossos arquivos de teste, parece que global está definido como o objeto window do JSDom.

Em outras palavras, dentro de um conjunto de testes, global é o mesmo que window , mas dentro da classe que estende JSDOMEnvironment , global vem do ambiente do Node.

Como resultado, ter este:

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

falha porque global.jsdom é indefinido.

Eu consegui contornar isso fazendo isso, mas não estou muito preocupado com isso.

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

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

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

Com este ambiente, global.jsdom dentro de um conjunto de testes é igual a this.dom , e o conjunto de testes acima funciona.

Para mim, parece que definir jsdom como uma propriedade própria window objeto está fadado a se desintegrar eventualmente - existe uma maneira mais limpa de fazer isso?

você precisa escrever jsdom vez de global.jsdom em seus testes.

@oliverzy Gosta disso?

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

Isso gera jsdom is not defined , mas posso estar interpretando mal.

@ simon360 Configure testEnvironment com o código de @oliverzy , consulte https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

@danielbayerlein minha configuração do Jest tem isto:

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

onde @wel-ui/jest-environment-jsdom-global é o nome de um pacote em nosso monorepo. O ambiente está sendo usado corretamente, porém, porque a solução que define jsdom em window funciona conforme o esperado.

BTW, alguém sabe por que a solução original não funciona na nova versão?
Este:

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

@modestfake , atualizamos de JSDOM @ 9 para JSDOM @ 11 , meu palpite é que eles mudaram a forma como a variável é definida

@SimenB Entendi. Acabei de encontrar uma descrição do método jsdom reconfigure .

A propriedade top na janela é marcada como [Unforgeable] na especificação, o que significa que é uma propriedade própria não configurável e, portanto, não pode ser substituída ou sombreada pelo código normal em execução dentro do jsdom, mesmo usando Object.defineProperty.

Eu adicionei um novo repositório para demonstrar esse comportamento. Alguém pode reproduzi-lo clonando localmente?

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

@ simon360 reproduzido
image

@ simon360 eu encontrei. Você perdeu this palavra-chave ao definir 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();
  }
};

E quanto a location.search ? Não encontrei nenhuma menção sobre isso https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha E quanto a isso?

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

Obrigado @modestfake - desculpe pelo erro idiota!

Ok, eu vejo agora - this.global em um objeto de ambiente Jest é definido como global em um arquivo de teste Jest. Isso faz sentido - obrigado por me ajudar nisso! Se houver interesse suficiente, posso empacotar a versão reparada desse repo e colocá-lo em npm como jest-environment-jsdom-global .

No entanto, espero que haja uma maneira mais limpa de fazer isso em Jest no futuro. Esta não é uma maneira de baixo atrito de mudar window.location -

Poderia haver um novo docblock, como há para @jest-environment ? Por exemplo...

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

Ou talvez o JSDom possa ser exposto em uma parte especial do objeto jest - algo como:

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

(o que teria o benefício adicional de poder alterar window.top )

Incorporamos # 5003 agora. ser capaz de adicioná-lo como um docblock pode fazer sentido, não tenho certeza. @cpojer? Poderíamos descontinuar testUrl também, pois ele pode ser fornecido por meio dessa nova opção.

Se houver interesse suficiente, eu poderia empacotar a versão reparada desse repo e colocá-lo em npm como jest-environment-jsdom-global .

Acho que faz sentido em qualquer caso, pois faz mais do que apenas permitir que você defina o url - expõe o JSDOM completo ao ambiente

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); lança o mesmo erro que window.location . Obrigado pela sugestão embora.

Object.defineProperty (window.location, 'href', {
definir: newValue => {currentUrl = newValue; },
});
Eu tinha isso nas versões anteriores e agora gera erro.
Se eu adicionar gravável: verdadeiro
lança outra exceção que não posso especificar acessador e gravável

Publiquei um novo pacote no npm chamado jest-environment-jsdom-global , que pode ajudar com os problemas que algumas pessoas estão tendo com Object.defineProperty .

Alguém tem uma solução alternativa para { writable: true } ?

Por exemplo:

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

...

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

...

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

@danielbayerlein leia este tópico. Você precisa criar um ambiente personalizado. A mensagem anterior contém url com exemplo

@modestfake Já li este tópico e https://github.com/facebook/jest/issues/5124#issuecomment -352749005 funciona bem. Mas tenho outro caso de uso. Com Jest 21.xx eu configurei Object.defineProperty(window.location, 'href', { writable: true }) sem o URL - apenas { writable: true } . Se eu definir o URL, o teste não fará sentido.

@danielbayerlein qual é o caso de uso para torná-lo gravável, mas não substituí-lo realmente? Talvez compreender isso possa me ajudar a encontrar uma solução alternativa

Tenho uma função que muda o 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')
  ...
})

Com Jest 21.xx eu defini Object.defineProperty(window.location, 'href', { writable: true })

Eu recomendo mudar para window.location.assign , dessa forma você pode simular a função.

@ simon360 Funciona como um encanto! Obrigada. 🤝

eu usei

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

junto com testURL na configuração do jest

A parte da localização não é o único problema. Não posso importar jsdom separadamente para chamar jsdom.reconfigureWindow (porque essa função não existe mais na versão mais recente do jsdom). Eu estava fazendo isso para testar um código que é executado de maneira diferente se window !== top . Não há mais uma maneira de conseguir isso na versão mais recente do jest, que usa uma versão mais recente do jsdom.

@andyearnshaw o jsdom.reconfigure tem um membro windowTop em seu objeto.

Se você estiver usando o ambiente JSDOM ( jest-environment-jsdom-global ) vinculado acima, você pode fazer:

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

em seus testes para simular um valor superior.

Estou usando isso e funciona muito bem, obrigado!

Na terça, 30 de janeiro de 2018, 13:40 simon360, [email protected] escreveu:

@andyearnshaw https://github.com/andyearnshaw the jsdom.reconfigure
método tem um membro windowTop em seu objeto.

Se você estiver usando o ambiente JSDOM (jest-environment-jsdom-global) I
vinculado acima, você pode fazer:

jsdom.reconfigure ({
windowTop: YOUR_VALUE
});

em seus testes para simular um valor superior.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

Usar window.history.pushState e testUrl funcionou para mim

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

Vou fechar isto porque não há nada a fazer do lado de Jest e existem soluções alternativas (fique à vontade para continuar a discussão!)

Atualizar do jsdom v8.5.0 para v11.6.2 resolveu o problema para mim. Portanto, meu package.json inclui:

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

Ele quebra se eu atualizar jest e jest-cli para a v22.2.2.

@ andr-3-w algum motivo particular pelo qual você tem jsdom em seu package.json? Ele vem junto com Jest.

@SimenB , boa pergunta, não há necessidade de jsdom em nosso package.json . Obrigado!

Então, sem usar jsdom diretamente, aqui está a solução que encontrei para minhas coisas:

Funciona para Jest 21.2.1 (eu testei naquele):

Vá para as configurações do Jest (por exemplo, usarei o package.json):

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

Agora você poderá alterar o objeto window.location e definir a URL como quiser durante os testes.

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

Espero que isso ajude alguém.

Pare de postar isso, não funciona no jest ":" ^ 22.4.2 "

@UserNT Anotei a versão na qual funciona aqui e a utilizo extensivamente em ternos de teste de produção. Se não funcionar em uma versão mais recente, desculpe, venha com sua própria solução em vez de apenas bashing aleatório.

Só para completar, já que a solução fica presa no meio deste thread ...

A solução de @ petar-prog91 funcionará no Jest 21, mas não no Jest 22 com o jsdom atualizado.

Para executar o Jest mais recente, use algo como jest-environment-jsdom-global (divulgação completa, este é o meu pacote) para expor o objeto jsdom e use jsdom.reconfigure , que terá o mesmo (ou, pelo menos, um efeito semelhante).

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 também funciona no jest 22 para mim

@ simon360 Olá, o que devo fazer se precisar hackear location.href setter?
Meus testes anteriores têm muitos testes como este e agora todos falharam ...

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 você pode me dar um exemplo de como atualizar o teste como abaixo com sua biblioteca

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

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

@abhijeetNmishra você viu a documentação ? Eu acredito que isso responderia sua pergunta.

@ simon360 Sim, com base no meu entendimento da documentação, leva cerca de

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

que substitui o URL globalmente e não por teste. Por favor ajude!

@abhijeetNmishra Não tenho certeza se este assunto é o melhor lugar para discutir. Você se importaria de abrir um problema no repositório jest-environment-jsdom-global onde podemos trabalhar nele? Obrigado!

@SimenB a solução alternativa declarada ("use jest-environment-jsdom-global ") parece uma solução extremamente subótima para o que é obviamente um problema muito comum. Qualquer um atualizando para Jest 22 agora precisa adicionar uma dependência a esse pacote de terceiros e (da perspectiva do usuário) reescrever alguns de seus testes. Esta é uma regressão no comportamento de Jest.

Existe uma solução para isso que possamos incorporar ao jest-environment-jsdom padrão? Fico feliz em fazer um RP com sua orientação.

É muito lamentável que alguém tenha que pular tantos obstáculos apenas para alterar window.location.href. Acabei de começar a usar o Jest e estou prestes a reconsiderar minha escolha de framework de teste devido a esse problema. Realmente não há solução melhor do que os hacks horríveis sugeridos acima?

@ydogandjiev sinta-se à vontade para contribuir com o projeto para resolver este problema. Lembre-se de que este é um código-fonte aberto, portanto, fazer comentários como “inaceitável” e “ridículo” não ajuda ninguém.

@ msholty-fd Adoraria ajudar se puder. Como estou apenas começando com jest e jsdom, não tenho certeza se tenho um conhecimento profundo o suficiente dessas bibliotecas para saber exatamente qual é o melhor caminho para melhorar essa experiência. É algo melhor tratado em uma brincadeira ou jsdom? Parece que uma dessas bibliotecas fez uma alteração em algum ponto que quebrou a abordagem Object.defineProperty; foi uma mudança feita em jest ou jsdom?

Portanto, aqui estão todas as opções que eu consideraria preferíveis com base no número de linhas necessárias para alterar window.location; nenhum deles funciona atualmente:

  1. Definir href diretamente não funciona porque jsdom lança uma exceção com a mensagem "Erro: Não implementado: navegação (exceto alterações de hash)":
    window.location.href = "https://www.example.com";
  1. O uso de Object.defineProperty não funciona porque JSDOM tornou a propriedade de localização da janela [Unforgeable] :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. Criar uma instância de jsdom e configurá-la não funciona porque jest parece usar sua própria instância, que não é exposta para fácil acesso:

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

Fazer as opções 1 ou 2 funcionarem exigiria que jsdom retrocedesse em seus objetivos atuais de se comportar mais como um navegador real. Portanto, parece que a única opção que temos é tornar mais fácil reconfigurar a instância de jsdom que jest usa. Faria sentido expor essa instância diretamente no objeto global jest; ou seja, permitindo algo assim:

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

Ainda acho que fazer location.assign('some-url') é melhor do que location.href = 'some-url' . Acho uma chamada de função mais explícita do que atribuição, e você pode simular a função

@SimenB nos casos em que o código está tentando _set_ location.href , sim, location.assign() é melhor. Mas se você estiver testando o comportamento que _reads_ location.href , location.assign() não resolve o problema, especialmente porque location.assign() em JSDOM não faz nada.

A ideia por trás de usar reconfigure é ativar um caminho de código que só é habilitado quando location.href é formado de uma maneira particular. Em nosso caso, tivemos alguns códigos que mudaram dependendo do domínio atual - o código fede, sim, mas também é necessário, e a melhor maneira de mitigar o código fedorento é ter acessórios de teste que capturem o comportamento e garantam que ele permaneça no lugar.

Existe uma maneira de usar isso com Enzyme e um componente montado para testar os redirecionamentos?

O teste abaixo passou antes de atualizar o Jest:

`` `
it ('rotas para corrigir a rota', () => {

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 ainda é igual a ' https://mysuperawesomesite.com/ ', não foi alterado para ('https://mysuperawesomesite.com/new').

O evento click no elemento não redireciona ao usar este método, e o redirecionamento ocorre configurando window.location.href.

Não está claro como testar isso corretamente ou se os testes que usaram anteriormente Object.defineProperty foram mal construídos para começar. Agradecemos antecipadamente por qualquer ajuda.

EDITAR: RESOLVIDO

Conseguiu resolver isso usando window.location.assign (url) em vez de window.location.href = href. Isso me permitiu esboçar o método de atribuição e testar se ele estava sendo chamado corretamente. Ver abaixo:

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 Há uma grande diferença entre .assign e .href Você pode ler no MDN. O primeiro tem uma grande restrição de domínio cruzado. No meu código, quero redirecionar a página pai de um iframe em que meu código está sendo executado. Esses são domínios cruzados. E a única maneira de fazer isso é alterando href .
Eu adoraria se este problema fosse reaberto, já que não vejo uma solução alternativa atual e terei que simplesmente não ter teste para essa função. O que obviamente é uma merda.

Estou no mesmo barco que @soswow. Seria ótimo se você pudesse fornecer um mecanismo para substituir o url, já que também estou removendo vários testes de unidade até que essa funcionalidade seja restaurada.

Eu adoraria se este problema fosse reaberto, já que não vejo uma solução alternativa atual e terei que simplesmente não ter teste para essa função. O que obviamente é uma merda.

Não há nada que possamos fazer do lado da piada. Tenho certeza que jsdom adoraria um PR apoiando isso. https://github.com/jsdom/jsdom/issues/2112

Aqui está uma solução simples que funciona.

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

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

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

@vastus Como eu indiquei explicitamente - o problema é com domínios cruzados . API de histórico não permite mudar para um domínio diferente, tanto quanto me lembro.

Como location não pode ser substituído diretamente no objeto jsdom window , uma abordagem possível é substituí-lo em um objeto derivado:

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

Eu uso isso para resolver o problema:

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

Inspirado por @RubenVerborgh e annemarie35

Adicionando um exemplo de teste location.search base na solução 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 Funciona como um encanto.

experimentar:

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

Uma solução semelhante a @sahalsaad :
`` `javascript
const oldWindow = window.location;
delete window.location;
window.location = {
... oldWindow,
// inclui qualquer substituição personalizada, como o seguinte esboço de sinon
substitua: sinon.stub (),
};

// faça sua mágica

window.location = oldWindow;
`` ``

@sahalsaad obrigado! Usei uma variação da sua solução para simular window.location.search:

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

provavelmente uma solução melhor:

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

Resolvi meu problema usando a solução fornecida por @kdelmonte , tive que simular a variável window.location.search . então eu usei

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

Como location não pode ser substituído diretamente no objeto jsdom window , uma abordagem possível é substituí-lo em um objeto derivado:

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

sua resposta é a única que funciona para a minha situação, obrigado!

Como location não pode ser substituído diretamente no objeto jsdom window , uma abordagem possível é substituí-lo em um objeto derivado:

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

sua resposta é a única que funciona para a minha situação, obrigado!

Isso não está mais funcionando 😢

Como location não pode ser substituído diretamente no objeto jsdom window , uma abordagem possível é substituí-lo em um objeto derivado:

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

sua resposta é a única que funciona para a minha situação, obrigado!

Isso não está mais funcionando 😢

Não funciona para mim também

Resolvi isso fazendo:

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

Estou usando o seguinte mock como um utilitário para trabalhar com 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
  }
}

Então em meus testes

let location: MockLocation

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

Eu me peguei tentando fazer stub em objetos complicados como esses o tempo todo e criei uma função auxiliar flexível:

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

E então o uso:

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

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

E você pode criar um stub o que quiser: pathname , href , etc ... Isso dá a você o benefício gratuito de limpeza.

A chave é que você não pode mexer com o próprio location , então apenas troque location por um falso e coloque-o de volta quando o teste for concluído.

Como posso ver em minha sessão de depuração, global.location é implementado via getter, mas não uma propriedade simples. Não seria mais seguro redefini-lo assim?

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

Embora seja difícil imaginar por que eu iria querer usar o global.location , parece um pouco mais correto.
E esse código funciona bem para mim, é claro. Acabei de acessar location.pathname , mas este objeto pode ser facilmente estendido com alguns jest.fn() se necessário.

Isso tem funcionado para mim, usando o gracejo 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");
});

Adicionando um exemplo de teste location.search base na solução 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');
  });

Isso funcionou perfeitamente bem para o problema que eu estava tendo

Esta página foi útil?
0 / 5 - 0 avaliações