Simple and complete DOM testing utilities that encourage good testing practices.
—
Work with ARIA roles and the accessibility tree to understand how assistive technologies perceive your UI.
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);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);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);
});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);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`);
});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;
}
});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);
});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