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 likeswitch
.
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>
- Native
- React
- Angular
- Cypress
import {screen} from '@testing-library/dom'
const dialogContainer = screen.getByRole('dialog')
import {render, screen} from '@testing-library/react'
render(<MyComponent />)
const dialogContainer = screen.getByRole('dialog')
import {render, screen} from '@testing-library/angular'
await render(MyComponent)
const dialogContainer = screen.getByRole('dialog')
cy.findByRole('dialog').should('exist')
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 theheading
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.