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.
Unfortunately, the spec defines that <input type="password" />
has no implicit
role. This means that in order to query this type of element we must fallback to
a less powerful query such as ByLabelText
.
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.