Skip to main content

ByRole

getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole, findAllByRole

API

getByRole(
// If you're using `screen`, then skip the container argument:
container: HTMLElement,
role: string,
options?: {
hidden?: boolean = false,
name?: TextMatch,
description?: TextMatch,
selected?: boolean,
busy?: boolean,
checked?: boolean,
pressed?: boolean,
suggest?: boolean,
current?: boolean | string,
expanded?: boolean,
queryFallbacks?: boolean,
level?: number,
value?: {
min?: number,
max?: number,
now?: number,
text?: TextMatch,
}
}): HTMLElement

Queries for elements with the given role (and it also accepts a TextMatch). Default roles are taken into consideration e.g. <button /> has the button role without explicitly setting the role attribute. Here you can see a table of HTML elements with their default and desired roles.

Please note that setting a role and/or aria-* attribute that matches the implicit ARIA semantics is unnecessary and is not recommended as these properties are already set by the browser, and we must not use the role and aria-* attributes in a manner that conflicts with the semantics described. For example, a button element can't have the role attribute of heading, because the button element has default characteristics that conflict with the heading role.

Roles are matched literally by string equality, without inheriting from the ARIA role hierarchy. As a result, querying a superclass role like checkbox will not include elements with a subclass role like switch.

You can query the returned element(s) by their accessible name or description. The accessible name is for simple cases equal to e.g. the label of a form element, or the text content of a button, or the value of the aria-label attribute. It can be used to query a specific element if multiple elements with the same role are present on the rendered content. For an in-depth guide check out "What is an accessible name?" from TPGi. If you only query for a single element with getByText('The name') it's oftentimes better to use getByRole(expectedRole, { name: 'The name' }). The accessible name query does not replace other queries such as *ByAlt or *ByTitle. While the accessible name can be equal to these attributes, it does not replace the functionality of these attributes. For example <img aria-label="fancy image" src="fancy.jpg" /> will be returned for getByRole('img', { name: 'fancy image' }). However, the image will not display its description if fancy.jpg could not be loaded. Whether you want to assert this functionality in your test or not is up to you.

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 follows https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion with the exception of role="none" and role="presentation" which are considered in the query in any case. For example in

<body>
<main aria-hidden="true">
<button>Open dialog</button>
</main>
<div role="dialog">
<button>Close dialog</button>
</div>
</body>

getByRole('button') would only return the Close dialog-button. To make assertions about the Open dialog-button you would need to use getAllByRole('button', { hidden: true }).

The default value for hidden can be configured.

selected

You can filter the returned elements by their selected state by setting selected: true or selected: false.

For example in

<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>

you can get the "Native"-tab by calling getByRole('tab', { selected: true }). To learn more about the selected state and which elements can have this state see ARIA aria-selected.

busy

You can filter the returned elements by their busy state by setting busy: true or busy: false.

For example in

<body>
<section>
<div role="alert" aria-busy="false">Login failed</div>
<div role="alert" aria-busy="true">Error: Loading message...</div>
</section>
</body>

you can get the "Login failed" alert by calling getByRole('alert', { busy: false }). To learn more about the busy state see ARIA aria-busy and MDN aria-busy attribute.

checked

You can filter the returned elements by their checked state by setting checked: true or checked: false.

For example in

<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>

you can get the "Sugar" option by calling getByRole('checkbox', { checked: true }). To learn more about the checked state and which elements can have this state see ARIA aria-checked.

Note

Checkboxes have a "mixed" state, which is considered neither checked nor unchecked (details here).

current

You can filter the returned elements by their current state by setting current: boolean | string. Note that no aria-current attribute will match current: false since false is the default value for aria-current.

For example in

<body>
<nav>
<a href="current/page" aria-current="page">👍</a>
<a href="another/page">👎</a>
</nav>
</body>

you can get the "👍" link by calling getByRole('link', { current: 'page' }) and the "👎" by calling getByRole('link', { current: false }). To learn more about the current state see ARIA aria-current.

pressed

Buttons can have a pressed state. You can filter the returned elements by their pressed state by setting pressed: true or pressed: false.

For example in

<body>
<section>
<button aria-pressed="true">👍</button>
<button aria-pressed="false">👎</button>
</section>
</body>

