Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
ByRole
2.1.
API
2.2.
Options
2.2.1.
hidden
2.2.2.
selected
2.2.3.
checked
2.2.4.
current
2.2.5.
pressed
2.2.6.
expanded
2.2.7.
queryFallbacks
2.2.8.
level
2.2.9.
description
3.
ByTestId
3.1.
API
3.2.
Overriding data-testid
4.
FAQs
4.1.
Elements with a subclass role like switch will be excluded when querying a superclass role like checkbox. Why?
4.2.
An element doesn't have multiple roles in a given environment. Explain.
4.3.
What will happen if the level option is applied to other than the heading role?
4.4.
Why should data-testid be avoided?
4.5.
What is testing library?
5.
Conclusion
Last Updated: Mar 27, 2024
Easy

Queries ByRole and ByTestId

Author Toohina Barua
0 upvote

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

ByTestId

ByTestId comprises of getByTestId, queryByTestId, getAllByTestId, queryAllByTestId, findByTestId, and findAllByTestId.
The options are the same as the TextMatch options.

API

getByTestId(
  // If you're using `screen`, then skip the container argument:
  container: HTMLElement,
  text: TextMatch,
  options?: {
    exact?: boolean = true,
    normalizer?: NormalizerFn,
  }): HTMLElement

 

container.querySelector('[data-testid="${yourId}"]') shortcut (and it also accepts a TextMatch).

<div data-testid="custom-element" />

 

Native:

import {screen} from '@testing-library/dom'

const element = screen.getByTestId('custom-element')


React:

import {render, screen} from '@testing-library/react'

render(<MyComponent />)
const element = screen.getByTestId('custom-element')


Cypress:

cy.findByTestId('custom-element').should('exist')

Overriding data-testid

The DOM Testing Library's ...ByTestId functions use the attribute data-testid by default, following React Native Web's lead, which uses a testID prop to emit a data-testid attribute on the element, and we recommend you use it whenever possible. If you already have a codebase that uses a different attribute for this purpose, you can use configure({testIdAttribute: 'data-my-test-attribute}') to override this value.

FAQs

Elements with a subclass role like switch will be excluded when querying a superclass role like checkbox. Why?

Without inheriting from the ARIA role hierarchy, roles are matched literally by string equality. As a result, elements with a subclass role like switch will be excluded when querying a superclass role like checkbox.

An element doesn't have multiple roles in a given environment. Explain.

In a given environment, an element cannot play multiple roles. There is only one. The environment evaluates multiple roles in the attribute from left to right until it finds the first role it recognizes. This is useful when new roles are introduced and you want to begin supporting them as well as older environments that are unfamiliar with them (yet).

What will happen if the level option is applied to other than the heading role?

Only the heading role is affected by the level option. When used with any other role, an error will be thrown.

Why should data-testid be avoided?

Using data-testid attributes is incompatible with how your software is used and should be avoided at all costs.

What is testing library?

The testing library is a collection of packages that make it easier to test user interface components in a user-centric manner. The closer your test is to how your software is used, the more trust you can have in it.

Conclusion

In this article, we learned about the theoretical and practical implementation of the Queries ByRole and ByTestId in the Testing Library.
We hope that this blog has helped you enhance your knowledge regarding the Queries ByRole and ByTestId in the Testing Library and if you would like to learn more about testing library for ReactVueAngular, check out our articles on Coding Ninjas Studio. Do upvote our blog to help other ninjas grow.

Recommended Reading:

Live masterclass