skills/bun-test-dom/SKILL.md
Learn how to test DOM elements and components using Bun with happy-dom and React Testing Library
npx skillsauth add jarle/bun-skills Bun DOM testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Learn how to test DOM elements and components using Bun with happy-dom and React Testing Library
Bun's test runner plays well with existing component and DOM testing libraries, including React Testing Library and happy-dom.
For writing headless tests for your frontend code and components, we recommend happy-dom. Happy DOM implements a complete set of HTML and DOM APIs in plain JavaScript, making it possible to simulate a browser environment with high fidelity.
To get started install the @happy-dom/global-registrator package as a dev dependency.
bun add -d @happy-dom/global-registrator
We'll be using Bun's preload functionality to register the happy-dom globals before running our tests. This step will make browser APIs like document available in the global scope. Create a file called happydom.ts in the root of your project and add the following code:
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register();
To preload this file before bun test, open or create a bunfig.toml file and add the following lines.
[test]
preload = ["./happydom.ts"]
This will execute happydom.ts when you run bun test. Now you can write tests that use browser APIs like document and window.
import { test, expect } from "bun:test";
test("dom test", () => {
document.body.innerHTML = `<button>My button</button>`;
const button = document.querySelector("button");
expect(button?.innerText).toEqual("My button");
});
Depending on your tsconfig.json setup, you may see a "Cannot find name 'document'" type error in the code above. To "inject" the types for document and other browser APIs, add the following triple-slash directive to the top of any test file.
/// <reference lib="dom" />
import { test, expect } from "bun:test";
test("dom test", () => {
document.body.innerHTML = `<button>My button</button>`;
const button = document.querySelector("button");
expect(button?.innerText).toEqual("My button");
});
Let's run this test with bun test:
bun test
bun test v1.3.3
dom.test.ts:
✓ dom test [0.82ms]
1 pass
0 fail
1 expect() calls
Ran 1 tests across 1 files. 1 total [125.00ms]
Bun works seamlessly with React Testing Library for testing React components. After setting up happy-dom as shown above, you can install and use React Testing Library normally.
bun add -d @testing-library/react @testing-library/jest-dom
/// <reference lib="dom" />
import { test, expect } from 'bun:test';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
function Button({ children }: { children: React.ReactNode }) {
return <button>{children}</button>;
}
test('renders button', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
You can test custom elements and web components using the same setup:
/// <reference lib="dom" />
import { test, expect } from "bun:test";
test("custom element", () => {
// Define a custom element
class MyElement extends HTMLElement {
constructor() {
super();
this.innerHTML = "<p>Custom element content</p>";
}
}
customElements.define("my-element", MyElement);
// Use it in tests
document.body.innerHTML = "<my-element></my-element>";
const element = document.querySelector("my-element");
expect(element?.innerHTML).toBe("<p>Custom element content</p>");
});
Test DOM events and user interactions:
/// <reference lib="dom" />
import { test, expect } from "bun:test";
test("button click event", () => {
let clicked = false;
document.body.innerHTML = '<button id="test-btn">Click me</button>';
const button = document.getElementById("test-btn");
button?.addEventListener("click", () => {
clicked = true;
});
button?.click();
expect(clicked).toBe(true);
});
For more complex DOM testing setups, you can create a more comprehensive preload file:
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import "@testing-library/jest-dom";
// Register happy-dom globals
GlobalRegistrator.register();
// Add any global test configuration here
global.ResizeObserver = class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
};
// Mock other APIs as needed
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
Then update your bunfig.toml:
[test]
preload = ["./test-setup.ts"]
TypeScript errors for DOM APIs: Make sure to include the /// <reference lib="dom" /> directive at the top of your test files.
Missing globals: Ensure that @happy-dom/global-registrator is properly imported and registered in your preload file.
React component rendering issues: Make sure you've installed both @testing-library/react and have happy-dom set up correctly.
Happy-dom is fast, but for very large test suites, you might want to:
beforeEach to reset the DOM state between testscleanup functions from testing librariesimport { afterEach } from "bun:test";
import { cleanup } from "@testing-library/react";
afterEach(() => {
cleanup();
document.body.innerHTML = "";
});
development
Using TypeScript with Bun, including type definitions and compiler options
development
Learn how to write tests using Bun's Jest-compatible API with support for async tests, timeouts, and various test modifiers
testing
Learn how to use snapshot testing in Bun to save and compare output between test runs
testing
Learn about Bun test's runtime integration, environment variables, timeouts, and error handling