Simple and complete React DOM testing utilities that encourage good testing practices
—
Find elements using accessible patterns. Inherited from @testing-library/dom.
Three variants for each query method:
Plural forms (getAll*, queryAll*, findAll*) return arrays.
Primary query method. Finds elements by their ARIA role. Encourages accessible markup and tests behavior users can actually see.
/**
* Find element by ARIA role
* @param role - ARIA role (button, heading, textbox, etc.)
* @param options - Additional constraints
* @returns Matching HTMLElement
* @throws When no element or multiple elements found
*/
getByRole(role: string, options?: ByRoleOptions): HTMLElement;
/**
* Find all elements by ARIA role
* @returns Array of matching HTMLElements
* @throws When no elements found
*/
getAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];
interface ByRoleOptions {
/**
* Filter by accessible name (label, aria-label, text content)
*/
name?: string | RegExp;
/**
* Filter by accessible description (aria-describedby, title)
*/
description?: string | RegExp;
/**
* Include hidden elements (default: false)
*/
hidden?: boolean;
/**
* Filter by selected state (for options, tabs, etc.)
*/
selected?: boolean;
/**
* Filter by checked state (for checkboxes, radio buttons)
*/
checked?: boolean;
/**
* Filter by pressed state (for toggle buttons)
*/
pressed?: boolean;
/**
* Filter by current state (for navigation items)
*/
current?: boolean | string;
/**
* Filter by expanded state (for accordions, dropdowns)
*/
expanded?: boolean;
/**
* Filter by heading level (for heading role)
*/
level?: number;
/**
* Enable exact matching (default: true)
*/
exact?: boolean;
/**
* Custom text normalizer function
*/
normalizer?: (text: string) => string;
/**
* Enable query fallbacks
*/
queryFallbacks?: boolean;
}Common Roles:
screen.getByRole('button'); // <button>, role="button"
screen.getByRole('link'); // <a href>
screen.getByRole('heading', { level: 1 }); // <h1>
screen.getByRole('textbox'); // <input type="text">, <textarea>
screen.getByRole('checkbox'); // <input type="checkbox">
screen.getByRole('radio'); // <input type="radio">
screen.getByRole('combobox'); // <select>
screen.getByRole('img'); // <img>
screen.getByRole('dialog'); // Modal/dialog
screen.getByRole('alert'); // Alert message
screen.getByRole('navigation'); // <nav>
screen.getByRole('list'); // <ul>, <ol>
screen.getByRole('listitem'); // <li>Examples:
// By role and name
screen.getByRole('button', { name: /submit/i });
// By state
screen.getByRole('checkbox', { checked: true });
screen.getByRole('button', { pressed: true });
// Heading level
screen.getByRole('heading', { level: 2 });Finds form elements by their associated label text. Best for form inputs.
/**
* Find form element by label text
* @param text - Label text (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
interface SelectorMatcherOptions {
/**
* Custom CSS selector to filter results
*/
selector?: string;
/**
* Enable exact matching (default: true)
*/
exact?: boolean;
/**
* Custom text normalizer function
*/
normalizer?: (text: string) => string;
}Examples:
// <label htmlFor="email">Email</label><input id="email" />
screen.getByLabelText('Email');
// <input aria-label="Search" />
screen.getByLabelText('Search');
// <label><input /> Remember me</label>
screen.getByLabelText('Remember me');Finds elements by their text content. Good for non-interactive text elements.
/**
* Find element by text content
* @param text - Text content (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];Examples:
screen.getByText('Hello World');
screen.getByText(/hello/i); // Case insensitive
screen.getByText('Hello', { exact: false }); // Partial match
screen.getByText('Submit', { selector: 'button' }); // Filter by tagFinds elements by placeholder text. Use as a last resort as placeholders are not accessible replacements for labels.
/**
* Find element by placeholder text
* @param text - Placeholder text (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement;
getAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];
interface MatcherOptions {
/**
* Enable exact matching (default: true)
*/
exact?: boolean;
/**
* Custom text normalizer function
*/
normalizer?: (text: string) => string;
}Finds form elements by their current value. Useful for testing filled forms.
/**
* Find form element by current value
* @param value - Current display value (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement;
getAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement[];Finds images and other elements by their alt text.
/**
* Find element by alt text
* @param text - Alt text (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement;
getAllByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];Finds elements by their title attribute.
/**
* Find element by title attribute
* @param title - Title text (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement;
getAllByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement[];Finds elements by data-testid attribute. Use as a last resort when other queries are not applicable.
/**
* Find element by test ID
* @param testId - Test ID value (string or regex)
* @param options - Matching options
* @returns Matching HTMLElement
*/
getByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement;
getAllByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement[];Non-throwing variants that return null when element is not found. Use for asserting elements don't exist.
queryByRole(role: string, options?: ByRoleOptions): HTMLElement | null;
queryAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];
queryByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement | null;
queryAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];
queryByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement | null;
queryAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement[];
queryByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement | null;
queryAllByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];
queryByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement | null;
queryAllByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement[];
queryByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement | null;
queryAllByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement[];Async queries that return a promise resolving when element is found. Use for elements that appear asynchronously.
findByRole(role: string, options?: ByRoleOptions): Promise<HTMLElement>;
findAllByRole(role: string, options?: ByRoleOptions): Promise<HTMLElement[]>;
findByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement>;
findAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement[]>;
findByPlaceholderText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;
findAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;
findByText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement>;
findAllByText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement[]>;
findByDisplayValue(value: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;
findAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;
findByAltText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;
findAllByAltText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;
findByTitle(title: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;
findAllByTitle(title: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;
findByTestId(testId: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;
findAllByTestId(testId: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;Pre-bound queries to document.body for convenient access.
import { screen } from '@testing-library/react';
const screen: {
// All query functions
getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;
// ... all other queries
// Debug utility
debug: (element?: HTMLElement, maxLength?: number) => void;
// Log roles utility
logTestingPlaygroundURL: (element?: HTMLElement) => void;
};Get queries bound to a specific element for scoped searches.
/**
* Get queries scoped to a specific element
* @param element - Element to scope queries to
* @returns Object with all query functions bound to element
*/
function within(element: HTMLElement): {
getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;
// ... all other query functions
};// Element exists
const button = screen.getByRole('button');
// Element may not exist
const dialog = screen.queryByRole('dialog');
if (dialog) {
// Handle dialog
}
// Negative assertion
expect(screen.queryByText('Error')).not.toBeInTheDocument();
// Multiple elements
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(5);// Wait for element to appear
const message = await screen.findByText('Loaded');
// Wait for multiple
const buttons = await screen.findAllByRole('button');
// With custom timeout (default: 1000ms)
const slow = await screen.findByText('Slow', {}, { timeout: 3000 });// Query within specific element
const modal = screen.getByRole('dialog');
const closeButton = within(modal).getByRole('button', { name: /close/i });
// Multiple scopes
const nav = screen.getByRole('navigation');
const homeLink = within(nav).getByRole('link', { name: /home/i });// 1. screen object (recommended)
import { screen } from '@testing-library/react';
screen.getByRole('button');
// 2. render result
const { getByRole } = render(<Component />);
getByRole('button');
// 3. within for scoped queries
import { within } from '@testing-library/react';
within(container).getByRole('button');test('form submission', () => {
render(<LoginForm />);
const emailInput = screen.getByLabelText(/email/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /log in/i });
fireEvent.change(emailInput, { target: { value: 'user@example.com' } });
fireEvent.change(passwordInput, { target: { value: 'password123' } });
fireEvent.click(submitButton);
});test('displays items', () => {
render(<TodoList items={['Task 1', 'Task 2']} />);
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(items[0]).toHaveTextContent('Task 1');
});test('modal interactions', async () => {
render(<App />);
// Open modal
fireEvent.click(screen.getByRole('button', { name: /open/i }));
// Wait for modal
const modal = await screen.findByRole('dialog');
expect(modal).toBeInTheDocument();
// Query within modal
const title = within(modal).getByRole('heading');
expect(title).toHaveTextContent('Modal Title');
});test('shows error on failure', async () => {
render(<Component />);
// Initially no error
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
// Trigger error
fireEvent.click(screen.getByRole('button'));
// Error appears
const alert = await screen.findByRole('alert');
expect(alert).toHaveTextContent('Error occurred');
});// Print DOM
screen.debug();
screen.debug(screen.getByRole('button'));
// Print available roles
import { logRoles } from '@testing-library/react';
logRoles(container);
// Get suggested queries
// Error messages suggest better queries when getBy* fails/submit/i instead of "Submit Form"Install with Tessl CLI
npx tessl i tessl/npm-testing-library--react