0
# Configuration and Utilities
1
2
Configuration options and utility functions for customizing Testing Library behavior and building custom queries. These utilities provide advanced functionality for debugging, configuration, and extending the library's capabilities.
3
4
## Capabilities
5
6
### Configuration
7
8
Global configuration options for customizing Testing Library behavior.
9
10
```typescript { .api }
11
/**
12
* Configure global Testing Library options
13
* @param options - Configuration options to set
14
*/
15
function configure(options: ConfigureOptions): void;
16
17
/**
18
* Get current Testing Library configuration
19
* @returns Current configuration object
20
*/
21
function getConfig(): Config;
22
23
/**
24
* Configuration options for Testing Library
25
*/
26
interface ConfigureOptions {
27
/** Custom attribute to use for test IDs (default: 'data-testid') */
28
testIdAttribute?: string;
29
/** Default timeout for async utilities in milliseconds (default: 1000) */
30
asyncUtilTimeout?: number;
31
/** Whether computed styles support pseudo-elements (default: false) */
32
computedStyleSupportsPseudoElements?: boolean;
33
/** Whether hidden elements should be ignored by default (default: false) */
34
defaultHidden?: boolean;
35
/** Show original stack trace in error messages (default: false) */
36
showOriginalStackTrace?: boolean;
37
/** Custom normalizer function for text matching */
38
defaultNormalizer?: (text: string) => string;
39
/** Ignore specific elements in queries (default: 'script, style') */
40
defaultIgnore?: string;
41
/** Throw errors on multiple elements found (default: true) */
42
throwSuggestions?: boolean;
43
}
44
45
/**
46
* Current configuration object
47
*/
48
interface Config {
49
testIdAttribute: string;
50
asyncUtilTimeout: number;
51
computedStyleSupportsPseudoElements: boolean;
52
defaultHidden: boolean;
53
showOriginalStackTrace: boolean;
54
defaultNormalizer: (text: string) => string;
55
defaultIgnore: string;
56
throwSuggestions: boolean;
57
}
58
```
59
60
### Query Building
61
62
Utilities for building custom query functions that follow Testing Library patterns.
63
64
```typescript { .api }
65
/**
66
* Build a complete set of query functions from a base queryAllBy function
67
* @param queryAllBy - Function that returns all matching elements
68
* @param getMultipleError - Function to generate error when multiple elements found
69
* @param getMissingError - Function to generate error when no elements found
70
* @returns Complete set of query functions (getBy, getAllBy, queryBy, queryAllBy, findBy, findAllBy)
71
*/
72
function buildQueries<T extends (...args: any[]) => HTMLElement[]>(
73
queryAllBy: T,
74
getMultipleError: (container: HTMLElement, ...args: Parameters<T>) => string,
75
getMissingError: (container: HTMLElement, ...args: Parameters<T>) => string
76
): QueryHelpers<T>;
77
78
/**
79
* Generated query functions from buildQueries
80
*/
81
interface QueryHelpers<T extends (...args: any[]) => HTMLElement[]> {
82
queryBy: (...args: Parameters<T>) => HTMLElement | null;
83
queryAllBy: T;
84
getBy: (...args: Parameters<T>) => HTMLElement;
85
getAllBy: T;
86
findBy: (...args: Parameters<T>) => Promise<HTMLElement>;
87
findAllBy: (...args: Parameters<T>) => Promise<HTMLElement[]>;
88
}
89
90
/**
91
* Object containing query helper functions
92
*/
93
declare const queryHelpers: {
94
buildQueries: typeof buildQueries;
95
getElementError: (message: string, container: HTMLElement) => Error;
96
wrapAllByQueryWithSuggestion: <T extends (...args: any[]) => HTMLElement[]>(
97
query: T,
98
queryAllByName: string,
99
container: HTMLElement
100
) => T;
101
};
102
103
/**
104
* Pre-built queries object containing all standard query functions
105
*/
106
declare const queries: {
107
queryByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => HTMLElement | null;
108
queryAllByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => HTMLElement[];
109
getByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => HTMLElement;
110
getAllByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => HTMLElement[];
111
findByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => Promise<HTMLElement>;
112
findAllByRole: (container: HTMLElement, role: string, options?: ByRoleOptions) => Promise<HTMLElement[]>;
113
// ... similar patterns for Text, TestId, LabelText, PlaceholderText, AltText, Title, DisplayValue
114
};
115
```
116
117
### Text Utilities
118
119
Utilities for working with element text content and normalization.
120
121
```typescript { .api }
122
/**
123
* Get the text content of a node, including descendant text
124
* @param node - Node to get text from
125
* @returns Text content of the node
126
*/
127
function getNodeText(node: Node): string;
128
129
/**
130
* Get the default text normalizer function
131
* @param options - Normalization options
132
* @returns Normalizer function
133
*/
134
function getDefaultNormalizer(options?: NormalizerOptions): (text: string) => string;
135
136
/**
137
* Options for text normalization
138
*/
139
interface NormalizerOptions {
140
/** Remove leading and trailing whitespace (default: true) */
141
trim?: boolean;
142
/** Collapse consecutive whitespace to single spaces (default: true) */
143
collapseWhitespace?: boolean;
144
}
145
```
146
147
### Accessibility Utilities
148
149
Utilities for working with accessibility features and ARIA roles.
150
151
```typescript { .api }
152
/**
153
* Get all available ARIA roles for an element
154
* @param element - Element to get roles for
155
* @returns Array of available roles
156
*/
157
function getRoles(element: HTMLElement): string[];
158
159
/**
160
* Check if an element is inaccessible (hidden from screen readers)
161
* @param element - Element to check
162
* @returns True if element is inaccessible
163
*/
164
function isInaccessible(element: HTMLElement): boolean;
165
166
/**
167
* Get suggested query method for finding an element
168
* @param element - Element to get suggestion for
169
* @param variant - Query variant to suggest (get, query, find)
170
* @param method - Specific method preference
171
* @returns Suggested query method as string
172
*/
173
function getSuggestedQuery(
174
element: HTMLElement,
175
variant?: 'get' | 'query' | 'find',
176
method?: string
177
): string;
178
```
179
180
### Debugging Utilities
181
182
Utilities for debugging and inspecting DOM elements during testing.
183
184
```typescript { .api }
185
/**
186
* Pretty-print DOM elements for debugging
187
* @param element - Element to pretty-print
188
* @param maxLength - Maximum length of output (default: 7000)
189
* @param options - Pretty printing options
190
* @returns Formatted string representation of element
191
*/
192
function prettyDOM(
193
element?: Element | HTMLDocument | null,
194
maxLength?: number,
195
options?: PrettyDOMOptions
196
): string;
197
198
/**
199
* Log DOM tree to console
200
* @param element - Element to log (defaults to document.body)
201
* @param maxLength - Maximum length of output
202
* @param options - Pretty printing options
203
*/
204
function logDOM(
205
element?: Element | HTMLDocument | null,
206
maxLength?: number,
207
options?: PrettyDOMOptions
208
): void;
209
210
/**
211
* Log available ARIA roles to console
212
* @param element - Container element (defaults to document.body)
213
*/
214
function logRoles(element?: Element | HTMLDocument): void;
215
216
/**
217
* Options for pretty DOM printing
218
*/
219
interface PrettyDOMOptions {
220
/** Highlight matching elements */
221
highlight?: boolean;
222
/** Filter out certain elements */
223
filterNode?: (node: Node) => boolean;
224
}
225
226
/**
227
* Pretty format values for display (from pretty-format library)
228
* @param value - Value to format
229
* @param options - Formatting options
230
* @returns Formatted string representation
231
*/
232
function prettyFormat(value: any, options?: PrettyFormatOptions): string;
233
234
/**
235
* Options for pretty formatting
236
*/
237
interface PrettyFormatOptions {
238
/** Maximum depth to traverse (default: Infinity) */
239
maxDepth?: number;
240
/** Minimum number of elements to trigger multi-line (default: Infinity) */
241
min?: boolean;
242
/** Include function names (default: false) */
243
printFunctionName?: boolean;
244
}
245
```
246
247
### Error Handling
248
249
Utilities for error handling and reporting.
250
251
```typescript { .api }
252
/**
253
* Get detailed error message for element queries
254
* @param message - Base error message
255
* @param container - Container element that was searched
256
* @returns Enhanced error object with debugging information
257
*/
258
function getElementError(message: string, container: HTMLElement): Error;
259
```
260
261
## Usage Examples
262
263
### Basic Configuration
264
265
```typescript
266
import { configure, getConfig } from "@storybook/testing-library";
267
268
export const ConfigurationExample = {
269
play: async () => {
270
// Configure Testing Library
271
configure({
272
testIdAttribute: 'data-test-id', // Use custom test ID attribute
273
asyncUtilTimeout: 2000, // Increase default timeout to 2 seconds
274
defaultHidden: true, // Include hidden elements by default
275
showOriginalStackTrace: true // Show full stack traces in errors
276
});
277
278
// Get current configuration
279
const config = getConfig();
280
console.log('Current timeout:', config.asyncUtilTimeout);
281
console.log('Test ID attribute:', config.testIdAttribute);
282
}
283
};
284
```
285
286
### Custom Text Normalizer
287
288
```typescript
289
import { configure, getDefaultNormalizer } from "@storybook/testing-library";
290
291
export const CustomNormalizerExample = {
292
play: async () => {
293
// Create custom normalizer that removes extra characters
294
const customNormalizer = getDefaultNormalizer({
295
trim: true,
296
collapseWhitespace: true
297
});
298
299
// Apply custom normalizer globally
300
configure({
301
defaultNormalizer: (text: string) => {
302
// First apply default normalization
303
const normalized = customNormalizer(text);
304
// Then apply custom logic
305
return normalized.replace(/[^\w\s]/gi, ''); // Remove special characters
306
}
307
});
308
}
309
};
310
```
311
312
### Building Custom Queries
313
314
```typescript
315
import { buildQueries, within } from "@storybook/testing-library";
316
317
// Build custom query for elements with specific CSS class
318
const queryAllByClass = (container: HTMLElement, className: string) => {
319
return Array.from(container.querySelectorAll(`.${className}`));
320
};
321
322
const getMultipleError = (container: HTMLElement, className: string) =>
323
`Found multiple elements with class: ${className}`;
324
325
const getMissingError = (container: HTMLElement, className: string) =>
326
`Unable to find element with class: ${className}`;
327
328
const [
329
queryByClass,
330
getAllByClass,
331
getByClass,
332
findAllByClass,
333
findByClass,
334
queryAllByClass
335
] = buildQueries(queryAllByClass, getMultipleError, getMissingError);
336
337
export const CustomQueriesExample = {
338
play: async ({ canvasElement }) => {
339
const canvas = within(canvasElement);
340
341
// Use custom queries (need to bind to container manually)
342
const errorElement = getByClass(canvasElement, 'error-message');
343
const warningElements = getAllByClass(canvasElement, 'warning');
344
const optionalElement = queryByClass(canvasElement, 'optional');
345
346
expect(errorElement).toBeInTheDocument();
347
expect(warningElements).toHaveLength(2);
348
expect(optionalElement).toBeNull();
349
}
350
};
351
```
352
353
### Debugging with Pretty DOM
354
355
```typescript
356
import { within, prettyDOM, logDOM, logRoles } from "@storybook/testing-library";
357
358
export const DebuggingExample = {
359
play: async ({ canvasElement }) => {
360
const canvas = within(canvasElement);
361
362
// Pretty print specific element
363
const form = canvas.getByRole('form');
364
console.log('Form HTML:', prettyDOM(form));
365
366
// Log entire DOM tree
367
logDOM(canvasElement);
368
369
// Log available roles
370
logRoles(canvasElement);
371
372
// Pretty print with options
373
const table = canvas.getByRole('table');
374
console.log('Table (limited):', prettyDOM(table, 1000, {
375
highlight: true,
376
filterNode: (node) => node.nodeType === Node.ELEMENT_NODE
377
}));
378
}
379
};
380
```
381
382
### Accessibility Utilities
383
384
```typescript
385
import { within, getRoles, isInaccessible, getSuggestedQuery } from "@storybook/testing-library";
386
387
export const AccessibilityExample = {
388
play: async ({ canvasElement }) => {
389
const canvas = within(canvasElement);
390
391
// Check available roles
392
const button = canvas.getByRole('button');
393
const availableRoles = getRoles(button);
394
console.log('Available roles for button:', availableRoles);
395
396
// Check if element is accessible
397
const hiddenElement = canvas.getByTestId('hidden-element');
398
const isHidden = isInaccessible(hiddenElement);
399
console.log('Element is inaccessible:', isHidden);
400
401
// Get suggested query
402
const input = canvas.getByLabelText(/username/i);
403
const suggestion = getSuggestedQuery(input, 'get');
404
console.log('Suggested query:', suggestion);
405
}
406
};
407
```
408
409
### Text Utilities
410
411
```typescript
412
import { within, getNodeText, getDefaultNormalizer } from "@storybook/testing-library";
413
414
export const TextUtilitiesExample = {
415
play: async ({ canvasElement }) => {
416
const canvas = within(canvasElement);
417
418
// Get text content including nested elements
419
const container = canvas.getByTestId('text-container');
420
const fullText = getNodeText(container);
421
console.log('Full text content:', fullText);
422
423
// Use custom normalizer
424
const normalizer = getDefaultNormalizer({
425
trim: true,
426
collapseWhitespace: true
427
});
428
429
const rawText = ' Hello World \n\n ';
430
const normalizedText = normalizer(rawText);
431
console.log('Normalized text:', normalizedText); // "Hello World"
432
}
433
};
434
```
435
436
### Error Handling
437
438
```typescript
439
import { within, getElementError } from "@storybook/testing-library";
440
441
export const ErrorHandlingExample = {
442
play: async ({ canvasElement }) => {
443
const canvas = within(canvasElement);
444
445
try {
446
// This will throw if element not found
447
canvas.getByText(/non-existent text/i);
448
} catch (error) {
449
// Enhance error with debugging info
450
const enhancedError = getElementError(
451
'Custom error message: Element not found',
452
canvasElement
453
);
454
console.error('Enhanced error:', enhancedError.message);
455
}
456
}
457
};
458
```
459
460
### Advanced Configuration
461
462
```typescript
463
import { configure } from "@storybook/testing-library";
464
465
export const AdvancedConfigExample = {
466
play: async () => {
467
configure({
468
// Use custom test ID attribute
469
testIdAttribute: 'data-cy',
470
471
// Increase timeout for slow operations
472
asyncUtilTimeout: 5000,
473
474
// Custom text normalizer
475
defaultNormalizer: (text: string) => {
476
return text
477
.toLowerCase()
478
.replace(/\s+/g, ' ')
479
.trim()
480
.replace(/[^\w\s]/g, '');
481
},
482
483
// Include hidden elements in queries
484
defaultHidden: false,
485
486
// Show full error stack traces
487
showOriginalStackTrace: true,
488
489
// Custom ignore pattern
490
defaultIgnore: 'script, style, [data-ignore]',
491
492
// Disable query suggestions
493
throwSuggestions: false
494
});
495
}
496
};
497
```
498
499
### Performance Optimization
500
501
```typescript
502
import { configure, within } from "@storybook/testing-library";
503
504
export const PerformanceExample = {
505
play: async ({ canvasElement }) => {
506
// Configure for better performance
507
configure({
508
asyncUtilTimeout: 500, // Shorter timeout for fast tests
509
computedStyleSupportsPseudoElements: false, // Disable if not needed
510
throwSuggestions: false // Reduce error message computation
511
});
512
513
const canvas = within(canvasElement);
514
515
// Use more specific queries for better performance
516
const button = canvas.getByRole('button', { name: /exact text/i });
517
const input = canvas.getByLabelText(/specific label/i);
518
519
expect(button).toBeInTheDocument();
520
expect(input).toBeInTheDocument();
521
}
522
};
523
```