Skip to content

Test DOM code with jsdom

This guide runs TypeScript tests that touch the DOM. The jsdom presets set testEnvironment: 'jsdom' and their own test file glob.

  1. Install Jest, the preset package, and the jsdom environment:

    Terminal window
    pnpm add -D jest @repobuddy/jest jest-environment-jsdom

    @repobuddy/jest does not declare jest-environment-jsdom, so your package manager will not add it for you.

  2. Install the transform for your module format:

    Terminal window
    # ESM ("type": "module"): jsdom-ts-esm
    pnpm add -D @swc/jest @swc/core cross-env
    # CommonJS: jsdom-ts-cjs
    pnpm add -D ts-jest typescript jest-esm-transformer-2
  3. Create the config. jsdom-ts picks the ESM or CommonJS preset from package.json type:

    jest.config.mjs
    export default {
    preset: '@repobuddy/jest/presets/jsdom-ts',
    }
  4. If the code imports style files, install identity-obj-proxy and add the CSS mapper:

    Terminal window
    pnpm add -D identity-obj-proxy
    jest.config.mjs
    import { fields } from '@repobuddy/jest'
    export default {
    preset: '@repobuddy/jest/presets/jsdom-ts',
    moduleNameMapper: fields.knownModuleNameMappers.cssAll,
    }

    Jest merges this moduleNameMapper with the preset’s. With identity-obj-proxy, styles.foo is the string 'foo'.

  5. Name tests <name>.spec.ts or <name>.spec.jsdom.ts. <name>.spec.node.ts files do not run in the jsdom presets. See test file names.

  6. For an ESM package, run Jest with NODE_OPTIONS=--experimental-vm-modules.

jest.config.mjs
import { fields } from '@repobuddy/jest'
export default {
preset: '@repobuddy/jest/presets/jsdom-ts',
moduleNameMapper: fields.knownModuleNameMappers.cssAll,
}

Write a test that imports a CSS module and uses document:

src/app.spec.ts
import styles from './app.module.css'
it('renders', () => {
const div = document.createElement('div')
div.className = styles.foo
document.body.append(div)
expect(document.querySelector('.foo')).toBe(div)
})

This test passed with jsdom-ts-cjs, the cssAll mapper, and Jest 30 in a scratch project. TypeScript needs a declaration for *.css imports to type-check it.