sa11y

@sa11y/jest

Accessibility matcher for Jest

Overview

The toBeAccessible() API from this library can be used in Jest unit tests to test HTML elements or DOM for accessibility.

[![Watch Automated Accessibility Tests with sa11y Developer Quick Takes](https://img.youtube.com/vi/ScqZisOBbUM/0.jpg)](https://www.youtube.com/watch?v=ScqZisOBbUM&list=PLgIMQe2PKPSJdFGHjGpjd1FbCsOqq5H8t&index=21)

Screenshot showing Sa11y Jest API usage and a11y errors showing up in VSCode

Install

Setup

The accessibility APIs need to be registered with Jest before they can be used in tests.

Project level

You can set up the sa11y API once at the project level to make it available to all the Jest tests in the project. For an example look at the Integration test setup in @sa11y.

// Import using either CommonJS `require` or ES6 `import`
const { setup } = require('@sa11y/jest'); // CommonJS
import { setup } from '@sa11y/jest'; // ES6
// Register the sa11y matcher
setup();

// Register with custom options for automatic checks and DOM saving
setup({
    autoCheckOpts: { runAfterEach: true, cleanupAfterEach: true },
    renderedDOMSaveOpts: {
        /* your RenderedDOMSaveOpts here */
    },
});
module.exports = {
    setupFilesAfterEnv: ['<rootDir>/sa11y-jest-setup.js'],
};
const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config');

const setupFilesAfterEnv = jestConfig.setupFilesAfterEnv || [];
setupFilesAfterEnv.push('<rootDir>/jest-sa11y-setup.js');

module.exports = {
    ...jestConfig,
    setupFilesAfterEnv,
};

Test module level

Invoke setup before using the toBeAccessible API in the tests

import { setup } from '@sa11y/jest';

beforeAll(() => {
    setup();
});

Usage

import { extended, full } from '@sa11y/preset-rules';
import { setup } from '@sa11y/jest';

beforeAll(() => {
    setup();
});

it('should be accessible', async () => {
    // Setup DOM to be tested for accessibility
    //...

    // assert that DOM is accessible (using base preset-rule)
    await expect(document).toBeAccessible();

    // Can be used to test accessibility of a specific HTML element
    const elem = document.getElementById('foo');
    await expect(elem).toBeAccessible();

    // To test using an extended ruleset with best practices and experimental rules
    await expect(document).toBeAccessible(extended);

    // To test using all rules provided by axe
    await expect(document).toBeAccessible(full);
});

Caveats

Disabled checks

Following rules are disabled for the Sa11y Jest API

Automatic checks

The Sa11y Jest API can be setup to be automatically invoked at the end of each test as an alternative to adding the toBeAccessible API at the end of each test.

setup({ autoCheckOpts: { runAfterEach: true } });

// To optionally cleanup the body after running a11y checks
setup({ autoCheckOpts: { runAfterEach: true, cleanupAfterEach: true } });

// To customize how/where the rendered DOM is saved during automatic checks
setup({
    autoCheckOpts: { runAfterEach: true },
    renderedDOMSaveOpts: {
        /* your RenderedDOMSaveOpts here */
    },
});

Using environment variables

Automatic checks can also be enabled using environment variables

SA11Y_AUTO=1 SA11Y_CLEANUP=1 jest

Sa11y results processor

The sa11y custom test results processor can be enabled using e.g., - jest --json --outputFile results.json --testResultsProcessor node_modules/@sa11y/jest/dist/resultsProcessor.js

JSON result transformation

With default results processor - a11y error is embedded within the test failure:

{
    "assertionResults": [
        {
            "ancestorTitles": ["integration test @sa11y/jest"],
            "failureMessages": [
                "A11yError: 1 Accessibility issues found\n * (link-name) Links must have discernible text: a\n\t- Help URL: https://dequeuniversity.com/rules/axe/4.1/link-name\n    at Function.checkAndThrow (packages/format/src/format.ts:67:19)\n    at automaticCheck (packages/jest/src/automatic.ts:54:19)\n    at Object.<anonymous> (packages/jest/src/automatic.ts:69:13)"
            ],
            "fullName": "integration test @sa11y/jest should throw error for inaccessible dom",
            "location": null,
            "status": "failed",
            "title": "should throw error for inaccessible dom"
        }
    ]
}

With sa11y results processor:

{
    "assertionResults": [
        {
            "ancestorTitles": ["integration test @sa11y/jest"],
            "failureMessages": [
                "A11yError: 1 Accessibility issues found\n * (link-name) Links must have discernible text: a\n\t- Help URL: https://dequeuniversity.com/rules/axe/4.1/link-name\n    at Function.checkAndThrow (packages/format/src/format.ts:67:19)\n    at automaticCheck (packages/jest/src/automatic.ts:54:19)\n    at Object.<anonymous> (packages/jest/src/automatic.ts:69:13)"
            ],
            "fullName": "integration test @sa11y/jest should throw error for inaccessible dom",
            "location": null,
            "status": "disabled",
            "title": "should throw error for inaccessible dom"
        }
    ]
}
{
    "assertionResults": [
        {
            "ancestorTitles": [
                "integration test @sa11y/jest",
                "integration test @sa11y/jest should throw error for inaccessible dom"
            ],
            "failureMessages": [
                "Accessibility issues found: Links must have discernible text\nCSS Selectors: a\nHTML element: <a href=\"#\"></a>\nHelp: https://dequeuniversity.com/rules/axe/4.1/link-name\nTests: \"integration test @sa11y/jest should throw error for inaccessible dom\"\nSummary: Fix all of the following:\n  Element is in tab order and does not have accessible text\n\nFix any of the following:\n  Element does not have text that is visible to screen readers\n  aria-label attribute does not exist or is empty\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty\n  Element has no title attribute"
            ],
            "fullName": "[Sa11y WCAG2.0-LevelA-SC4.1.2] Links must have discernible text: a",
            "location": null,
            "status": "failed",
            "title": "should throw error for inaccessible dom"
        }
    ]
}

Group violation results processor

Limitations

Automatic checks currently has the following limitations.

Disabled Checks

Options

autoCheckOpts:

Environment Variables