0
# @testing-library/dom
1
2
Simple and complete DOM testing utilities that encourage good testing practices by querying the DOM in the way users find elements. Promotes accessibility-focused testing through queries based on ARIA roles, labels, text content, and other accessible attributes.
3
4
## Package Information
5
6
```json
7
{
8
"name": "@testing-library/dom",
9
"version": "10.4.1",
10
"install": "npm install --save-dev @testing-library/dom"
11
}
12
```
13
14
## Core Imports
15
16
```javascript
17
import {screen, within, waitFor, fireEvent} from '@testing-library/dom';
18
19
// All query methods
20
import {
21
getByRole, queryByRole, findByRole,
22
getByLabelText, queryByLabelText, findByLabelText,
23
getByPlaceholderText, queryByPlaceholderText, findByPlaceholderText,
24
getByText, queryByText, findByText,
25
getByAltText, queryByAltText, findByAltText,
26
getByTitle, queryByTitle, findByTitle,
27
getByDisplayValue, queryByDisplayValue, findByDisplayValue,
28
getByTestId, queryByTestId, findByTestId
29
} from '@testing-library/dom';
30
31
// Plural variants
32
import {
33
getAllBy*, queryAllBy*, findAllBy*
34
} from '@testing-library/dom';
35
36
// Namespace imports for grouped access
37
import {queries, queryHelpers} from '@testing-library/dom';
38
39
// Access queries through namespace
40
const element = queries.getByRole(container, 'button', {name: /submit/i});
41
42
// Access query helpers through namespace
43
const customQuery = queryHelpers.buildQueries(...);
44
```
45
46
## Core Concepts
47
48
### Query Variants
49
50
Every query has three variants with different error handling:
51
52
- **`getBy*`** - Returns element, throws if 0 or 2+ matches found
53
- **`queryBy*`** - Returns element or null if not found, throws if 2+ found
54
- **`findBy*`** - Returns Promise, waits up to 1000ms (async)
55
56
Plural variants return arrays and have different throwing behavior.
57
58
### Query Priority
59
60
Use queries in this order (most accessible to least):
61
62
1. `getByRole` - Accessible to everyone (ARIA roles)
63
2. `getByLabelText` - Form fields
64
3. `getByPlaceholderText` - Form alternative
65
4. `getByText` - Non-interactive content
66
5. `getByDisplayValue` - Current form values
67
6. `getByAltText` - Images
68
7. `getByTitle` - Title attribute
69
8. `getByTestId` - Last resort
70
71
### Screen vs Container
72
73
```javascript
74
// Use screen - queries entire document
75
const button = screen.getByRole('button', {name: /submit/i});
76
77
// Use within for scoped queries
78
const form = screen.getByRole('form');
79
const emailInput = within(form).getByLabelText('Email');
80
```
81
82
## Quick Start Examples
83
84
```javascript
85
import {screen, fireEvent, waitFor} from '@testing-library/dom';
86
87
// Basic query
88
const button = screen.getByRole('button', {name: /submit/i});
89
fireEvent.click(button);
90
91
// Form interaction
92
const email = screen.getByLabelText('Email');
93
fireEvent.change(email, {target: {value: 'user@example.com'}});
94
95
// Async content
96
await waitFor(() => {
97
expect(screen.getByText('Success')).toBeInTheDocument();
98
});
99
100
// Or use findBy directly
101
const message = await screen.findByText('Success');
102
```
103
104
## Architecture
105
106
### Query System
107
- **8 query types**: Role, LabelText, PlaceholderText, Text, DisplayValue, AltText, Title, TestId
108
- **3 variants each**: get, query, find (plus plural variants)
109
- **Screen**: Global queries bound to document.body
110
- **Within**: Scoped queries to containers
111
112
### Async Utilities
113
- `waitFor`: Poll with MutationObserver support
114
- `waitForElementToBeRemoved`: Wait for element removal
115
116
### Events
117
- `fireEvent`: Fire DOM events on elements
118
- `createEvent`: Create events without firing
119
120
### Utilities
121
- **Debugging**: `prettyDOM`, `logDOM`, `screen.debug`, `logTestingPlaygroundURL`
122
- **Configuration**: `configure`, `getConfig`
123
- **Role tools**: `getRoles`, `logRoles`, `isInaccessible`, `computeHeadingLevel`
124
- **Query builders**: `buildQueries`, `queryByAttribute`, `queryAllByAttribute`
125
- **Text tools**: `getNodeText`, `getDefaultNormalizer`
126
- **Query suggestions**: `getSuggestedQuery` for better query recommendations
127
128
## Detailed Documentation
129
130
- [Queries](./queries.md) - All query methods and types
131
- [Events](./events.md) - Event simulation
132
- [Async](./async.md) - Async utilities
133
- [Screen](./screen.md) - Screen object API
134
- [Within](./within.md) - Scoped queries
135
- [Config](./config.md) - Configuration
136
- [Debugging](./debugging.md) - Debug utilities
137
- [Query Helpers](./query-helpers.md) - Custom queries
138
- [Role Utilities](./role-utilities.md) - ARIA utilities
139
140
## Additional Utilities
141
142
### Text Utilities
143
144
Extract text content as users perceive it.
145
146
```typescript
147
function getNodeText(node: HTMLElement): string;
148
```
149
150
Usage:
151
```javascript
152
import {getNodeText} from '@testing-library/dom';
153
154
const element = screen.getByRole('button');
155
const text = getNodeText(element); // Returns visible text content
156
```
157
158
### Matcher Utilities
159
160
Text normalization for customizing query behavior.
161
162
```typescript
163
function getDefaultNormalizer(
164
options?: DefaultNormalizerOptions
165
): NormalizerFn;
166
167
interface DefaultNormalizerOptions {
168
trim?: boolean; // Default: true
169
collapseWhitespace?: boolean; // Default: true
170
}
171
172
type NormalizerFn = (text: string) => string;
173
```
174
175
Usage:
176
```javascript
177
import {getDefaultNormalizer, getByText} from '@testing-library/dom';
178
179
// Custom normalizer
180
const normalizer = getDefaultNormalizer({trim: false});
181
const element = getByText(container, 'Text', {normalizer});
182
183
// Case-insensitive matching
184
const caseInsensitive = (text) => text.toLowerCase();
185
const element = getByText(container, 'submit', {normalizer: caseInsensitive});
186
```
187
188
### Query Suggestions
189
190
Get suggested better queries for improving test quality.
191
192
```typescript
193
function getSuggestedQuery(
194
element: HTMLElement,
195
variant?: Variant,
196
method?: Method
197
): Suggestion | undefined;
198
199
interface Suggestion {
200
queryName: string;
201
queryMethod: string;
202
queryArgs: QueryArgs;
203
variant: string;
204
warning?: string;
205
toString(): string;
206
}
207
208
type Variant = 'find' | 'findAll' | 'get' | 'getAll' | 'query' | 'queryAll';
209
210
type Method = 'Role' | 'LabelText' | 'PlaceholderText' | 'Text' |
211
'DisplayValue' | 'AltText' | 'Title' | 'TestId';
212
```
213
214
Usage:
215
```javascript
216
import {getSuggestedQuery} from '@testing-library/dom';
217
218
const button = document.querySelector('button');
219
const suggestion = getSuggestedQuery(button, 'get');
220
221
if (suggestion) {
222
console.log(suggestion.toString());
223
// "getByRole('button', {name: /submit/i})"
224
}
225
226
// Use with throwSuggestions config
227
import {configure} from '@testing-library/dom';
228
configure({throwSuggestions: true});
229
230
// Errors now include better query suggestions
231
```
232
233
## TypeScript Support
234
235
All APIs are fully typed. The library provides TypeScript definitions with generic types for element narrowing.
236
237
```typescript
238
import {screen} from '@testing-library/dom';
239
240
// Automatically typed as HTMLElement
241
const button = screen.getByRole('button');
242
243
// Narrow to specific element type
244
const button = screen.getByRole('button') as HTMLButtonElement;
245
246
// Or use generic
247
const button = screen.getByRole<HTMLButtonElement>('button');
248
```
249
250
## Common Patterns
251
252
### Form Testing
253
254
```javascript
255
import {screen, fireEvent} from '@testing-library/dom';
256
257
// Fill form
258
fireEvent.change(screen.getByLabelText('Name'), {target: {value: 'John'}});
259
fireEvent.change(screen.getByLabelText('Email'), {target: {value: 'john@example.com'}});
260
fireEvent.click(screen.getByRole('button', {name: /submit/i}));
261
262
// Verify result
263
expect(screen.getByText('Thank you')).toBeInTheDocument();
264
```
265
266
### Modal Testing
267
268
```javascript
269
import {screen, within, fireEvent} from '@testing-library/dom';
270
271
fireEvent.click(screen.getByRole('button', {name: /open/i}));
272
273
const modal = screen.getByRole('dialog');
274
expect(within(modal).getByText('Modal Title')).toBeInTheDocument();
275
276
fireEvent.click(within(modal).getByRole('button', {name: /close/i}));
277
```
278
279
### Async Content
280
281
```javascript
282
import {screen, fireEvent, waitFor} from '@testing-library/dom';
283
284
fireEvent.click(screen.getByRole('button', {name: /load/i}));
285
286
// Wait for loading to complete
287
await waitFor(() => {
288
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
289
});
290
291
// Verify loaded content
292
expect(screen.getByText('Data loaded')).toBeInTheDocument();
293
```
294
295
## Matchers
296
297
All text-based queries accept a `Matcher` type:
298
299
```typescript
300
type Matcher = string | RegExp | number | MatcherFunction;
301
302
type MatcherFunction = (content: string, element: Element | null) => boolean;
303
304
type ByRoleMatcher = ARIARole | string;
305
306
// Matcher options
307
interface MatcherOptions {
308
exact?: boolean; // Exact string match (default: false)
309
trim?: boolean; // Trim whitespace (default: true)
310
collapseWhitespace?: boolean; // Collapse whitespace (default: true)
311
normalizer?: NormalizerFn; // Custom text normalizer
312
suggest?: boolean; // Include query suggestions in errors
313
}
314
315
interface SelectorMatcherOptions extends MatcherOptions {
316
selector?: string; // CSS selector to narrow results
317
ignore?: boolean | string; // CSS selectors to ignore
318
}
319
```
320
321
Examples:
322
```javascript
323
// String (substring match)
324
getByText(container, 'Submit');
325
326
// Regex
327
getByText(container, /submit/i);
328
329
// Number
330
getByText(container, 42);
331
332
// Function
333
getByText(container, (content, el) => content.startsWith('Total:'));
334
335
// Exact match
336
getByText(container, 'Submit', {exact: true});
337
338
// With selector
339
getByText(container, 'Click me', {selector: 'button'});
340
341
// With normalizer
342
getByText(container, 'submit', {
343
normalizer: str => str.toLowerCase()
344
});
345
```
346
347
## Core Types
348
349
Common type definitions used throughout the library:
350
351
```typescript
352
// Query function types
353
type BoundFunction<T> = T extends (
354
container: HTMLElement,
355
...args: infer P
356
) => infer R
357
? (...args: P) => R
358
: never;
359
360
interface Queries {
361
[T: string]: Query;
362
}
363
364
type Query = (
365
container: HTMLElement,
366
...args: any[]
367
) => Error | HTMLElement | HTMLElement[] | Promise<HTMLElement[]> | Promise<HTMLElement> | null;
368
369
// Wait options for async utilities
370
interface waitForOptions {
371
container?: HTMLElement; // Element to observe (default: document)
372
timeout?: number; // Max wait in ms (default: 1000)
373
interval?: number; // Polling interval in ms (default: 50)
374
onTimeout?: (error: Error) => Error; // Custom timeout error
375
mutationObserverOptions?: MutationObserverInit;
376
}
377
378
// Query arguments
379
type QueryArgs = [string, QueryOptions?];
380
381
interface QueryOptions {
382
[key: string]: RegExp | boolean;
383
}
384
```
385
386