O desenvolvimento de código JavaScript para vários ambientes geralmente revela armadilhas sutis e pode causar dores de cabeça latejantes e frustração sem fim. Ao construir um fetch wrapper projetado para funcionar de forma consistente em navegadores, nós e ambientes de teste, me deparei com um problema surpreendente: URLs relativos funcionavam bem no navegador, mas falhavam em JSDOM.
JSDOM é uma implementação JavaScript dos padrões DOM e web, permitindo que o código Node.js interaja com um ambiente de navegador virtual. É amplamente utilizado em estruturas de teste como Jest e Vitest.
Como desenvolvedor full-stack sênior e contribuidor de código aberto com foco em ferramentas JavaScript e Golang, mantenho bibliotecas como buscar e escrever sobre confiabilidade em vários ambientes e configurações práticas de testes. Este tópico é importante para mim porque, tanto no trabalho quanto em meus projetos paralelos, muitas vezes tenho que construir bibliotecas que devem se comportar de forma idêntica em Node, navegadores, tempos de execução de borda e ambientes de teste — um objetivo enganosamente complicado que levou a esta história de depuração.
O problema
Nos navegadores, os URLs relativos são automaticamente resolvidos em relação a window.location.origin. Mas em alguns ambientes de teste, isso não é garantido. Em JSDOM bruto, window.location.href o padrão é about:blankfazendo com que os URLs relativos falhem. No Jest ou Vitest, entretanto, o ambiente JSDOM define automaticamente uma URL base padrão (geralmente http://localhost), e é por isso que URLs relativos muitas vezes “simplesmente funcionam” ali.
Feliz DOM (outra alternativa JSDOM popular) por padrão também inicializa com about:blankportanto, os URLs relativos serão quebrados, a menos que você defina explicitamente window.location.href ou forneça um URL base durante a configuração.
A primeira lição é garantir que seu ambiente de teste esteja configurado corretamente. Mas a história não termina aí.
Encontrando o problema no wrapper
Mesmo que Vitest e Jest pré-configurem JSDOM com um URL base válido (http://localhost), o wrapper ainda encontrou TypeError: Failed to parse URL erros ao criar solicitações com URLs relativos no executor de teste.
Depois de algumas pesquisas, encontrei este antigo JSDOM emitirque explica como a resolução relativa do URL pode falhar devido a diferenças no location objeto.
Acontece que o motivo não é o URL base em si – o JSDOM é definido corretamente globalThis.location.origin – mas sim como o JSDOM implementa globalThis.location. Isso é location objeto não é totalmente idêntico ao de um navegador window.locatione Request pode rejeitar determinados valores ao resolver URLs relativos. Isso acontece porque Request(input) usa internamente as especificações URL construtor, que impõe validação estrita do Location objeto.
Em outras palavras, o ambiente fornece uma URL base, mas o wrapper ainda precisa resolver explicitamente as URLs relativas (por exemplo, via new URL(relative, globalThis.location.origin)) para garantir a compatibilidade.
A correção
Sabendo disso, a correção é simples (embora feia): detecte explicitamente URLs relativos e resolva-os em relação a globalThis.location.origin antes de construir a solicitação. Dessa forma, o wrapper se comporta de forma consistente entre navegadores, Node e todas as alternativas de DOM, independentemente de diferenças sutis em suas location implementações:
|
deixar url = entrada; se (tipo de entrada === ‘corda’ && !/^((az)(a-z0-9+.-)*:)/i.test(input)) { //URL relativa detectada if (typeof globalThis.location?.origin === ‘string’) { url = novo URL(input, globalThis.location.origin).href; } } const solicitação = novo Solicitar(url, iniciar); |
Isso garante que o wrapper funcione em todos os ambientes onde globalThis.location.origin é definido, o que inclui navegadores, Node com jsdom (se você configurá-lo) e executores de teste como Jest e Vitest.
Eu apliquei esse patch em busca do caosminha biblioteca de testes de caos, para lidar com URLs relativos de maneira robusta em todos os ambientes.
Lições aprendidas
A experiência destacou duas lições importantes:
- Os ambientes de teste devem ser configurados corretamente. Certifique-se sempre
window.locationou o URL base é definido em seu executor de teste ou alternativa de DOM. Sem ele, os URLs relativos falharão de qualquer maneira. Esta não é responsabilidade do invólucro; é o meio ambiente. - A lógica do wrapper ainda é essencial. Mesmo com um ambiente configurado corretamente, seu wrapper deve lidar com URLs relativas com segurança para garantir um comportamento consistente entre navegadores, Node, JSDOM e Happy DOM.
Observação: se estivéssemos usando Happy DOM em vez de JSDOM, e ele fosse configurado com uma URL base adequada, as URLs relativas poderiam funcionar sem o patch, mas manter a lógica no wrapper adiciona robustez e um pouco mais de proteção contra diferenças sutis de ambiente.
O desenvolvimento de JavaScript em vários ambientes revela complexidades ocultas em operações aparentemente simples. URLs relativos em fetch são simples em um navegador, mas ambientes de teste como JSDOM e Happy DOM expõem casos extremos estranhos. Ao diagnosticar cuidadosamente o problema, implementar uma lógica de resolução mais robusta e configurar seus ambientes adequadamente, os autores da biblioteca podem construir abstrações mais confiáveis e previsíveis que funcionam (quase) em qualquer lugar.
Essa experiência ressalta uma lição mais ampla: sempre teste suposições em vários ambientes e projete suas bibliotecas para lidar com o inesperado com elegância.

Café Codificado é um portal dinâmico e confiável criado especialmente para desenvolvedores. Nosso foco é entregar:
Dicas práticas para programação, produtividade, frameworks, testes, DevOps e muito mais;
Notícias atualizadas, acompanhando tendências e lançamentos do mundo da tecnologia, compiladas com relevância e sem jargões desnecessários.
O que você encontra aqui:
Artigos objetivos e comandáveis — Tutoriais, tutoriais passo-a-passo e dicas que vão direto ao ponto.
Cobertura das tecnologias que estão em alta — do universo da IA, computação em nuvem e segurança à engenharia de software e criatividade em código.
Conteúdo para todos os níveis — de iniciantes buscando praticidade, a profissionais em busca de insights estratégicos e aperfeiçoamento.
Comunidade ativa — textos humanizados, perguntinhas instigantes e espaço para você contribuir com reflexões e comentários.