you can get the "👍" button by calling getByRole('button', { pressed: true }). To learn more about the pressed state see ARIA aria-pressed.

suggest

You can disable the ability to throw suggestions for a specific query by setting this value to false.
Setting this value to true will throw suggestions for the specific query.

expanded

You can filter the returned elements by their expanded state by setting expanded: true or expanded: false.

For example in

<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>

you can get the "Expandable Menu Item" link by calling getByRole('link', { expanded: false }). To learn more about the expanded state and which elements can have this state see ARIA aria-expanded.

<div role="dialog">...</div>
import {screen} from '@testing-library/dom'

const dialogContainer = screen.getByRole('dialog')

queryFallbacks

By default, it's assumed that the first role of each element is supported, so only the first role can be queried. If you need to query an element by any of its fallback roles instead, you can use queryFallbacks: true.

For example, getByRole('switch') would always match <div role="switch checkbox" /> because it's the first role, while getByRole('checkbox') would not. However, getByRole('checkbox', { queryFallbacks: true }) would enable all fallback roles and therefore match the same element.

An element doesn't have multiple roles in a given environment. It has a single one. Multiple roles in the attribute are evaluated from left to right until the environment finds the first role it understands. This is useful when new roles get introduced and you want to start supporting those as well as older environments that don't understand that role (yet).

level

An element with the heading role can be queried by any heading level getByRole('heading') or by a specific heading level using the level option getByRole('heading', { level: 2 }).

The level option queries the element(s) with the heading role matching the indicated level determined by the semantic HTML heading elements <h1>-<h6> or matching the aria-level attribute.

Given the example below,

<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>

you can query the Heading Level Three heading using getByRole('heading', { level: 3 }).

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 it is possible to explicitly set role="heading" and aria-level attribute on an element, it is strongly encouraged to use the semantic HTML headings <h1>-<h6>.

To learn more about the aria-level property, see ARIA aria-level.

The level option is only applicable to the heading role. An error will be thrown when used with any other role.

value

A range widget can be queried by any value getByRole('spinbutton') or by a specific value using the level option getByRole('spinbutton', { value: { now: 5, min: 0, max: 10, text: 'medium' } }).

Note that you don't have to specify all properties in value. A subset is sufficient e.g. getByRole('spinbutton', { value: { now: 5, text: 'medium' } }).

Given the example below,

<body>
<section>
<button
role="spinbutton"
aria-valuenow="5"
aria-valuemin="0"
aria-valuemax="10"
aria-valuetext="medium"
>
Volume
</button>
<button
role="spinbutton"
aria-valuenow="3"
aria-valuemin="0"
aria-valuemax="10"
aria-valuetext="medium"
>
Pitch
</button>
</section>
</body>

you can query specific spinbutton(s) with the following queries,

getByRole('spinbutton', {value: {now: 5}})
// <button>Volume</button>

getAllByRole('spinbutton', {value: {min: 0}})
// [
// <button>Volume</button>,
// <button>Pitch</button>
// ]

Every specified property in value must match. For example, if you query for {value: {min: 0, now: 3}} aria-valuemin must be equal to 0 AND > aria-valuenow must be equal to 3

The value option is only applicable to certain roles (check the linked MDN pages below for applicable roles). An error will be thrown when used with any other role.

To learn more about the aria-value* properties, see MDN aria-valuemin, MDN aria-valuemax, MDN aria-valuenow, MDN aria-valuetext.

description

You can filter the returned elements by their accessible description for those cases where you have several elements with the same role and they don't have an accessible name but they do have a description.
This would be the case for elements with alertdialog role, where the aria-describedby attribute is used to describe the element's content.

For example 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'})

Performance

getByRole is the most preferred query to use as it most closely resembles the user experience, however the calculations it must perform to provide this confidence can be expensive (particularly with large DOM trees).

Where test performance is a concern it may be desirable to trade some of this confidence for improved performance.

getByRole performance can be improved by setting the option hidden to true and thereby avoid expensive visibility checks. Note that in doing so inaccessible elements will now be included in the result.

Another option may be to substitute getByRole for simpler getByLabelText and getByText queries which can be significantly faster though less robust alternatives.