A Jest environment for running browser JavaScript tests in Node.js with Happy DOM, real 2D Canvas rendering, and additional working Web APIs.
Install the public package and select it as your Jest environment:
npm install --save-dev jest@30 jest-happy-dom-extendedWith npm 12, approve and run Skia's native installation script before running Jest:
npm approve-scripts skia-canvas
npm rebuild skia-canvas// jest.config.mjs
export default {
testEnvironment: 'jest-happy-dom-extended',
testEnvironmentOptions: { url: 'https://example.test/' },
}npx jestRequires Node.js >=22.18.0 and Jest 30. CommonJS projects can put the same configuration object in jest.config.cjs with module.exports. Your existing transforms, test files and application fixtures continue to use normal Jest configuration. Extensions are available before setupFiles and setupFilesAfterEnv run.
Before the first npm publication, or when trying an unreleased commit, use a locally built tarball. Publishing is a maintainer action; CI only validates packages.
Happy DOM supplies the DOM and browser object families. This package builds on its official Jest environment, adds missing Node-backed APIs, repairs verified compatibility gaps across Jest's VM boundary, and owns the Canvas behavior listed below. One environment setting supplies these capabilities to every test file.
| Capability | Included behavior |
|---|---|
| HTML Canvas / OffscreenCanvas | Real CPU Skia 2D drawing, paths, filters, compositing, PNG/JPEG/WebP output, attribute-driven dimension resets and asynchronous output snapshots |
| ImageData and image sources | Window-compatible pixel arrays, preserved subarray/shared storage, sRGB/display-p3 byte conversion, intrinsic image dimensions, invocation-time readiness and createImageBitmap |
| Cross-origin media | Image/video CORS and redirect checks, credentials, origin taint, protected readback/export and request cancellation |
| Bitmap transport and Workers | ImageBitmap cloning/transfer and context-free OffscreenCanvas transfer, HTML placeholder presentation, MessageChannel and actual dedicated worker_threads execution |
| Video Canvas sources | Actual decoded frames, seeking and clock-driven playback when ffmpeg/ffprobe are installed |
| Blob / File | Binary VM inputs, FileReader compatibility, UTF-8 BOM handling and bytes() |
| Streams and messaging | Encoding/compression streams, native structuredClone, MessagePort and environment-isolated BroadcastChannel |
| Events, animation and XHR | CompositionEvent text, observable animation cancellation rejection and XHR instance constants |
Application-specific mocks and fixtures stay in your tests. Real browsers remain necessary for layout, WebGL/WebGPU, browser-specific scheduling and exact browser rendering. The package guide explains configuration and lifecycle behavior; the Canvas compatibility contract records precise supported APIs, limits and comparison evidence.
The package includes skia-canvas as a required runtime dependency. Its installation must be allowed to obtain the platform-native binary. Supported builds are available for Linux, Windows and macOS; see the Skia installation guide for architecture, system-library and source-build requirements.
For pnpm:
pnpm add -D jest@30 jest-happy-dom-extended
pnpm approve-buildsApprove skia-canvas and the native scripts required by your project. Jest 30 also lists @parcel/watcher and unrs-resolver. With pnpm 12, a non-interactive project can merge this into pnpm-workspace.yaml before installation:
allowBuilds:
skia-canvas: true
'@parcel/watcher': true
unrs-resolver: trueUse the build-approval setting supported by your pnpm version. Ordinary consumers do not need the official node-canvas adapter or Cairo/Pango.
Video input also requires ffmpeg and ffprobe on PATH. These executables are used only for video; image loading and ordinary Canvas drawing do not start them. CI installs and verifies both on Linux and Windows.
Save this as canvas.test.cjs; it runs with the configuration above without a TypeScript transform.
const { expect, test } = require('@jest/globals')
test('draws a red pixel and exports PNG', async () => {
// Arrange
const canvas = document.createElement('canvas')
canvas.width = 1
canvas.height = 1
const context = canvas.getContext('2d')
// Act
context.fillStyle = 'red'
context.fillRect(0, 0, 1, 1)
const blob = await new Promise((resolve) => canvas.toBlob(resolve))
// Assert
expect([...context.getImageData(0, 0, 1, 1).data]).toEqual([255, 0, 0, 255])
expect(blob?.type).toBe('image/png')
})OffscreenCanvas offers the same 2D drawing with await canvas.convertToBlob() for output. Asynchronous exports preserve the pixels present when requested, including when a test immediately redraws or resizes. Environment teardown drains its own pending exports and releases owned media, ports and Workers.
Image loading is enabled by default. Disable it with testEnvironmentOptions.settings.enableImageFileLoading: false. Custom Canvas adapters can still be supplied programmatically; ownership remains with the caller. No extra Canvas setup import is required.
The runtime pair is pinned to Happy DOM 20.14.0, and the renderer is skia-canvas 3.0.8 in CPU mode. CI tests Node 22.18.0, 24.20.0 and 26.8.1 on Linux and Windows, including installed consumers using Jest 30.0.0 and 30.5.1, serial execution and two worker processes.
The tests check real pixels and encoded images, actual HTTP/decoder/Worker cancellation, ownership transfer, failure recovery and teardown. Shared browser fixtures measure renderer-dependent differences with explicit tolerances. This is selected conformance evidence, not a complete Web Platform Tests run. See verification and the Canvas contract.
Start with CONTRIBUTING.md, TESTING.md and ARCHITECTURE.md. Public behavior changes need an observable regression and a Changeset.
pnpm install --frozen-lockfile
pnpm checkUse the pnpm version pinned in the root package manifest. pnpm check runs source/Jest tests with coverage, lint, format, types, Sherif, Fallow, package export/type checks and isolated tarball consumers. Maintainers can follow the manual npm release guide to inspect and publish the validated tarball.
Workflows are separated into Test, Lint, Format, TypeCheck, Build, Fallow, Security, Socket and OpenSSF Scorecard. Security includes CodeQL, dependency review and a production dependency audit. Codecov receives the Linux Node 24 coverage report.
Socket scans same-repository PRs, pushes to main, its weekly schedule and manual runs, using the SOCKET_SECURITY_API_TOKEN Actions secret. Fork PRs skip that secret-dependent workflow. See Socket token setup for maintainer configuration.
| Workspace | Purpose |
|---|---|
| packages/jest-happy-dom-extended | Public Jest environment distributed on npm |
| packages/compat | Private implementation bundled into the Jest package |
| packages/vitest-happy-dom-extended | Reserved workspace; no usable Vitest environment is provided yet |
This is an independent Laststance project. It is not an official Happy DOM or Jest package. Report vulnerabilities through SECURITY.md and follow the Code of Conduct.
MIT.