Introduction
The DOM's base queries The first argument in the Testing Library must be a container. When you render your components with most framework implementations of Testing Library, they provide a pre-bound version of these queries, so you don't have to provide a container.
A string, regular expression, or function can be used as the query's primary argument. There are also options for modifying the way node text is parsed.
In this article, we will have a look at the queries ByRole and ByTestId. Let us dive in!
ByRole
ByRole comprises of getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole, and findAllByRole.
API
getByRole(
// If you're using `screen`, then skip the container argument:
container: HTMLElement,
role: TextMatch,
options?: {
exact?: boolean = true,
hidden?: boolean = false,
name?: TextMatch,
description?: TextMatch,
normalizer?: NormalizerFn,
selected?: boolean,
checked?: boolean,
pressed?: boolean,
current?: boolean | string,
expanded?: boolean,
queryFallbacks?: boolean,
level?: number,
}): HTMLElement
Options
hidden
If you set hidden to true elements that are normally excluded from the accessibility tree are considered for the query as well. The default behavior with the exception of role="none" and role="presentation" are considered in the query in any case.
For example, getByRole('button') would only return the Close dialog-button in the code below. You'd need to use getAllByRole('button', {hidden: true}) to make assertions about the Open dialog-button.
<body>
<main aria-hidden="true">
<button>Open dialog</button>
</main>
<div role="dialog">
<button>Close dialog</button>
</div>
</body>
selected
Set selected: true or selected: false to filter the returned elements by their selected state.
For example, in the code below, getByRole('tab', {selected: true}) is used to get the "Native"-tab.
<body>
<div role="tablist">
<button role="tab" aria-selected="true">Native</button>
<button role="tab" aria-selected="false">React</button>
<button role="tab" aria-selected="false">Cypress</button>
</div>
</body>
checked
Set checked: true or checked: false to filter the returned elements by their checked state.
For example, you can get the "Sugar" option by calling getByRole('checkbox', {checked: true }), as shown below.
<body>
<section>
<button role="checkbox" aria-checked="true">Sugar</button>
<button role="checkbox" aria-checked="false">Gummy bears</button>
<button role="checkbox" aria-checked="false">Whipped cream</button>
</section>
</body>
current
Set current: boolean | string to filter the returned elements by their current state. Because false is the default value for aria-current, no aria-current attribute will match current: false.
You can get the "👍" link in the code snippet below by calling getByRole('link', {current: true} ) and the "👎" link by calling getByRole('link', {current: false} ).
<body>
<nav>
<a href="current/page" aria-current="true">👍</a>
<a href="another/page">👎</a>
</nav>
</body>
pressed
A pressed state is possible for buttons. Set pressed: true or false to filter the returned elements based on their pressed state.
For example, you can get the "👍" button in the code below by calling getByRole('button', {pressed: true }).
<body>
<section>
<button aria-pressed="true">👍</button>
<button aria-pressed="false">👎</button>
</section>
</body>
expanded
Set expanded: true or expanded: false to filter the returned elements by their expanded state.
For example in the code below, by calling getByRole('link', {expanded: false}), you can get the "Expandable Menu Item" link.
<body>
<nav>
<ul>
<li>
<a aria-expanded="false" aria-haspopup="true" href="..."
>Expandable Menu Item</a
>
<ul>
<li><a href="#">Submenu Item 1</a></li>
<li><a href="#">Submenu Item 1</a></li>
</ul>
</li>
<li><a href="#">Regular Menu Item</a></li>
</ul>
</nav>
</body>
<div role="dialog">...</div>
Native:
import {screen} from '@testing-library/dom'
const dialogContainer = screen.getByRole('dialog')
React:
import {render, screen} from '@testing-library/react'
render(<MyComponent />)
const dialogContainer = screen.getByRole('dialog')
Cypress:
cy.findByRole('dialog').should('exist')
queryFallbacks
Because the first role of each element is assumed to be supported by default, only the first role can be queried. You can use "queryFallbacks: true" to query an element by any of its fallback roles instead.
Because it's the first role, getByRole('switch') will always match div role="switch checkbox" />, whereas getByRole('checkbox') will not. getByRole('checkbox', {queryFallbacks: true}) would, on the other hand, enable all fallback roles and thus match the same element.
level
Any heading level getByRole('heading') or a specific heading level using the level option getByRole('heading', {level: 2}) can query an element with the heading role.
The level option looks for elements with heading roles that match the indicated level, which is determined by the semantic HTML heading elements <h1>-<h6> or the aria-level attribute.
Given the example below, you can query the Heading Level Three heading using getByRole('heading', { level: 3 }).
<body>
<section>
<h1>Heading Level One</h1>
<h2>First Heading Level Two</h2>
<h3>Heading Level Three</h3>
<div role="heading" aria-level="2">Second Heading Level Two</div>
</section>
</body>
getByRole('heading', {level: 1})
// <h1>Heading Level One</h1>
getAllByRole('heading', {level: 2})
// [
// <h2>First Heading Level Two</h2>,
// <div role="heading" aria-level="2">Second Heading Level Two</div>
// ]
While an element's role="heading" and aria-level attributes can be explicitly set, the semantic HTML headings <h1>-<h6> are strongly recommended.
description
If you have several elements with the same role that don't have an accessible name but do have a description, you can filter the returned elements by their accessible description.
This is the case for elements with the alertdialog role, which use the aria-describedby attribute to describe their content.
For instance, in
<body>
<ul>
<li role="alertdialog" aria-describedby="notification-id-1">
<div><button>Close</button></div>
<div id="notification-id-1">You have unread emails</div>
</li>
<li role="alertdialog" aria-describedby="notification-id-2">
<div><button>Close</button></div>
<div id="notification-id-2">Your session is about to expire</div>
</li>
</ul>
</body>
You can query a specific element like this
getByRole('alertdialog', {description: 'Your session is about to expire'})