227 lines
6.1 KiB
Plaintext
227 lines
6.1 KiB
Plaintext
---
|
|
title: "DOM testing"
|
|
description: "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.
|
|
|
|
## happy-dom
|
|
|
|
For headless tests of your frontend code and components, we recommend happy-dom. It implements a complete set of HTML and DOM APIs in plain JavaScript, so it can simulate a browser environment with high fidelity.
|
|
|
|
Install the `@happy-dom/global-registrator` package as a dev dependency.
|
|
|
|
```bash terminal icon="terminal"
|
|
bun add -d @happy-dom/global-registrator
|
|
```
|
|
|
|
Use Bun's preload feature to register the happy-dom globals before your tests run, which makes browser APIs like `document` available in the global scope. Create a file called `happydom.ts` in the root of your project with the following code:
|
|
|
|
```ts title="happydom.ts" icon="/icons/typescript.svg"
|
|
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.
|
|
|
|
```toml title="bunfig.toml" icon="settings"
|
|
[test]
|
|
preload = ["./happydom.ts"]
|
|
```
|
|
|
|
`bun test` now executes `happydom.ts` before your tests, so they can use browser APIs like `document` and `window`.
|
|
|
|
```ts title="dom.test.ts" icon="/icons/typescript.svg"
|
|
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");
|
|
});
|
|
```
|
|
|
|
### TypeScript Support
|
|
|
|
Depending on your `tsconfig.json` setup, you may see a "Cannot find name 'document'" type error in the earlier code. To load the types for `document` and other browser APIs, add the following triple-slash directive to the top of any test file.
|
|
|
|
```ts title="dom.test.ts" icon="/icons/typescript.svg"
|
|
/// <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");
|
|
});
|
|
```
|
|
|
|
Run the test with `bun test`:
|
|
|
|
```bash terminal icon="terminal"
|
|
bun test
|
|
```
|
|
|
|
```
|
|
bun test v1.3.3
|
|
|
|
dom.test.ts:
|
|
✓ dom test [0.82ms]
|
|
|
|
1 pass
|
|
0 fail
|
|
1 expect() calls
|
|
Ran 1 test across 1 file. [125.00ms]
|
|
```
|
|
|
|
## React Testing Library
|
|
|
|
Bun works with React Testing Library for testing React components. After setting up happy-dom as described earlier, install and use React Testing Library normally.
|
|
|
|
```bash terminal icon="terminal"
|
|
bun add -d @testing-library/react @testing-library/jest-dom
|
|
```
|
|
|
|
```ts title="component.test.tsx" icon="/icons/typescript.svg"
|
|
/// <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');
|
|
});
|
|
```
|
|
|
|
## Advanced DOM Testing
|
|
|
|
### Custom Elements
|
|
|
|
Test custom elements and web components with the same setup:
|
|
|
|
```ts title="custom-element.test.ts" icon="/icons/typescript.svg"
|
|
/// <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>");
|
|
});
|
|
```
|
|
|
|
### Event Testing
|
|
|
|
Test DOM events and user interactions:
|
|
|
|
```ts title="events.test.ts" icon="/icons/typescript.svg"
|
|
/// <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);
|
|
});
|
|
```
|
|
|
|
## Configuration Tips
|
|
|
|
### Global Setup
|
|
|
|
For more involved setups, create a preload file that also registers global mocks:
|
|
|
|
```ts title="test-setup.ts" icon="/icons/typescript.svg"
|
|
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`:
|
|
|
|
```toml title="bunfig.toml" icon="settings"
|
|
[test]
|
|
preload = ["./test-setup.ts"]
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**TypeScript errors for DOM APIs**: Include the `/// <reference lib="dom" />` directive at the top of your test files.
|
|
|
|
**Missing globals**: Check that your preload file imports and registers `@happy-dom/global-registrator`.
|
|
|
|
**React component rendering issues**: Check that `@testing-library/react` is installed and happy-dom is set up.
|
|
|
|
### Performance Considerations
|
|
|
|
happy-dom is fast, but for very large test suites you may want to:
|
|
|
|
- Use `beforeEach` to reset the DOM state between tests
|
|
- Avoid creating too many DOM elements in a single test
|
|
- Use `cleanup` functions from testing libraries
|
|
|
|
```ts title="test-setup.ts" icon="/icons/typescript.svg"
|
|
import { afterEach } from "bun:test";
|
|
import { cleanup } from "@testing-library/react";
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
document.body.innerHTML = "";
|
|
});
|
|
```
|