Simple and complete DOM testing utilities that encourage good testing practices.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
{
"name": "@testing-library/dom",
"version": "10.4.1",
"install": "npm install --save-dev @testing-library/dom"
}import {screen, within, waitFor, fireEvent} from '@testing-library/dom';
// All query methods
import {
getByRole, queryByRole, findByRole,
getByLabelText, queryByLabelText, findByLabelText,
getByPlaceholderText, queryByPlaceholderText, findByPlaceholderText,
getByText, queryByText, findByText,
getByAltText, queryByAltText, findByAltText,
getByTitle, queryByTitle, findByTitle,
getByDisplayValue, queryByDisplayValue, findByDisplayValue,
getByTestId, queryByTestId, findByTestId
} from '@testing-library/dom';
// Plural variants
import {
getAllBy*, queryAllBy*, findAllBy*
} from '@testing-library/dom';
// Namespace imports for grouped access
import {queries, queryHelpers} from '@testing-library/dom';
// Access queries through namespace
const element = queries.getByRole(container, 'button', {name: /submit/i});
// Access query helpers through namespace
const customQuery = queryHelpers.buildQueries(...);Every query has three variants with different error handling:
getBy* - Returns element, throws if 0 or 2+ matches foundqueryBy* - Returns element or null if not found, throws if 2+ foundfindBy* - Returns Promise, waits up to 1000ms (async)Plural variants return arrays and have different throwing behavior.
Use queries in this order (most accessible to least):
getByRole - Accessible to everyone (ARIA roles)getByLabelText - Form fieldsgetByPlaceholderText - Form alternativegetByText - Non-interactive contentgetByDisplayValue - Current form valuesgetByAltText - ImagesgetByTitle - Title attributegetByTestId - Last resort// Use screen - queries entire document
const button = screen.getByRole('button', {name: /submit/i});
// Use within for scoped queries
const form = screen.getByRole('form');
const emailInput = within(form).getByLabelText('Email');import {screen, fireEvent, waitFor} from '@testing-library/dom';
// Basic query
const button = screen.getByRole('button', {name: /submit/i});
fireEvent.click(button);
// Form interaction
const email = screen.getByLabelText('Email');
fireEvent.change(email, {target: {value: 'user@example.com'}});
// Async content
await waitFor(() => {
expect(screen.getByText('Success')).toBeInTheDocument();
});
// Or use findBy directly
const message = await screen.findByText('Success');waitFor: Poll with MutationObserver supportwaitForElementToBeRemoved: Wait for element removalfireEvent: Fire DOM events on elementscreateEvent: Create events without firingprettyDOM, logDOM, screen.debug, logTestingPlaygroundURLconfigure, getConfiggetRoles, logRoles, isInaccessible, computeHeadingLevelbuildQueries, queryByAttribute, queryAllByAttributegetNodeText, getDefaultNormalizergetSuggestedQuery for better query recommendationsExtract text content as users perceive it.
function getNodeText(node: HTMLElement): string;Usage:
import {getNodeText} from '@testing-library/dom';
const element = screen.getByRole('button');
const text = getNodeText(element); // Returns visible text contentText normalization for customizing query behavior.
function getDefaultNormalizer(
options?: DefaultNormalizerOptions
): NormalizerFn;
interface DefaultNormalizerOptions {
trim?: boolean; // Default: true
collapseWhitespace?: boolean; // Default: true
}
type NormalizerFn = (text: string) => string;Usage:
import {getDefaultNormalizer, getByText} from '@testing-library/dom';
// Custom normalizer
const normalizer = getDefaultNormalizer({trim: false});
const element = getByText(container, 'Text', {normalizer});
// Case-insensitive matching
const caseInsensitive = (text) => text.toLowerCase();
const element = getByText(container, 'submit', {normalizer: caseInsensitive});Get suggested better queries for improving test quality.
function getSuggestedQuery(
element: HTMLElement,
variant?: Variant,
method?: Method
): Suggestion | undefined;
interface Suggestion {
queryName: string;
queryMethod: string;
queryArgs: QueryArgs;
variant: string;
warning?: string;
toString(): string;
}
type Variant = 'find' | 'findAll' | 'get' | 'getAll' | 'query' | 'queryAll';
type Method = 'Role' | 'LabelText' | 'PlaceholderText' | 'Text' |
'DisplayValue' | 'AltText' | 'Title' | 'TestId';Usage:
import {getSuggestedQuery} from '@testing-library/dom';
const button = document.querySelector('button');
const suggestion = getSuggestedQuery(button, 'get');
if (suggestion) {
console.log(suggestion.toString());
// "getByRole('button', {name: /submit/i})"
}
// Use with throwSuggestions config
import {configure} from '@testing-library/dom';
configure({throwSuggestions: true});
// Errors now include better query suggestionsAll APIs are fully typed. The library provides TypeScript definitions with generic types for element narrowing.
import {screen} from '@testing-library/dom';
// Automatically typed as HTMLElement
const button = screen.getByRole('button');
// Narrow to specific element type
const button = screen.getByRole('button') as HTMLButtonElement;
// Or use generic
const button = screen.getByRole<HTMLButtonElement>('button');import {screen, fireEvent} from '@testing-library/dom';
// Fill form
fireEvent.change(screen.getByLabelText('Name'), {target: {value: 'John'}});
fireEvent.change(screen.getByLabelText('Email'), {target: {value: 'john@example.com'}});
fireEvent.click(screen.getByRole('button', {name: /submit/i}));
// Verify result
expect(screen.getByText('Thank you')).toBeInTheDocument();import {screen, within, fireEvent} from '@testing-library/dom';
fireEvent.click(screen.getByRole('button', {name: /open/i}));
const modal = screen.getByRole('dialog');
expect(within(modal).getByText('Modal Title')).toBeInTheDocument();
fireEvent.click(within(modal).getByRole('button', {name: /close/i}));import {screen, fireEvent, waitFor} from '@testing-library/dom';
fireEvent.click(screen.getByRole('button', {name: /load/i}));
// Wait for loading to complete
await waitFor(() => {
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
});
// Verify loaded content
expect(screen.getByText('Data loaded')).toBeInTheDocument();All text-based queries accept a Matcher type:
type Matcher = string | RegExp | number | MatcherFunction;
type MatcherFunction = (content: string, element: Element | null) => boolean;
type ByRoleMatcher = ARIARole | string;
// Matcher options
interface MatcherOptions {
exact?: boolean; // Exact string match (default: false)
trim?: boolean; // Trim whitespace (default: true)
collapseWhitespace?: boolean; // Collapse whitespace (default: true)
normalizer?: NormalizerFn; // Custom text normalizer
suggest?: boolean; // Include query suggestions in errors
}
interface SelectorMatcherOptions extends MatcherOptions {
selector?: string; // CSS selector to narrow results
ignore?: boolean | string; // CSS selectors to ignore
}Examples:
// String (substring match)
getByText(container, 'Submit');
// Regex
getByText(container, /submit/i);
// Number
getByText(container, 42);
// Function
getByText(container, (content, el) => content.startsWith('Total:'));
// Exact match
getByText(container, 'Submit', {exact: true});
// With selector
getByText(container, 'Click me', {selector: 'button'});
// With normalizer
getByText(container, 'submit', {
normalizer: str => str.toLowerCase()
});Common type definitions used throughout the library:
// Query function types
type BoundFunction<T> = T extends (
container: HTMLElement,
...args: infer P
) => infer R
? (...args: P) => R
: never;
interface Queries {
[T: string]: Query;
}
type Query = (
container: HTMLElement,
...args: any[]
) => Error | HTMLElement | HTMLElement[] | Promise<HTMLElement[]> | Promise<HTMLElement> | null;
// Wait options for async utilities
interface waitForOptions {
container?: HTMLElement; // Element to observe (default: document)
timeout?: number; // Max wait in ms (default: 1000)
interval?: number; // Polling interval in ms (default: 50)
onTimeout?: (error: Error) => Error; // Custom timeout error
mutationObserverOptions?: MutationObserverInit;
}
// Query arguments
type QueryArgs = [string, QueryOptions?];
interface QueryOptions {
[key: string]: RegExp | boolean;
}