Why I use Node as the default testing environment instead of JSDOM

A while ago I was updating the Node version from 18 to 20 on Playground.

This felt like a good time to review our Node polyfills and I found that we were pollyfilling URL.canParse because it wasn’t available in Node 18. But, when I removed the URL.canParse polyfill, unit tests started failing.

JSDOM removes some Node features without replacing them

After some research I found out that JSDOM creates an environment that overrides some existing Node.js globals with its polyfills.
One of the globals it replaces is URL and it replaces it with whatwg-url.

As a result, when we run tests with JSDOM they might fail because the JSDOM environment isn’t fully compatible with Node.

Example of JSDOM removing Node features

If you run this code in Node 20 which supports URL.canParse and JSDOM 22 or older, URL.canParse will be undefined because JSDOM added support for it in version 23.

This was the point when I decided to avoid JSDOM. Instead of reusing existing, working Node features, JSDOM removes them and polyfills only some features.

const jsdom = require("jsdom");
const { JSDOM } = jsdom;

const myDom = new JSDOM(`<body>
  <div id="content"></div>
  <script>document.getElementById("content").innerHTML = typeof URL.canParse;</script>
</body>`, { runScripts: "dangerously" });

console.log(myDom.window.document.body.innerHTML);

Node is a better default test environment than JSDOM

Because most Playground packages are Node compatible, we decided to use Node as the environment for running tests.

Now looking at it, there wasn’t any need to use JSDOM to test Node compatible packages in the first place, but at least I learned something.

Some tests still need Web API support

Using Node worked for all packages, except the web version of Playground because these packages depend on Web APIs, and still need to run in a Web-like environment.

Vitest lists only two alternative web environments, happy-dom, it’s considered to be faster than JSDOM, but lacks some APIs and Browser Mode which is still experimental.

We decided to keep using JSDOM for the packages that require Web APIs.

JSDOM is a emulator and isn’t fully compatible with Web APIs

These days I still use JSDOM when I need Web API support in test, but I keep a few thing in mind.

  • JSDOM is just a web emulator that sits on top of the Node environment.
  • JSDOM isn’t fully compatible with Web APIs, and polyfill may be necessary.
  • Some functions are different between environments, so it’s possible to get a different response from a function call in Node and JSDOM.
  • JSDOM deliberately removes Node features even if it doesn’t support these features.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *