← Back to React Testing Library Mastery
Intermediate18 min read

Accessibility Queries

Master role, label, and accessible name queries for inclusive tests.

Roles and Accessible Names

Browsers expose accessibility tree from DOM—roles from implicit HTML (button, input) or explicit aria-role. Accessible name from label association, aria-label, or text content.

getByRole("button", { name: "Save" }) matches visible text or aria-label combined with role.

<button aria-label="Close dialog">×</button>
// test:
screen.getByRole("button", { name: /close dialog/i });

Label Association

Prefer label with htmlFor matching input id or wrapping input. getByLabelText finds control by label text—most forms should use this query.

Placeholder is not substitute for label—tests failing getByLabelText often reveal a11y bug.

<label htmlFor="email">Email</label>
<input id="email" type="email" />

screen.getByLabelText("Email");

Heading and Landmark Structure

getByRole("heading", { level: 2 }) verifies document outline. navigation, main, complementary landmarks help screen reader navigation—assert presence in layout tests.

Single h1 per page rule testable in page component integration test.

expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent("Dashboard");
expect(screen.getByRole("main")).toBeInTheDocument();

Hidden and aria-hidden

Elements with aria-hidden true or display:none excluded from getByRole default. Decorative icons should be aria-hidden with meaningful text elsewhere.

{ hidden: true } option finds off-screen elements intentionally—skip links, visually hidden text.

  • aria-live regions: status and alert roles for dynamic messages
  • getByRole combobox, listbox for custom select widgets
  • Match aria-expanded state in query when multiple similar buttons

Improving App Accessibility via Tests

When test forced to use testid, fix component markup first—add label or role. RTL tests as living a11y specification for team.

Pair query-first tests with periodic axe scan on page level. Keyboard test: tab to control and activate without mouse.

await user.tab();
expect(screen.getByRole("button", { name: /submit/i })).toHaveFocus();

Get In Touch


Ready to discuss your next project? Drop me a message.