CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-testing-library--dom

Simple and complete DOM testing utilities that encourage good testing practices.

Pending
Overview
Eval results
Files

role-utilities.mddocs/

Role Utilities

Work with ARIA roles and the accessibility tree to understand how assistive technologies perceive your UI.

getRoles

Get all ARIA roles in a container mapped to their elements.

function getRoles(container: HTMLElement): {
  [index: string]: HTMLElement[];
};

Usage:

import {getRoles} from '@testing-library/dom';

// Get all roles
const roles = getRoles(document.body);
// { button: [<button>, <button>], heading: [<h1>], link: [<a>, <a>] }

// Check specific role
const mainRoles = getRoles(document.body);
if (mainRoles.button) {
  console.log(`Found ${mainRoles.button.length} buttons`);
}

// Get all elements with specific role
const allButtons = getRoles(document.body).button || [];
allButtons.forEach(button => console.log('Button:', button.textContent));

// In specific container
const nav = screen.getByRole('navigation');
const navRoles = getRoles(nav);

logRoles

Log all roles with their elements to console.

function logRoles(
  container: HTMLElement,
  options?: {hidden?: boolean}
): string;

Usage:

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

// Log all roles
logRoles(document.body);
// Output:
// button:
//   <button>Submit</button>
// heading:
//   <h1>Welcome</h1>

// Include hidden elements
logRoles(document.body, {hidden: true});

// Capture output
const output = logRoles(document.body);

isInaccessible

Check if element is inaccessible (hidden from screen readers).

function isInaccessible(element: Element): boolean;

Usage:

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

// Check accessibility
const button = screen.getByRole('button');
console.log('Is inaccessible:', isInaccessible(button));  // false

const hiddenDiv = document.getElementById('hidden-content');
console.log('Is inaccessible:', isInaccessible(hiddenDiv));  // true

// Filter accessible elements
const allElements = Array.from(document.querySelectorAll('*'));
const accessibleElements = allElements.filter(el => !isInaccessible(el));

// Validate in tests
test('content is accessible', () => {
  const mainContent = document.getElementById('main-content');
  expect(isInaccessible(mainContent)).toBe(false);
});

computeHeadingLevel

Get heading level (1-6) from tag or aria-level.

function computeHeadingLevel(element: Element): number | undefined;

Usage:

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

// From tag
const heading = document.querySelector('h1');
console.log(computeHeadingLevel(heading));  // 1

// From aria-level
const customHeading = document.querySelector('[role="heading"][aria-level="3"]');
console.log(computeHeadingLevel(customHeading));  // 3

// Validate heading hierarchy
test('proper structure', () => {
  const headings = screen.getAllByRole('heading');
  const levels = headings.map(computeHeadingLevel);
  
  expect(levels[0]).toBe(1);  // First heading is h1
  
  // No skipped levels
  for (let i = 1; i < levels.length; i++) {
    expect(levels[i]).toBeLessThanOrEqual(levels[i - 1] + 1);
  }
});

// Filter by level
const allHeadings = screen.getAllByRole('heading');
const h1Headings = allHeadings.filter(h => computeHeadingLevel(h) === 1);
const h2Headings = allHeadings.filter(h => computeHeadingLevel(h) === 2);

Common Patterns

Understanding Available Roles

import {getRoles, logRoles} from '@testing-library/dom';

// Quick overview
logRoles(document.body);

// Or programmatically
const roles = getRoles(document.body);
Object.keys(roles).forEach(roleName => {
  console.log(`${roleName}: ${roles[roleName].length} elements`);
});

Debugging Query Failures

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

test('find button', () => {
  try {
    screen.getByRole('button', {name: /submit/i});
  } catch (error) {
    console.log('Available roles:');
    logRoles(document.body);
    throw error;
  }
});

Validating Accessibility

import {getRoles, isInaccessible} from '@testing-library/dom';

test('navigation is accessible', () => {
  const nav = document.querySelector('nav');
  expect(isInaccessible(nav)).toBe(false);
  
  const roles = getRoles(nav);
  expect(roles.link).toBeDefined();
  expect(roles.link.length).toBeGreaterThan(0);
});

Comprehensive Accessibility Check

import {getRoles, isInaccessible, logRoles} from '@testing-library/dom';

function validateAccessibility(container) {
  console.log('=== Accessibility Report ===');
  
  logRoles(container);
  
  const allElements = container.querySelectorAll('*');
  const inaccessibleCount = Array.from(allElements)
    .filter(isInaccessible).length;
  
  console.log(`
Total elements: ${allElements.length}
Inaccessible: ${inaccessibleCount}
Accessible: ${allElements.length - inaccessibleCount}
  `);
  
  const roles = getRoles(container);
  const interactiveRoles = ['button', 'link', 'textbox', 'combobox'];
  const interactiveCount = interactiveRoles.reduce((sum, role) => {
    return sum + (roles[role]?.length || 0);
  }, 0);
  
  console.log(`Interactive elements: ${interactiveCount}`);
  
  return {
    totalElements: allElements.length,
    inaccessibleCount,
    interactiveCount,
    roles: Object.keys(roles)
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-testing-library--dom@10.4.2

docs

async.md

config.md

debugging.md

events.md

index.md

queries.md

query-helpers.md

role-utilities.md

screen.md

within.md

tile.json