Skip to main content
Version: v6

Testing with Jest

The AudioEye Accessibility Testing SDK Jest library lets you test rendered components for accessibility issues as part of your existing unit and integration test suite.

If you are new to the SDK, complete the client-credential and package-manager setup in getting started before installing the Jest package.

When to use Jest

Jest is a good fit when you want to:

  • catch accessibility issues while developing components
  • validate rendered output in unit or integration tests
  • test isolated UI states without launching a full browser
  • add accessibility assertions to an existing React Testing Library workflow

If you need to test full browser behavior such as routing, authentication, or multi-step flows, use Playwright or Cypress instead.

Prerequisites

Before you begin, make sure you have:

  • configured your client credentials as described in getting started
  • installed Jest in your project
  • set up a DOM-based test environment when your tests render UI
  • installed any rendering utilities your app already uses, such as @testing-library/react
Browser-context limitations

The Jest SDK evaluates component markup in a test environment. Because Jest does not run in a real browser page, some rules that depend on full browser context, computed styles, layout, or runtime page behavior are not available here.

For more details, see About Our Rules.

Installation

Install the Jest SDK as a development dependency:

npm install -D @audioeye/testing-sdk-jest

Test environment setup

Most component tests should run in a DOM-capable environment such as jsdom.

A minimal Jest configuration looks like this:

jest.config.js
module.exports = {
testEnvironment: 'jsdom',
};

If you are using Jest 28 or later, make sure your project has configured the test environment correctly for that version of Jest. See the Jest documentation for details:

Register the matcher

Once at the top of your test file (or in a Jest setup file applied to all tests), extend expect with the toBeAccessible matcher:

import { toBeAccessible } from '@audioeye/testing-sdk-jest';

expect.extend({ toBeAccessible });

If you have a project-wide Jest setup file (e.g. jest.setup.ts referenced from setupFilesAfterEach in your Jest config), this is a good place to register the matcher once for the whole suite.

Your first accessibility test

The basic workflow is:

  1. render a component
  2. assert it is accessible with expect(...).toBeAccessible()

Here is a simple React example using React Testing Library:

import { toBeAccessible } from '@audioeye/testing-sdk-jest';
import { render } from '@testing-library/react';
import React from 'react';

expect.extend({ toBeAccessible });

function Image({ altText }: { altText: string }) {
return <img src="/logo.png" alt={altText} />;
}

describe('Image', () => {
it('passes when alt text is descriptive', () => {
expect(render(<Image altText="The AudioEye company logo" />)).toBeAccessible();
});

it('catches weak alt text', () => {
expect(render(<Image altText="image" />)).not.toBeAccessible();
});
});

When the assertion fails, the matcher prints every issue it found, with the rule code, target selector, and a direct link to the rule's developer documentation.

Supported input types

For convenience, the matcher and scan accept several common input types. This makes it easier to use with the testing tools you already have:

  • string
  • RenderResult (from @testing-library/react)
  • HTMLElement
  • DocumentFragment
  • JQuery<HTMLElement>

Common usage patterns

Filter to a specific WCAG conformance level

expect(render(<Image altText="image" />)).toBeAccessible({ level: 'AA' });

level accepts 'A', 'AA', or 'AAA'. The filter is cumulative — AA includes both A and AA results.

Ignore specific rules

expect(container).toBeAccessible({ ignoreRules: ['Img_Name_WeakName'] });

Restrict to specific rules

expect(container).toBeAccessible({ onlyRules: ['Img_Name_Missing', 'Img_Name_WeakName'] });

Filter by CSS selector

Use withinSelector to keep only issues inside a section, or excludingSelector to suppress issues inside a known container. Selectors use normal CSS ancestor matching against the rendered test document.

expect(container).toBeAccessible({ excludingSelector: '[data-testid="third-party-widget"]' });

const report = scan(container, { withinSelector: 'main article' });
expect(report.isEmpty).toBe(true);

Inspect the report instead of asserting

When you want to look at issues — count them, group them, or assert about which rule fired — call scan directly. It returns an A11yResults instance you can chain assertions against.

import { scan } from '@audioeye/testing-sdk-jest';
import { render } from '@testing-library/react';

import { Image } from './image';

describe('Image', () => {
it('produces exactly one weak-name issue', () => {
const report = scan(render(<Image altText="image" />));

expect(report.count).toBe(1);
expect(report.has('Img_Name_WeakName')).toBe(true);
expect(report.issues[0].helpUrl).toMatch(/developer\.audioeye\.com/);
});
});

scan returns an A11yResults instance synchronously, so all of its filters and getters work directly inside expect:

expect(scan(container).conformanceLevel('AA').isEmpty).toBe(true);
expect(scan(container).withoutRule('Img_Name_WeakName').count).toBe(0);
expect(scan(container).resultCodes).toEqual([]);

Use an HTMLElement

const { container } = render(<Image altText="image" />);
expect(container).toBeAccessible();

Use a DocumentFragment

const { asFragment } = render(<Image altText="image" />);
expect(asFragment()).toBeAccessible();

