jsdom é uma implementação JavaScript pura de muitos padrões da web, notadamente os padrões WHATWG DOM e HTML, para uso com Node.js. Em geral, o objetivo do projeto é emular um subconjunto suficiente de um navegador da web para ser útil para testar e extrair aplicativos da web do mundo real.
As versões mais recentes do jsdom requerem Node.js v18 ou mais recente. (As versões do jsdom abaixo da v23 ainda funcionam com versões anteriores do Node.js, mas não são suportadas.)
const jsdom = require("jsdom"); const { JSDOM } = jsdom;
Para usar jsdom, você usará principalmente o construtor JSDOM
, que é uma exportação nomeada do módulo principal jsdom. Passe uma string ao construtor. Você receberá de volta um objeto JSDOM
, que possui diversas propriedades úteis, principalmente window
:
const dom = new JSDOM(`<!DOCTYPE html><p>Olá mundo</p>`);console.log(dom.window.document.querySelector("p").textContent); // "Olá mundo"
(Observe que o jsdom analisará o HTML que você passa, assim como um navegador faz, incluindo tags implícitas <html>
, <head>
e <body>
.)
O objeto resultante é uma instância da classe JSDOM
, que contém diversas propriedades e métodos úteis além de window
. Em geral, ele pode ser usado para atuar no jsdom “de fora”, fazendo coisas que não são possíveis com as APIs DOM normais. Para casos simples, onde você não precisa de nenhuma dessas funcionalidades, recomendamos um padrão de codificação como
const { janela } = new JSDOM(`...`);// ou evenconst { document } = (new JSDOM(`...`)).window;
A documentação completa sobre tudo o que você pode fazer com a classe JSDOM
está abaixo, na seção " JSDOM
Object API".
O construtor JSDOM
aceita um segundo parâmetro que pode ser usado para customizar seu jsdom das seguintes maneiras.
const dom = new JSDOM(``, { url: "https://example.org/", referenciador: "https://example.com/", contentType: "texto/html", includeNodeLocations: verdadeiro, cota de armazenamento: 10000000});
url
define o valor retornado por window.location
, document.URL
e document.documentURI
e afeta coisas como a resolução de URLs relativos dentro do documento e as restrições de mesma origem e referenciador usado ao buscar sub-recursos. O padrão é "about:blank"
.
referrer
afeta apenas o valor lido de document.referrer
. O padrão é nenhum referenciador (que reflete como uma string vazia).
contentType
afeta o valor lido de document.contentType
, bem como a forma como o documento é analisado: como HTML ou XML. Valores que não são do tipo HTML MIME ou XML MIME serão lançados. O padrão é "text/html"
. Se um parâmetro charset
estiver presente, ele poderá afetar o processamento de dados binários.
includeNodeLocations
preserva as informações de localização produzidas pelo analisador HTML, permitindo recuperá-las com o método nodeLocation()
(descrito abaixo). Ele também garante que os números de linha relatados nos rastreamentos de pilha de exceções para o código executado dentro dos elementos <script>
estejam corretos. O padrão é false
para fornecer o melhor desempenho e não pode ser usado com um tipo de conteúdo XML, pois nosso analisador XML não oferece suporte a informações de localização.
storageQuota
é o tamanho máximo em unidades de código para as áreas de armazenamento separadas usadas por localStorage
e sessionStorage
. As tentativas de armazenar dados maiores que esse limite farão com que uma DOMException
seja lançada. Por padrão, é definido como 5.000.000 de unidades de código por origem, inspirado na especificação HTML.
Observe que tanto url
quanto referrer
são canônicos antes de serem usados, por exemplo, se você passar "https:example.com"
, o jsdom interpretará isso como se você tivesse fornecido "https://example.com/"
. Se você passar um URL não analisável, a chamada será lançada. (URLs são analisados e serializados de acordo com o padrão de URL.)
A habilidade mais poderosa do jsdom é que ele pode executar scripts dentro do jsdom. Esses scripts podem modificar o conteúdo da página e acessar todas as APIs da plataforma web que o jsdom implementa.
No entanto, isto também é altamente perigoso quando se lida com conteúdo não confiável. O sandbox jsdom não é infalível, e o código executado dentro dos <script>
s do DOM pode, se tentar bastante, obter acesso ao ambiente Node.js e, portanto, à sua máquina. Como tal, a capacidade de executar scripts incorporados no HTML está desabilitada por padrão:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`);// O script não será executado, por padrão:console.log(dom.window.document.getElementById("content").children.length); //0
Para habilitar a execução de scripts dentro da página, você pode usar a opção runScripts: "dangerously"
:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "dangerously" });// O script será executado e modificará o DOM:console.log(dom.window.document.getElementById("content").children.length); //1
Mais uma vez, enfatizamos que você deve usar isso apenas ao alimentar código jsdom que você sabe que é seguro. Se você usá-lo em código arbitrário fornecido pelo usuário ou em código da Internet, estará efetivamente executando código Node.js não confiável e sua máquina poderá ser comprometida.
Se você deseja executar scripts externos , incluídos via <script src="">
, você também precisará garantir que eles os carreguem. Para fazer isso, adicione a opção resources: "usable"
conforme descrito abaixo. (Você provavelmente também desejará definir a opção url
, pelos motivos discutidos lá.)
Atributos do manipulador de eventos, como <div onclick="">
, também são regidos por esta configuração; eles não funcionarão a menos que runScripts
esteja definido como "dangerously"
. (No entanto, as propriedades do manipulador de eventos, como div.onclick = ...
, funcionarão independentemente dos runScripts
.)
Se você está simplesmente tentando executar o script "de fora", em vez de permitir que elementos <script>
e atributos de manipuladores de eventos sejam executados "de dentro", você pode usar a opção runScripts: "outside-only"
, que permite novas cópias de todos os globais fornecidos pelas especificações do JavaScript devem ser instalados em window
. Isso inclui coisas como window.Array
, window.Promise
, etc. Também inclui notavelmente window.eval
, que permite a execução de scripts, mas com a window
jsdom como global:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "outside-only" });// executa um script fora de JSDOM:dom.window.eval('document.getElementById("content").append(document.createElement("p"));');console.log(dom.window.document.getElementById("content"). comprimento dos filhos); // 1console.log(dom.window.document.getElementsByTagName("hr").length); // 0console.log(dom.window.document.getElementsByTagName("p").length); //1
Isso está desativado por padrão por motivos de desempenho, mas é seguro ativá-lo.
Observe que na configuração padrão, sem definir runScripts
, os valores de window.Array
, window.eval
, etc. serão os mesmos fornecidos pelo ambiente externo do Node.js. Ou seja, window.eval === eval
será mantido, portanto window.eval
não executará scripts de maneira útil.
Aconselhamos fortemente não tentar "executar scripts" misturando os ambientes globais jsdom e Node (por exemplo, fazendo global.window = dom.window
) e depois executando scripts ou código de teste dentro do ambiente global do Node. Em vez disso, você deve tratar o jsdom como faria com um navegador e executar todos os scripts e testes que precisam de acesso a um DOM dentro do ambiente jsdom, usando window.eval
ou runScripts: "dangerously"
. Isso pode exigir, por exemplo, a criação de um pacote browserify para ser executado como um elemento <script>
— assim como você faria em um navegador.
Finalmente, para casos de uso avançados você pode usar o método dom.getInternalVMContext()
, documentado abaixo.
jsdom não tem a capacidade de renderizar conteúdo visual e funcionará como um navegador headless por padrão. Ele fornece dicas para páginas da web por meio de APIs como document.hidden
de que seu conteúdo não está visível.
Quando a opção pretendToBeVisual
estiver definida como true
, o jsdom fingirá que está renderizando e exibindo conteúdo. Isso é feito por:
Alterando document.hidden
para retornar false
em vez de true
Alterando document.visibilityState
para retornar "visible"
em vez de "prerender"
Habilitando os métodos window.requestAnimationFrame()
e window.cancelAnimationFrame()
, que de outra forma não existem
const window = (new JSDOM(``, { fingiToBeVisual: true })).window;window.requestAnimationFrame(timestamp => { console.log(timestamp > 0);});
Observe que o jsdom ainda não faz nenhum layout ou renderização, então trata-se apenas de fingir ser visual, não de implementar as partes da plataforma que um navegador visual real implementaria.
Por padrão, o jsdom não carrega nenhum sub-recurso, como scripts, folhas de estilo, imagens ou iframes. Se desejar que o jsdom carregue esses recursos, você pode passar a opção resources: "usable"
, que carregará todos os recursos utilizáveis. São eles:
Frames e iframes, via <frame>
e <iframe>
Folhas de estilo, via <link rel="stylesheet">
Scripts, via <script>
, mas somente se runScripts: "dangerously"
também estiver definido
Imagens, via <img>
, mas somente se o pacote canvas
npm também estiver instalado (veja "Suporte ao Canvas" abaixo)
Ao tentar carregar recursos, lembre-se de que o valor padrão para a opção url
é "about:blank"
, o que significa que quaisquer recursos incluídos por meio de URLs relativos não serão carregados. (O resultado da tentativa de analisar o URL /something
em relação ao URL about:blank
é um erro.) Portanto, você provavelmente desejará definir um valor não padrão para a opção url
nesses casos ou usar um dos métodos de conveniência APIs que fazem isso automaticamente.
Para personalizar mais completamente o comportamento de carregamento de recursos do jsdom, você pode passar uma instância da classe ResourceLoader
como o valor da opção resources
:
const resourceLoader = novo jsdom.ResourceLoader ({ proxy: "http://127.0.0.1:9001", strictSSL: falso, userAgent: "Mellblomenator/9000",});const dom = new JSDOM(``, { recursos: resourceLoader });
As três opções para o construtor ResourceLoader
são:
proxy
é o endereço de um proxy HTTP a ser usado.
strictSSL
pode ser definido como false para desabilitar o requisito de que os certificados SSL sejam válidos.
userAgent
afeta o cabeçalho User-Agent
enviado e, portanto, o valor resultante para navigator.userAgent
. O padrão é `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}`
.
Você pode personalizar ainda mais a busca de recursos subclassificando ResourceLoader
e substituindo o método fetch()
. Por exemplo, aqui está uma versão que substitui a resposta fornecida para um URL específico:
classe CustomResourceLoader estende jsdom.ResourceLoader { fetch(url, options) {// Substitua o conteúdo deste script para fazer algo incomum.if (url === "https://example.com/some-specific-script.js") { return Promise.resolve( Buffer.from("window.someGlobal = 5;"));}return super.fetch(url, opções); }}
jsdom chamará o método fetch()
do seu carregador de recursos personalizado sempre que encontrar um recurso "utilizável", conforme a seção acima. O método usa uma string de URL, bem como algumas opções que você deve passar sem modificações ao chamar super.fetch()
. Ele deve retornar uma promessa para um objeto Buffer
do Node.js ou retornar null
se o recurso não for carregado intencionalmente. Em geral, a maioria dos casos desejará delegar para super.fetch()
, conforme mostrado.
Uma das opções que você receberá em fetch()
será o elemento (se aplicável) que está buscando um recurso.
classe CustomResourceLoader estende jsdom.ResourceLoader { fetch(url, options) {if (options.element) { console.log(`Element ${options.element.localName} está solicitando a url ${url}`);}return super.fetch(url, options); }}
Assim como os navegadores web, o jsdom tem o conceito de “console”. Ele registra tanto as informações enviadas diretamente da página, por meio de scripts executados dentro do documento, quanto as informações da própria implementação do jsdom. Chamamos o console controlável pelo usuário de "console virtual", para distingui-lo da API console
Node.js e da API window.console
dentro da página.
Por padrão, o construtor JSDOM
retornará uma instância com um console virtual que encaminha toda a sua saída para o console Node.js. Para criar seu próprio console virtual e passá-lo para jsdom, você pode substituir esse padrão fazendo
const virtualConsole = new jsdom.VirtualConsole();const dom = new JSDOM(``, { virtualConsole });
Código como este criará um console virtual sem comportamento. Você pode dar um comportamento adicionando ouvintes de eventos para todos os métodos de console possíveis:
virtualConsole.on("erro", () => { ... });virtualConsole.on("avisar", () => { ... });virtualConsole.on("info", () => { ... });virtualConsole.on("dir", () => { ... });// ... etc. Consulte https://console.spec.whatwg.org/#logging
(Observe que provavelmente é melhor configurar esses ouvintes de eventos antes de chamar new JSDOM()
, pois podem ocorrer erros ou script de chamada de console durante a análise.)
Se você simplesmente deseja redirecionar a saída do console virtual para outro console, como o Node.js padrão, você pode fazer
virtualConsole.sendTo(console);
Há também um evento especial, "jsdomError"
, que será acionado com objetos de erro para relatar erros do próprio jsdom. Isso é semelhante ao modo como as mensagens de erro geralmente aparecem nos consoles do navegador da Web, mesmo que não sejam iniciadas por console.error
. Até agora, os seguintes erros são gerados desta forma:
Erros ao carregar ou analisar sub-recursos (scripts, folhas de estilo, frames e iframes)
Erros de execução de script que não são tratados por um manipulador de eventos onerror
de janela que retorna true
ou chama event.preventDefault()
Erros não implementados resultantes de chamadas a métodos, como window.alert
, que o jsdom não implementa, mas instala mesmo assim para compatibilidade com a web
Se você estiver usando sendTo(c)
para enviar erros para c
, por padrão ele chamará c.error(errorStack[, errorDetail])
com informações de eventos "jsdomError"
. Se você preferir manter um mapeamento estrito de eventos um para um para chamadas de método e talvez lidar com "jsdomError"
você mesmo, então você pode fazer
virtualConsole.sendTo(c, { omitJSDOMErrors: true });
Assim como os navegadores da web, o jsdom tem o conceito de um cookie jar, armazenando cookies HTTP. Os cookies que possuem uma URL no mesmo domínio do documento e não estão marcados como somente HTTP são acessíveis por meio da API document.cookie
. Além disso, todos os cookies no cookie jar afetarão a busca de sub-recursos.
Por padrão, o construtor JSDOM
retornará uma instância com um cookie jar vazio. Para criar seu próprio cookie jar e passá-lo para jsdom, você pode substituir esse padrão fazendo
const cookieJar = new jsdom.CookieJar(loja, opções);const dom = new JSDOM(``, { cookieJar });
Isso é útil principalmente se você deseja compartilhar o mesmo pote de cookies entre vários jsdoms ou preparar o pote de cookies com determinados valores antecipadamente.
Os potes de biscoitos são fornecidos pelo pacote de biscoitos resistentes. O construtor jsdom.CookieJar
é uma subclasse do cookie jar resistente que por padrão define a opção looseMode: true
, uma vez que corresponde melhor ao comportamento dos navegadores. Se você mesmo quiser usar os utilitários e classes do hard-cookie, poderá usar a exportação do módulo jsdom.toughCookie
para obter acesso à instância do módulo hard-cookie empacotada com jsdom.
jsdom permite que você intervenha na criação de um jsdom muito cedo: depois que os objetos Window
e Document
forem criados, mas antes de qualquer HTML ser analisado para preencher o documento com nós:
const dom = new JSDOM(`<p>Olá</p>`, { beforeParse(janela) {window.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; }});
Isso é especialmente útil se você deseja modificar o ambiente de alguma forma, por exemplo, adicionando correções para APIs de plataforma web que o jsdom não suporta.
JSDOM
Depois de construir um objeto JSDOM
, ele terá os seguintes recursos úteis:
A window
de propriedades recupera o objeto Window
que foi criado para você.
As propriedades virtualConsole
e cookieJar
refletem as opções que você passa ou os padrões criados para você se nada tiver sido passado para essas opções.
serialize()
O método serialize()
retornará a serialização HTML do documento, incluindo o doctype:
const dom = new JSDOM(`<!DOCTYPE html>hello`);dom.serialize() === "<!DOCTYPE html><html><head></head><body>olá</body></ html>";// Contraste com:dom.window.document.documentElement.outerHTML === "<html><head></head><body>olá</body></html>";
nodeLocation(node)
O método nodeLocation()
encontrará onde um nó DOM está dentro do documento de origem, retornando as informações de localização parse5 para o nó:
const dom = novo JSDOM( `<p>Olá <img src="foo.jpg"> </p>`, { includeNodeLocations: true });const document = dom.window.document;const bodyEl = document.body; // implicitamente criadoconst pEl = document.querySelector("p");const textNode = pEl.firstChild;const imgEl = document.querySelector("img");console.log(dom.nodeLocation(bodyEl)); // nulo; não está em sourceconsole.log(dom.nodeLocation(pEl)); // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }console.log(dom.nodeLocation(imgEl)); // { startOffset: 13, endOffset: 32 }
Observe que esse recurso só funciona se você tiver definido a opção includeNodeLocations
; os locais dos nós estão desativados por padrão por motivos de desempenho.
vm
usando getInternalVMContext()
O módulo vm
integrado do Node.js é o que sustenta a mágica de execução de scripts do jsdom. Alguns casos de uso avançados, como pré-compilar um script e depois executá-lo várias vezes, se beneficiam do uso do módulo vm
diretamente com um Window
criado por jsdom.
Para obter acesso ao objeto global contextualizado, adequado para uso com as APIs vm
, você pode usar o método getInternalVMContext()
:
const { Script } = require("vm");const dom = new JSDOM(``, { runScripts: "outside-only" });const script = new Script(` if (!this.ran) { this.ran = 0; } ++this.ran;`);const vmContext = dom.getInternalVMContext();script.runInContext(vmContext);script.runInContext(vmContext);script.runInContext(vmContext);console.assert(dom.window.ran === 3);
Esta é uma funcionalidade um tanto avançada e recomendamos usar APIs DOM normais (como window.eval()
ou document.createElement("script")
), a menos que você tenha necessidades muito específicas.
Observe que este método lançará uma exceção se a instância JSDOM
foi criada sem runScripts
definidos ou se você estiver usando jsdom em um navegador da web.
reconfigure(settings)
A propriedade top
na window
está 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 ocultada pelo código normal em execução dentro do jsdom, mesmo usando Object.defineProperty
.
Da mesma forma, atualmente o jsdom não lida com a navegação (como a configuração window.location.href = "https://example.com/"
); fazer isso fará com que o console virtual emita um "jsdomError"
explicando que esse recurso não está implementado e nada mudará: não haverá nenhum novo objeto Window
ou Document
e o objeto location
da window
existente ainda terá todos os mesmos valores de propriedade.
No entanto, se você estiver agindo de fora da janela, por exemplo, em alguma estrutura de teste que cria jsdoms, você pode substituir um ou ambos usando o método reconfigure()
especial:
const dom = new JSDOM();dom.window.top === dom.window;dom.window.location.href === "about:blank";dom.reconfigure({ windowTop: myFakeTopForTesting, url: "https: //example.com/" });dom.window.top === myFakeTopForTesting;dom.window.location.href === "https://example.com/";
Observe que a alteração do URL do jsdom afetará todas as APIs que retornam o URL do documento atual, como window.location
, document.URL
e document.documentURI
, bem como a resolução de URLs relativos no documento e as verificações de mesma origem e referenciador usado ao buscar sub-recursos. No entanto, não realizará navegação até o conteúdo dessa URL; o conteúdo do DOM permanecerá inalterado e nenhuma nova instância de Window
, Document
, etc.
fromURL()
Além do próprio construtor JSDOM
, o jsdom fornece um método de fábrica com retorno de promessa para construir um jsdom a partir de uma URL:
JSDOM.fromURL("https://example.com/", opções).then(dom => { console.log(dom.serialize());});
A promessa retornada será cumprida com uma instância JSDOM
se a URL for válida e a solicitação for bem-sucedida. Quaisquer redirecionamentos serão seguidos até seu destino final.
As opções fornecidas para fromURL()
são semelhantes àquelas fornecidas para o construtor JSDOM
, com as seguintes restrições e consequências adicionais:
As opções url
e contentType
não podem ser fornecidas.
A opção referrer
é usada como o cabeçalho de solicitação Referer
HTTP da solicitação inicial.
A opção resources
também afeta a solicitação inicial; isso é útil se você quiser, por exemplo, configurar um proxy (veja acima).
O URL, o tipo de conteúdo e o referenciador do jsdom resultante são determinados a partir da resposta.
Quaisquer cookies definidos por meio de cabeçalhos de resposta HTTP Set-Cookie
são armazenados no jar de cookies do jsdom. Da mesma forma, quaisquer cookies que já estejam em um cookie jar fornecido são enviados como cabeçalhos de solicitação Cookie
HTTP.
fromFile()
Semelhante a fromURL()
, jsdom também fornece um método de fábrica fromFile()
para construir um jsdom a partir de um nome de arquivo:
JSDOM.fromFile("coisas.html", opções).then(dom => { console.log(dom.serialize());});
A promessa retornada será cumprida com uma instância JSDOM
se o arquivo fornecido puder ser aberto. Como de costume nas APIs Node.js, o nome do arquivo é fornecido em relação ao diretório de trabalho atual.
As opções fornecidas para fromFile()
são semelhantes àquelas fornecidas para o construtor JSDOM
, com os seguintes padrões adicionais:
A opção url
será padronizada para um URL de arquivo correspondente ao nome de arquivo fornecido, em vez de "about:blank"
.
A opção contentType
será padronizada como "application/xhtml+xml"
se o nome do arquivo fornecido terminar em .xht
, .xhtml
ou .xml
; caso contrário, continuará com o padrão "text/html"
.
fragment()
Para os casos mais simples, talvez você não precise de uma instância JSDOM
inteira com todo o seu poder associado. Talvez você nem precise de uma Window
ou Document
! Em vez disso, você só precisa analisar um pouco de HTML e obter um objeto DOM que possa manipular. Para isso temos fragment()
, que cria um DocumentFragment
a partir de uma determinada string:
const frag = JSDOM.fragment(`<p>Olá</p><p><strong>Olá!</strong>`);frag.childNodes.length === 2;frag.querySelector("strong"). textContent === "Olá!";// etc.
Aqui frag
é uma instância DocumentFragment
, cujo conteúdo é criado analisando a string fornecida. A análise é feita usando um elemento <template>
, então você pode incluir qualquer elemento lá (incluindo aqueles com regras de análise estranhas como <td>
). Também é importante observar que o DocumentFragment
resultante não terá um contexto de navegação associado: ou seja, ownerDocument
dos elementos terá uma propriedade defaultView
nula, os recursos não serão carregados, etc.
Todas as invocações da fábrica fragment()
resultam em DocumentFragment
s que compartilham o mesmo proprietário do modelo Document
. Isso permite muitas chamadas para fragment()
sem sobrecarga extra. Mas também significa que as chamadas para fragment()
não podem ser personalizadas com nenhuma opção.
Observe que a serialização não é tão fácil com DocumentFragment
s quanto com objetos JSDOM
completos. Se você precisar serializar seu DOM, provavelmente deverá usar o construtor JSDOM
mais diretamente. Mas para o caso especial de um fragmento contendo um único elemento, é muito fácil fazer isso por meios normais:
const frag = JSDOM.fragment(`<p>Olá</p>`);console.log(frag.firstChild.outerHTML); // registra "<p>Olá</p>"
jsdom inclui suporte para usar o pacote canvas
para estender quaisquer elementos <canvas>
com a API canvas. Para fazer isso funcionar, você precisa incluir canvas
como uma dependência em seu projeto, como um par de jsdom
. Se o jsdom puder encontrar o pacote canvas
, ele o utilizará, mas se não estiver presente, os elementos <canvas>
se comportarão como <div>
s. Desde o jsdom v13, a versão 2.x do canvas
é necessária; a versão 1.x não é mais suportada.
Além de fornecer uma string, o construtor JSDOM
também pode receber dados binários, na forma de um Buffer
Node.js ou um tipo de dados binários JavaScript padrão como ArrayBuffer
, Uint8Array
, DataView
, etc. a codificação dos bytes fornecidos, procurando tags <meta charset>
exatamente como um navegador faz.
Se a opção contentType
fornecida contiver um parâmetro charset
, essa codificação substituirá a codificação detectada - a menos que uma BOM UTF-8 ou UTF-16 esteja presente; nesse caso, elas têm precedência. (Novamente, isso é como um navegador.)
Essa detecção de codificação também se aplica a JSDOM.fromFile()
e JSDOM.fromURL()
. Neste último caso, quaisquer cabeçalhos Content-Type
enviados com a resposta terão prioridade, da mesma forma que a opção contentType
do construtor.
Observe que, em muitos casos, fornecer bytes dessa maneira pode ser melhor do que fornecer uma string. Por exemplo, se você tentar usar a API buffer.toString("utf-8")
do Node.js, o Node.js não removerá nenhuma BOM principal. Se você fornecer essa string ao jsdom, ele a interpretará literalmente, deixando a lista técnica intacta. Mas o código de decodificação de dados binários do jsdom removerá as listas técnicas principais, assim como um navegador; nesses casos, fornecer buffer
diretamente dará o resultado desejado.
Os temporizadores no jsdom (definidos por window.setTimeout()
ou window.setInterval()
) irão, por definição, executar o código no futuro no contexto da janela. Como não há como executar código no futuro sem manter o processo ativo, excelentes temporizadores jsdom manterão seu processo Node.js ativo. Da mesma forma, como não há como executar código no contexto de um objeto sem mantê-lo ativo, os temporizadores jsdom pendentes impedirão a coleta de lixo da janela na qual estão agendados.
Se você quiser encerrar uma janela jsdom, use window.close()
, que encerrará todos os temporizadores em execução (e também removerá quaisquer ouvintes de eventos na janela e no documento).
No Node.js você pode depurar programas usando o Chrome DevTools. Consulte a documentação oficial para saber como começar.
Por padrão, os elementos jsdom são formatados como objetos JS simples e antigos no console. Para facilitar a depuração, você pode usar jsdom-devtools-formatter, que permite inspecioná-los como elementos DOM reais.
As pessoas geralmente têm problemas com o carregamento assíncrono de scripts ao usar jsdom. Muitas páginas carregam scripts de forma assíncrona, mas não há como saber quando isso é feito e, portanto, quando é um bom momento para executar seu código e inspecionar a estrutura DOM resultante. Esta é uma limitação fundamental; não podemos prever o que os scripts da página da Web farão e, portanto, não podemos dizer quando terminarão de carregar mais scripts.
Isso pode ser contornado de algumas maneiras. A melhor maneira, se você controla a página em questão, é usar quaisquer mecanismos fornecidos pelo carregador de script para detectar quando o carregamento é concluído. Por exemplo, se você estiver usando um carregador de módulo como RequireJS, o código poderá ser semelhante a:
// No lado do Node.js:const window = (new JSDOM(...)).window;window.onModulesLoaded = () => { console.log("pronto para começar!");};
<!-- Dentro do HTML você fornece para jsdom --><script>requirejs(["entry-module"], () => { window.onModulesLoaded();});</script>
Se você não controlar a página, poderá tentar soluções alternativas, como pesquisar a presença de um elemento específico.
Para obter mais detalhes, consulte a discussão no nº 640, especialmente o comentário perspicaz de @matthewkastor.
Embora gostemos de adicionar novos recursos ao jsdom e mantê-lo atualizado com as especificações da web mais recentes, ele tem muitas APIs ausentes. Sinta-se à vontade para registrar um problema por qualquer coisa que esteja faltando, mas somos uma equipe pequena e ocupada, então uma solicitação pull pode funcionar ainda melhor.
Alguns recursos do jsdom são fornecidos por nossas dependências. Documentação notável a esse respeito inclui a lista de seletores CSS suportados para nosso mecanismo de seletor CSS, nwsapi
.
Além dos recursos que ainda não conhecemos, existem dois recursos principais que estão atualmente fora do escopo do jsdom. Estes são:
Navegação : a capacidade de alterar o objeto global, e todos os outros objetos, ao clicar em um link ou atribuir location.href
ou similar.
Layout : a capacidade de calcular onde os elementos serão dispostos visualmente como resultado do CSS, o que afeta métodos como getBoundingClientRects()
ou propriedades como offsetTop
.
Atualmente, o jsdom possui comportamentos fictícios para alguns aspectos desses recursos, como enviar um " "jsdomError"
ao console virtual para navegação ou retornar zeros para muitas propriedades relacionadas ao layout. Muitas vezes você pode contornar essas limitações em seu código, por exemplo, criando novas instâncias JSDOM
para cada página que você "navega" durante um rastreamento ou usando Object.defineProperty()
para alterar o que vários getters e métodos relacionados ao layout retornam.
Observe que outras ferramentas no mesmo espaço, como PhantomJS, oferecem suporte a esses recursos. No wiki, temos um artigo mais completo sobre jsdom vs. PhantomJS.
jsdom é um projeto comunitário mantido por uma equipe de voluntários. Você poderia apoiar jsdom:
Obtendo suporte profissional para jsdom como parte de uma assinatura do Tidelift. O Tidelift ajuda a tornar o código aberto sustentável para nós, ao mesmo tempo que dá às equipes garantias de manutenção, licenciamento e segurança.
Contribuindo diretamente para o projeto.
Se precisar de ajuda com jsdom, sinta-se à vontade para usar qualquer um dos seguintes locais:
A lista de discussão (melhor para perguntas do tipo "como faço")
O rastreador de problemas (melhor para relatórios de bugs)
A sala Matrix: #jsdom:matrix.org