Rafael Camargo

Testando redirecionamento feito via window.location.href com Jest

Às vezes somos surpreendindos com o seguinte erro ao rodar nossos testes:

not implemented: navigation

Uma situação muito comum sob a qual este erro pode acontecer é quando a implementação altera o valor de window.location.href.

Se nenhuma situação como esta lhe vem à cabeça, pense no seguinte caso: ao clicar em um botão da sua aplicação, o usuário pode tanto ser redirecionado para outra view desta mesma aplicação — situação em que apenas o window.history é manipulado — quanto pode ser redirecionado para uma página externa. É quando a aplicação leva o usuário para a página externa alterando o valor de window.location.href, que o teste cai exatamente no erro comentado no início do post.

O jeito mais simples de evitar esse problema é estabelecendo uma fronteira entre o comportamento que está sob domínio da aplicação, e o comportamento que é responsabilidade do browser (carregar uma outra página a partir do novo valor atribuído ao window.location.href).

Diagrama exibindo uma fronteira, delimitada por um serviço chamado "window service", entre a aplicação e o browser
Fronteira estabelecida pelo Window Service

Essa fronteira pode ser estabelecida com a criação de um serviço que convenciono chamar de window service, dado que ele pode abrigar outros métodos que resolvem problemas similares ao da reatribuição do href. Especifico-o com a seguinte suite de testes:

// src/services/window.test.js
describe('Window Service', () => {
  it('should set window location href', () => {
    const { origin } = window.location;
    const newUrl = `${origin}/#my-custom-hash`;
    windowService.setHref(newUrl);
    expect(window.location.href).toEqual(newUrl);
  });
});

Como o objetivo desta suíte é garantir que o valor de window.location.href é de fato atualizado pelo browser quando eu o defino através do método setHref, temos toda a liberdade do mundo para definir um valor que não dispara qualquer navegação por parte do browser: /#my-custom-hash.

// src/services/window.js
const _public = {};

_public.setHref = href  => {
  window.location.href = href;
}

export default _public

Tendo agora a garantia de que chamando windowService.setHref usufruímos do mesmo resultado que atribuir diretamente um valor para window.location.href, podemos substituir a atribuição direta pelo método setHref na nossa implementação, e mocar o respectivo método nos testes:

describe('Conditional Button', () => {
  beforeEach(() => {
    windowService.setHref = jest.fn();
  });

  it('should redirect to help page if user does not satisfy some condition', () => {
    render (<ConditonalButton />);
    await screen.getByRole('button', { name: 'Go' });
    expect(windowService.setHref).toHaveBeenCalledWith(
      'https://some.support.page'
    );
  });
});

Saiba mais: Se esta dica te ajudou a testar o redirecionamento para uma página externa, você provavelmente vai gostar de saber como simular search params nos testes de um componente React.