Flexible Testing Library React
A thin wrapper around React Testing Library
which makes using custom queries easier.
See this PR for the
discussion behind this and for reasoning why this isn't in core @testing-library/dom
.
Install dependency
npm install --save-dev flexible-testing-library-react
Usage
This mostly follows the API of React Testing Library but with one important difference:
import { screen, render } from '@testing-library/react';
render(<MyComponent />);
screen.getByLabelText('foo').something();
import { screen, render, labelText } from 'flexible-testing-library-react';
render(<MyComponent />);
screen.getBy(labelText('foo')).something();
Or the alternative (scoped) syntax:
import { render } from '@testing-library/react';
const { getByLabelText } = render(<MyComponent />);
getByLabelText('foo').something();
import { render, labelText } from 'flexible-testing-library-react';
const { getBy } = render(<MyComponent />);
getBy(labelText('foo')).something();
Also parameters for findBy
now live in a more logical place:
import { screen, render } from '@testing-library/react';
render(<MyComponent />);
screen.findByTitle('foo', {}, { timeout: 1000 }).something();
import { screen, render, title } from 'flexible-testing-library-react';
render(<MyComponent />);
screen.findBy(title('foo'), { timeout: 1000 }).something();
If you are using Jest, a new matcher is also available:
import { screen, render, labelText } from 'flexible-testing-library-react';
import 'flexible-testing-library-react/extend-expect';
render(<MyComponent />);
expect(screen).toContainElementWith(labelText('foo'));
expect(screen).not.toContainElementWith(labelText('nope'));
(this matcher improves on toBeInTheDocument
, which has
problems with negation)
Reference
getBy
getBy(title('hello'));
Returns en element, or throws an exception if no elements were found (or multiple elements
matched).
getAllBy
getAllBy(title('hello'));
Returns a list of elements, or throws an exception if no elements were found.
queryBy
queryBy(title('hello'));
Returns an element, or null
if no elements were found, or throws an exception if multiple
elements matched.
queryAllBy
queryAllBy(title('hello'));
Returns a list of elements (which could be empty).
findBy
await findBy(title('hello'));
await findBy(title('hello'), { timeout: 1000 });
Waits until at least one matching element exists and returns it, or throws if the
timeout is reached before a matching element is found. Throws if multiple elements
match.
The second parameter is an optional options dictionary, which is passed directly to
DOM Testing Library's waitFor
.
findAllBy
await findAllBy(title('hello'));
await findAllBy(title('hello'), { timeout: 1000 });
Waits until at least one matching element exists and returns a list of all matches,
or throws if the timeout is reached before a matching element is found.
The second parameter is an optional options dictionary, which is passed directly to
DOM Testing Library's waitFor
.
Queries
For a list of supported queries, see the
DOM Testing Library documentation;
each query is available here.
Examples (note that the options can be omitted but are shown here to demonstrate their usage):
Function | Example | Upstream Docs |
---|
labelText | getAllBy(labelText('hello', { exact: false })) | ByLabelText |
placeholderText | getAllBy(placeholderText('hello', { exact: false })) | ByPlaceholderText |
text | getAllBy(text('hello', { exact: false })) | ByText |
altText | getAllBy(altText('hello', { exact: false })) | ByAltText |
title | getAllBy(title('hello', { exact: false })) | ByTitle |
displayValue | getAllBy(displayValue('hello', { exact: false })) | ByDisplayValue |
role | getAllBy(role('tab', { selected: true })) | ByRole |
testId | getAllBy(testId('hello', { exact: false })) | ByTestId |
As a convenience another query is available as a shorthand:
Function | Description | Example |
---|
textFragment | Same as text with exact: false in the options. | getAllBy(textFragment('hello')) |
For other features, see the main React Testing Library documentation.
Writing custom queries
const positionInTable = (column, row) => ({
description: `in column ${column}, row ${row}`,
queryAll: (container) => {
const rowElement = container.querySelectorAll('tr')[row];
if (!rowElement) {
return [];
}
const cellElement = rowElement.querySelectorAll('td')[column];
if (!cellElement) {
return [];
}
return [cellElement];
},
});
This can now be used easily with any of
getBy
, getAllBy
, findBy
, findAllBy
, queryBy
, queryAllBy
:
import { screen, render } from 'flexible-testing-library-react';
import { positionInTable } from './positionInTable';
render(<MyComponent />);
screen.getBy(positionInTable(2, 3)).something();
And can be used with toContainElementWith
:
import { screen, render } from 'flexible-testing-library-react';
import 'flexible-testing-library-react/extend-expect';
import { positionInTable } from './positionInTable';
render(<MyComponent />);
expect(screen).toContainElementWith(positionInTable(2, 3));
Optional query configuration
As well as a description
and queryAll
, you can provide some other (optional)
configuration:
multipleErrorDetail
: a string to include in error messages about finding too many
matching elements.missingErrorDetail
: a string to include in error messages about not finding any
element.getAll
: a version of queryAll
which throws if no elements are found (can be used
to provide more detailed error information). Note that you must specify queryAll
,
even if you also provide getAll
.
TypeScript
The types for custom queries are:
import type { Query } from 'flexible-testing-library-react';
const tableCell = (row: number, column: number): Query => ({
description: `in column ${column}, row ${row}`,
queryAll: (container): NodeListOf<HTMLElement> | HTMLElement[] => {
},
});