id | title |
---|---|
api-queries |
Queries |
getBy
queries are shown by default in the query documentation below.
getBy*
queries returns the first matching node for a query, and throws an
error if no elements match or if more than one match is found (use getAllBy
instead).
getAllBy*
queries return an array of all matching nodes for a query, and
throws an error if no elements match.
queryBy*
queries returns the first matching node for a query, and return
null
if no elements match. This is useful for asserting an element is not
present. This throws if more than one match is found (use queryAllBy
instead).
queryAllBy*
queries return an array of all matching nodes for a query, and
return an empty array ([]
) if no elements match.
findBy*
queries return a promise which resolves when an element is found which
matches the given query. The promise is rejected if no element is found or if
more than one element is found after a default timeout of 4500
ms. If you need
to find more than one element, then use findAllBy
.
Note, this is a simple combination of
getBy*
queries andwaitForElement
. ThefindBy*
queries accept thewaitForElement
options as the last argument. (i.e.findByText(container, 'text', queryOptions, waitForElementOptions)
)
findAllBy*
queries return a promise which resolves to an array of elements
when any elements are found which match the given query. The promise is rejected
if no elements are found after a default timeout of 4500
ms.
The argument to a query can be a string, regular expression, or function. There are also options to adjust how node text is parsed.
See TextMatch for documentation on what can be passed to a query.
getByLabelText, queryByLabelText, getAllByLabelText, queryAllByLabelText findByLabelText, findAllByLabelText
getByLabelText(
container: HTMLElement,
text: TextMatch,
options?: {
selector?: string = '*',
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
This will search for the label that matches the given TextMatch
,
then find the element associated with that label.
The example below will find the input node for the following DOM structures:
// for/htmlFor relationship between label and form element id
<label for="username-input">Username</label>
<input id="username-input" />
// The aria-labelledby attribute with form elements
<label id="username-label">Username</label>
<input aria-labelledby="username-label" />
// The aria-labelledby attribute with non-form elements
<section aria-labelledby="section-one-header">
<h3 id="section-one-header">Section One</h3>
<p>some content</p>
</section>
// Wrapper labels
<label>Username <input /></label>
// aria-label attributes
// Take care because this is not a label that users can see on the page,
// so the purpose of your input must be obvious to visual users.
<input aria-label="username" />
import { getByLabelText } from '@testing-library/dom'
const container = document.body
const inputNode = getByLabelText(container, 'Username')
import { render } from 'react-testing-library'
const { getByLabelText } = render(<Login />)
const inputNode = getByLabelText('username')
cy.getByLabelText('username').should('exist')
It will NOT find the input node for label text broken up by elements. For this
case, you can provide a selector
in the options:
<label> <span>Username</span> <input /> </label>
const container = document.body
const inputNode = getByLabelText(container, 'username', {
selector: 'input',
})
getByPlaceholderText, queryByPlaceholderText, getAllByPlaceholderText, queryAllByPlaceholderText, findByPlaceholderText, findAllByPlaceholderText
getByPlaceholderText(
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
This will search for all elements with a placeholder attribute and find one that
matches the given TextMatch
.
<input placeholder="Username" />
import { getByPlaceholderText } from '@testing-library/dom'
const container = document.body
const inputNode = getByPlaceholderText(container, 'Username')
import { render } from 'react-testing-library'
const { getByPlaceholderText } = render(<MyComponent />)
const inputNode = getByPlaceholderText('Username')
cy.getByPlaceholderText('Username').should('exist')
Note
A placeholder is not a good substitute for a label so you should generally use
getByLabelText
instead.
getByText, queryByText, getAllByText, queryAllByText, findByText, findAllByText
getByText(
container: HTMLElement,
text: TextMatch,
options?: {
selector?: string = '*',
exact?: boolean = true,
ignore?: string|boolean = 'script, style',
normalizer?: NormalizerFn,
}): HTMLElement
This will search for all elements that have a text node with textContent
matching the given TextMatch
.
<a href="/about">About ℹ️</a>
import { getByText } from '@testing-library/dom'
const container = document.body
const aboutAnchorNode = getByText(container, /about/i)
import { render } from 'react-testing-library'
const { getByText } = render(<MyComponent />)
const aboutAnchorNode = getByText(/about/i)
cy.getByText(/about/i).should('exist')
It also works with input
s whose type
attribute is either submit
or
button
:
<input type="submit" value="Send data" />
Note
See
getByLabelText
for more details on how and when to use theselector
option
The ignore
option accepts a query selector. If the
node.matches
returns true for that selector, the node will be ignored. This defaults to
'script'
because generally you don't want to select script tags, but if your
content is in an inline script file, then the script tag could be returned.
If you'd rather disable this behavior, set ignore
to false
.
getByAltText, queryByAltText, getAllByAltText, queryAllByAltText, findByAltText, findAllByAltText
getByAltText(
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
This will return the element (normally an <img>
) that has the given alt
text. Note that it only supports elements which accept an alt
attribute:
<img>
,
<input>
,
and <area>
(intentionally excluding
<applet>
as it's deprecated).
<img alt="Incredibles 2 Poster" src="/incredibles-2.png" />
import { getByAltText } from '@testing-library/dom'
const container = document.body
const incrediblesPosterImg = getByAltText(container, /incredibles.*? poster/i)
import { render } from 'react-testing-library'
const { getByAltText } = render(<MyComponent />)
const incrediblesPosterImg = getByAltText(/incredibles.*? poster/i)
cy.getByAltText(/incredibles.*? poster/i).should('exist')
getByTitle, queryByTitle, getAllByTitle, queryAllByTitle, findByTitle, findAllByTitle
getByTitle(
container: HTMLElement,
title: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
Returns the element that has the matching title
attribute.
Will also find a title
element within an SVG.
<span title="Delete" id="2"></span>
<svg>
<title>Close</title>
<g><path /></g>
</svg>
import { getByTitle } from '@testing-library/dom'
const container = document.body
const deleteElement = getByTitle(container, 'Delete')
const closeElement = getByTitle(container, 'Close')
import { render } from 'react-testing-library'
const { getByTitle } = render(<MyComponent />)
const deleteElement = getByTitle('Delete')
const closeElement = getByTitle('Close')
cy.getByTitle('Delete').should('exist')
cy.getByTitle('Close').should('exist')
getByDisplayValue, queryByDisplayValue, getAllByDisplayValue, queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue
getByDisplayValue(
container: HTMLElement,
value: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
Returns the input
, textarea
, or select
element that has the matching
display value.
<input type="text" id="lastName" />
document.getElementById('lastName').value = 'Norris'
import { getByDisplayValue } from '@testing-library/dom'
const container = document.body
const lastNameInput = getByDisplayValue(container, 'Norris')
import { render } from 'react-testing-library'
const { getByDisplayValue } = render(<MyComponent />)
const lastNameInput = getByDisplayValue('Norris')
cy.getByDisplayValue('Norris').should('exist')
<textarea id="messageTextArea" />
document.getElementById('messageTextArea').value = 'Hello World'
import { getByDisplayValue } from '@testing-library/dom'
const container = document.body
const messageTextArea = getByDisplayValue(container, 'Hello World')
import { render } from 'react-testing-library'
const { getByDisplayValue } = render(<MyComponent />)
const messageTextArea = getByDisplayValue('Hello World')
cy.getByDisplayValue('Hello World').should('exist')
In case of select
, this will search for a <select>
whose selected <option>
matches the given TextMatch
.
<select id="state-select" data-testid="state">
<option value="">State</option>
<option value="AL">Alabama</option>
<option selected value="AK">Alaska</option>
<option value="AZ">Arizona</option>
</select>
import { getByDisplayValue } from '@testing-library/dom'
const container = document.body
const selectElement = getByDisplayValue(container, 'Alaska')
import { render } from 'react-testing-library'
const { getByDisplayValue } = render(<MyComponent />)
const selectElement = getByDisplayValue('Alaska')
cy.getByDisplayValue('Alaska').should('exist')
getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole, findAllByRole
getByRole(
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
A shortcut to container.querySelector(`[role="${yourRole}"]`)
(and it also
accepts a TextMatch
).
<div role="dialog">...</div>
import { getByRole } from '@testing-library/dom'
const container = document.body
const dialogContainer = getByRole(container, 'dialog')
import { render } from 'react-testing-library'
const { getByRole } = render(<MyComponent />)
const dialogContainer = getByRole('dialog')
cy.getByRole('dialog').should('exist')
getByTestId, queryByTestId, getAllByTestId, queryAllByTestId, findByTestId, findAllByTestId
getByTestId(
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
A shortcut to container.querySelector(`[data-testid="${yourId}"]`)
(and it
also accepts a TextMatch
).
<input data-testid="username-input" />
import { getByTestId } from '@testing-library/dom'
const container = document.body
const usernameInput = getByTestId(container, 'username-input')
import { render } from 'react-testing-library'
const { getByTestId } = render(<MyComponent />)
const usernameInput = getByTestId('username-input')
cy.getByTestId('username-input').should('exist')
In the spirit of the guiding principles, it is recommended to use this only after the other queries don't work for your use case. Using data-testid attributes do not resemble how your software is used and should be avoided if possible. That said, they are way better than querying based on DOM structure or styling css class names. Learn more about
data-testid
s from the blog post "Making your UI tests resilient to change"
The ...ByTestId
functions in DOM Testing Library
use the attribute
data-testid
by default, following the precedent set by
React Native Web
which uses a testID
prop to emit a data-testid
attribute on the element, and
we recommend you adopt that attribute where possible. But if you already have an
existing codebase that uses a different attribute for this purpose, you can
override this value via
configure({testIdAttribute: 'data-my-test-attribute'})
.
Several APIs accept a TextMatch
which can be a string
, regex
or a
function
which returns true
for a match and false
for a mismatch.
Some APIs accept an object as the final argument that can contain options that affect the precision of string matching:
exact
: Defaults totrue
; matches full strings, case-sensitive. When false, matches substrings and is not case-sensitive.exact
has no effect onregex
orfunction
arguments.- In most cases using a regex instead of a string gives you more control over
fuzzy matching and should be preferred over
{ exact: false }
.
normalizer
: An optional function which overrides normalization behavior. SeeNormalization
.
Before running any matching logic against text in the DOM, DOM Testing Library
automatically normalizes that text. By default, normalization consists of
trimming whitespace from the start and end of text, and collapsing multiple
adjacent whitespace characters into a single space.
If you want to prevent that normalization, or provide alternative normalization
(e.g. to remove Unicode control characters), you can provide a normalizer
function in the options object. This function will be given a string and is
expected to return a normalized version of that string.
Note: Specifying a value for normalizer
replaces the built-in normalization,
but you can call getDefaultNormalizer
to obtain a built-in normalizer, either
to adjust that normalization or to call it from your own normalizer.
getDefaultNormalizer
takes an options object which allows the selection of
behaviour:
trim
: Defaults totrue
. Trims leading and trailing whitespacecollapseWhitespace
: Defaults totrue
. Collapses inner whitespace (newlines, tabs, repeated spaces) into a single space.
To perform a match against text without trimming:
getByText(node, 'text', {
normalizer: getDefaultNormalizer({ trim: false }),
})
To override normalization to remove some Unicode characters whilst keeping some (but not all) of the built-in normalization behavior:
getByText(node, 'text', {
normalizer: str =>
getDefaultNormalizer({ trim: false })(str).replace(/[\u200E-\u200F]*/g, ''),
})
Given the following HTML:
<div>Hello World</div>
Will find the div:
// Matching a string:
getByText(container, 'Hello World') // full string match
getByText(container, 'llo Worl', { exact: false }) // substring match
getByText(container, 'hello world', { exact: false }) // ignore case
// Matching a regex:
getByText(container, /World/) // substring match
getByText(container, /world/i) // substring match, ignore case
getByText(container, /^hello world$/i) // full string match, ignore case
getByText(container, /Hello W?oRlD/i) // advanced regex
// Matching with a custom function:
getByText(container, (content, element) => content.startsWith('Hello'))
Will not find the div:
// full string does not match
getByText(container, 'Goodbye World')
// case-sensitive regex with different case
getByText(container, /hello world/)
// function looking for a span when it's actually a div:
getByText(container, (content, element) => {
return element.tagName.toLowerCase() === 'span' && content.startsWith('Hello')
})