0
# Utilities
1
2
Text processing, DOM debugging, role helpers, and other utility functions for enhanced testing capabilities. These utilities provide support for text extraction, accessibility analysis, DOM formatting, and query optimization.
3
4
## Capabilities
5
6
### Text Processing
7
8
Extract and normalize text content from DOM nodes.
9
10
```typescript { .api }
11
function getNodeText(node: HTMLElement): string;
12
13
function getDefaultNormalizer(
14
options?: DefaultNormalizerOptions,
15
): NormalizerFn;
16
17
interface DefaultNormalizerOptions {
18
trim?: boolean;
19
collapseWhitespace?: boolean;
20
}
21
22
type NormalizerFn = (text: string) => string;
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import { getNodeText, getDefaultNormalizer } from "@testing-library/dom";
29
30
// Extract text from DOM node
31
const button = document.querySelector('button');
32
const buttonText = getNodeText(button);
33
console.log(buttonText); // "Click me"
34
35
// Extract text from complex nested structure
36
const card = document.querySelector('.card');
37
const cardText = getNodeText(card);
38
// Returns all text content including nested elements
39
40
// Create custom normalizer
41
const normalizer = getDefaultNormalizer({
42
trim: true,
43
collapseWhitespace: true,
44
});
45
46
const messyText = " Multiple spaces\n\nand newlines ";
47
const cleanText = normalizer(messyText);
48
console.log(cleanText); // "Multiple spaces and newlines"
49
50
// Use normalizer in queries
51
const element = getByText(container, "Some text", {
52
normalizer: getDefaultNormalizer({ trim: false }),
53
});
54
```
55
56
### DOM Debugging and Pretty Printing
57
58
Format DOM elements for debugging and console output.
59
60
```typescript { .api }
61
function prettyDOM(
62
dom?: Element | HTMLDocument,
63
maxLength?: number,
64
options?: PrettyDOMOptions,
65
): string | false;
66
67
function logDOM(
68
dom?: Element | HTMLDocument,
69
maxLength?: number,
70
options?: PrettyDOMOptions,
71
): void;
72
73
interface PrettyDOMOptions extends prettyFormat.OptionsReceived {
74
filterNode?: (node: Node) => boolean;
75
}
76
```
77
78
**Usage Examples:**
79
80
```typescript
81
import { prettyDOM, logDOM } from "@testing-library/dom";
82
83
// Format entire document
84
const formatted = prettyDOM(document.body);
85
console.log(formatted);
86
87
// Format specific element
88
const modal = document.querySelector('[role="dialog"]');
89
const modalHTML = prettyDOM(modal);
90
91
// Limit output length
92
const shortFormat = prettyDOM(modal, 500);
93
94
// Log to console directly
95
logDOM(modal); // Prints formatted HTML to console
96
97
// Custom filtering
98
const customFormatted = prettyDOM(modal, undefined, {
99
filterNode: (node) => {
100
// Exclude script and style tags
101
return !['SCRIPT', 'STYLE'].includes(node.nodeName);
102
},
103
});
104
105
// Use in test debugging
106
test("modal content", () => {
107
render(App);
108
109
const modal = getByRole(document.body, "dialog");
110
111
// Debug when test fails
112
console.log("Modal content:", prettyDOM(modal));
113
114
// Or log directly
115
logDOM(modal);
116
});
117
118
// Format with styling options
119
const styledFormat = prettyDOM(element, undefined, {
120
highlight: true,
121
theme: {
122
tag: 'cyan',
123
content: 'reset',
124
prop: 'yellow',
125
},
126
});
127
```
128
129
### Role and Accessibility Helpers
130
131
Analyze accessibility roles and states of DOM elements.
132
133
```typescript { .api }
134
function getRoles(container: HTMLElement): {
135
[index: string]: HTMLElement[];
136
};
137
138
function logRoles(
139
container: HTMLElement,
140
options?: LogRolesOptions,
141
): string;
142
143
interface LogRolesOptions {
144
hidden?: boolean;
145
}
146
147
function isInaccessible(element: Element): boolean;
148
149
function computeHeadingLevel(element: Element): number | undefined;
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import { getRoles, logRoles, isInaccessible, computeHeadingLevel } from "@testing-library/dom";
156
157
// Get all roles in container
158
const container = document.body;
159
const roles = getRoles(container);
160
161
console.log(roles);
162
// {
163
// button: [<button>Submit</button>, <button>Cancel</button>],
164
// textbox: [<input type="text">],
165
// heading: [<h1>Title</h1>, <h2>Subtitle</h2>],
166
// list: [<ul>...</ul>],
167
// listitem: [<li>Item 1</li>, <li>Item 2</li>]
168
// }
169
170
// Log roles to console
171
logRoles(container);
172
// Prints formatted list of all roles and their elements
173
174
// Include hidden elements
175
logRoles(container, { hidden: true });
176
177
// Check if element is accessible
178
const hiddenDiv = document.querySelector('.visually-hidden');
179
const isHidden = isInaccessible(hiddenDiv);
180
console.log(isHidden); // true or false
181
182
// Get heading level
183
const heading = document.querySelector('h2');
184
const level = computeHeadingLevel(heading);
185
console.log(level); // 2
186
187
const ariaHeading = document.querySelector('[role="heading"][aria-level="3"]');
188
const ariaLevel = computeHeadingLevel(ariaHeading);
189
console.log(ariaLevel); // 3
190
191
// Accessibility testing workflow
192
function analyzeAccessibility(container) {
193
console.log("=== Accessibility Analysis ===");
194
195
// Show all roles
196
console.log("Available roles:");
197
logRoles(container);
198
199
// Check for inaccessible elements
200
const allElements = container.querySelectorAll('*');
201
const inaccessibleElements = Array.from(allElements).filter(isInaccessible);
202
203
console.log("Inaccessible elements:", inaccessibleElements.length);
204
205
// Analyze heading structure
206
const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');
207
console.log("Heading structure:");
208
headings.forEach(heading => {
209
const level = computeHeadingLevel(heading);
210
const text = getNodeText(heading);
211
console.log(` Level ${level}: ${text}`);
212
});
213
}
214
```
215
216
### Query Suggestions
217
218
Get recommended queries for elements to improve test maintainability.
219
220
```typescript { .api }
221
function getSuggestedQuery(
222
element: HTMLElement,
223
variant?: Variant,
224
method?: Method,
225
): Suggestion | undefined;
226
227
interface Suggestion {
228
queryName: string;
229
queryMethod: string;
230
queryArgs: QueryArgs;
231
variant: string;
232
warning?: string;
233
toString(): string;
234
}
235
236
type Variant = 'find' | 'findAll' | 'get' | 'getAll' | 'query' | 'queryAll';
237
type Method = 'AltText' | 'alttext' | 'DisplayValue' | 'displayvalue' | 'LabelText' | 'labeltext' | 'PlaceholderText' | 'placeholdertext' | 'Role' | 'role' | 'TestId' | 'testid' | 'Text' | 'text' | 'Title' | 'title';
238
type QueryArgs = [string, QueryOptions?];
239
240
interface QueryOptions {
241
[key: string]: RegExp | boolean;
242
}
243
```
244
245
**Usage Examples:**
246
247
```typescript
248
import { getSuggestedQuery } from "@testing-library/dom";
249
250
// Get suggestion for button element
251
const button = document.querySelector('button[data-testid="submit-btn"]');
252
const suggestion = getSuggestedQuery(button);
253
254
if (suggestion) {
255
console.log(suggestion.toString());
256
// Might suggest: getByRole('button', { name: 'Submit' })
257
// instead of getByTestId('submit-btn')
258
259
console.log("Query method:", suggestion.queryMethod);
260
console.log("Query args:", suggestion.queryArgs);
261
262
if (suggestion.warning) {
263
console.log("Warning:", suggestion.warning);
264
}
265
}
266
267
// Get specific variant suggestion
268
const getAllSuggestion = getSuggestedQuery(button, 'getAll');
269
const findSuggestion = getSuggestedQuery(button, 'find');
270
271
// Get suggestion for specific method
272
const roleSuggestion = getSuggestedQuery(button, 'get', 'Role');
273
const textSuggestion = getSuggestedQuery(button, 'get', 'Text');
274
275
// Use in test optimization
276
function optimizeQuery(element) {
277
const suggestion = getSuggestedQuery(element);
278
279
if (suggestion) {
280
console.log("Current query could be improved:");
281
console.log("Suggested:", suggestion.toString());
282
283
if (suggestion.warning) {
284
console.log("Note:", suggestion.warning);
285
}
286
} else {
287
console.log("No better query suggestion available");
288
}
289
}
290
291
// Batch analyze elements
292
function analyzeQueries(container) {
293
const testIdElements = container.querySelectorAll('[data-testid]');
294
295
console.log("Query optimization suggestions:");
296
testIdElements.forEach((element, index) => {
297
const testId = element.getAttribute('data-testid');
298
const suggestion = getSuggestedQuery(element);
299
300
console.log(`\n${index + 1}. Element with testid="${testId}"`);
301
302
if (suggestion) {
303
console.log(` Better query: ${suggestion.toString()}`);
304
} else {
305
console.log(" No better alternative found");
306
}
307
});
308
}
309
```
310
311
### Query Building Utilities
312
313
Low-level utilities for building custom queries.
314
315
```typescript { .api }
316
type QueryByAttribute = (
317
attribute: string,
318
container: HTMLElement,
319
id: Matcher,
320
options?: MatcherOptions,
321
) => HTMLElement | null;
322
323
type AllByAttribute = (
324
attribute: string,
325
container: HTMLElement,
326
id: Matcher,
327
options?: MatcherOptions,
328
) => HTMLElement[];
329
330
declare const queryByAttribute: QueryByAttribute;
331
declare const queryAllByAttribute: AllByAttribute;
332
333
function getElementError(
334
message: string | null,
335
container: HTMLElement,
336
): Error;
337
338
function buildQueries<Arguments extends any[]>(
339
queryAllBy: GetAllBy<Arguments>,
340
getMultipleError: GetErrorFunction<Arguments>,
341
getMissingError: GetErrorFunction<Arguments>,
342
): BuiltQueryMethods<Arguments>;
343
344
type BuiltQueryMethods<Arguments extends any[]> = [
345
QueryBy<Arguments>,
346
GetAllBy<Arguments>,
347
GetBy<Arguments>,
348
FindAllBy<Arguments>,
349
FindBy<Arguments>,
350
];
351
```
352
353
**Usage Examples:**
354
355
```typescript
356
import { queryByAttribute, queryAllByAttribute, buildQueries } from "@testing-library/dom";
357
358
// Query by custom attribute
359
const elementByDataId = queryByAttribute('data-id', container, 'user-123');
360
const elementsByClass = queryAllByAttribute('class', container, 'btn-primary');
361
362
// Build custom query set
363
const queryAllByDataId = (container, id, options) =>
364
queryAllByAttribute('data-id', container, id, options);
365
366
const getMultipleError = (container, id) =>
367
`Found multiple elements with data-id="${id}"`;
368
369
const getMissingError = (container, id) =>
370
`Unable to find element with data-id="${id}"`;
371
372
const [
373
queryByDataId,
374
getAllByDataId,
375
getByDataId,
376
findAllByDataId,
377
findByDataId,
378
] = buildQueries(queryAllByDataId, getMultipleError, getMissingError);
379
380
// Use custom queries
381
const user = getByDataId(container, 'user-123');
382
const allUsers = getAllByDataId(container, /user-\d+/);
383
const maybeUser = queryByDataId(container, 'user-456');
384
```
385
386
## Types
387
388
```typescript { .api }
389
// Text processing types
390
type NormalizerFn = (text: string) => string;
391
392
interface DefaultNormalizerOptions {
393
trim?: boolean;
394
collapseWhitespace?: boolean;
395
}
396
397
// Pretty DOM types
398
interface PrettyDOMOptions extends prettyFormat.OptionsReceived {
399
filterNode?: (node: Node) => boolean;
400
}
401
402
// Role helpers types
403
interface LogRolesOptions {
404
hidden?: boolean;
405
}
406
407
// Suggestion types
408
interface Suggestion {
409
queryName: string;
410
queryMethod: string;
411
queryArgs: QueryArgs;
412
variant: string;
413
warning?: string;
414
toString(): string;
415
}
416
417
type Variant = 'find' | 'findAll' | 'get' | 'getAll' | 'query' | 'queryAll';
418
type Method = 'AltText' | 'alttext' | 'DisplayValue' | 'displayvalue' | 'LabelText' | 'labeltext' | 'PlaceholderText' | 'placeholdertext' | 'Role' | 'role' | 'TestId' | 'testid' | 'Text' | 'text' | 'Title' | 'title';
419
type QueryArgs = [string, QueryOptions?];
420
421
interface QueryOptions {
422
[key: string]: RegExp | boolean;
423
}
424
425
// Query building types
426
type QueryByAttribute = (
427
attribute: string,
428
container: HTMLElement,
429
id: Matcher,
430
options?: MatcherOptions,
431
) => HTMLElement | null;
432
433
type AllByAttribute = (
434
attribute: string,
435
container: HTMLElement,
436
id: Matcher,
437
options?: MatcherOptions,
438
) => HTMLElement[];
439
440
type GetErrorFunction<Arguments extends any[] = [string]> = (
441
c: Element | null,
442
...args: Arguments
443
) => string;
444
```