Use raw HTML

expect('<img src="/logo.png" alt="image" />').toBeAccessible();

SDK API

toBeAccessible(options?) — Jest matcher

Asserts that the rendered output has no accessibility issues after applying the given filters.

Parameters

options.level ('A' | 'AA' | 'AAA', optional) — WCAG conformance level filter; cumulative by default.

options.onlyRules (string[], optional) — Restrict to these rule codes.

options.ignoreRules (string[], optional) — Drop these rule codes.

options.withinSelector (string, optional) — Keep only issues whose target descends from this CSS selector.

options.excludingSelector (string, optional) — Drop issues whose target descends from this CSS selector.

options.baselineIds (string[], optional) — Drop issues whose stable fingerprint (A11yIssue.id) appears in this list.

scan(source, options?) — functional API

Run AudioEye's rules against a rendered source and return an A11yResults instance for inspection.

const report = scan(render(<Image altText="image" />));

Parameters

source (string | RenderResult | HTMLElement | DocumentFragment | JQuery<HTMLElement>) — what to evaluate.

options (same shape as toBeAccessible options, optional) — filters applied to the report before returning.

Returns

An instance of A11yResults.

A11yResults API

A11yResults is the chainable report object returned by scan (and used internally by toBeAccessible). All filter methods return a new instance, so chains are immutable.

Filters

A11yResults filter methods, their return types, and what each one does.
MethodReturnsDescription
conformanceLevel(level, opts?)A11yResultsFilter to issues at the given WCAG level. Cumulative; pass { exact: true } for exact-level only.
withRule(...codes)A11yResultsRestrict to issues for the given rule codes.
withoutRule(...codes)A11yResultsDrop issues for the given rule codes.
withinSelector(selector)A11yResultsRestrict to issues inside the given CSS ancestor selector.
excludingSelector(selector)A11yResultsDrop issues inside the given CSS ancestor selector.
excludingIds(ids)A11yResultsDrop issues whose stable fingerprint matches an entry in ids.

Queries

A11yResults query members, their types, and what each one returns.
MemberTypeDescription
countnumberTotal issue count after filtering.
isEmptybooleantrue when no issues remain after filtering.
has(ruleCode)booleantrue when at least one issue matches the rule code.
get(ruleCode)A11yIssue[]Issues for the given rule code.
issuesA11yIssue[]All issues remaining after filtering.
resultCodesstring[]Sorted, deduplicated array of rule codes.
resultsGroupedByWcagSuccessCriteriaLevelRecord<string, A11yIssue[]>Issues grouped by WCAG level ('A', 'AA', 'AAA', or '').

Issue shape

Each entry in issues has:

Fields on each A11yIssue entry, their types, and what each holds.
FieldTypeDescription
idstringStable SARIF fingerprint, useful for baseline ignore lists.
ruleCodestringThe rule that fired (e.g. 'Img_Name_WeakName').
ruleMetadataRuleMetaOutputWCAG criterion, level, name, fix-guidance, etc.
helpUrlstringDirect link to the rule's developer documentation.
cssSelectorstringCSS selector path for the violating element.
xpathstringXPath for the violating element.
sourcestringouterHTML of the violating element.
boundingBox{ x; y; width; height } | undefinedLayout box of the element (real-browser scans only).
framestring[] | undefinedFrame chain for violations inside iframes.
relatedNodesArray<{ cssSelector; xpath?; html }> | undefinedOptional context nodes the rule provides.

Legacy API (deprecated)

Deprecated in v6, removed in v7

The accessibility.evaluate (and a11y.evaluate) form continues to work in v6 but emits a one-time deprecation warning the first time it is called per process. New code should use the matcher or scan. See the v5 → v6 upgrade guide.

import { accessibility } from '@audioeye/testing-sdk-jest';
import { render } from '@testing-library/react';

const results = accessibility.evaluate(render(<Image altText="image" />));
expect(results.resultCodes).toEqual(['Img_Name_WeakName']);

The SDK provides matching accessibility and a11y singleton exports. Both behave identically and both are deprecated.

Troubleshooting

No package found during install

If installation fails, verify that your client credentials and package-manager configuration are set up correctly in getting started.

Tests fail because document or DOM APIs are missing

Confirm that Jest is using a DOM test environment such as jsdom.

Expected browser-dependent rules are not running

Jest does not provide the same browser context as Playwright, Cypress, or the CLI. If you need browser-context rules, use a browser-based integration instead. See About Our Rules.

expect(...).toBeAccessible is not a function

Make sure you registered the matcher with expect.extend({ toBeAccessible }). The simplest place is a project-wide Jest setup file (referenced from setupFilesAfterEach in jest.config.js).

Next steps

After your first Jest test is working, you can:

  • add expect(node).toBeAccessible() to existing component tests
  • use scan(...) when you want to assert which issues are present, not just whether the page is clean
  • move browser-dependent scenarios to Playwright or Cypress
  • review Troubleshooting if installation or execution fails