You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This PR introduces a unit-level accessibility testing framework using jest-axe, adds new a11y tests for the Container component (covering multiple API states), and fixes several accessibility gaps in existing components. Below is a summary of the changes:
1. Accessibility Test Setup
jest-axe-setup.js
Registers the toHaveNoViolations() matcher from jest-axe, so we can assert that rendered DOM snapshots have zero a11y violations:
Covers three API-driven states for the Container component, mocking fetch and using waitFor to ensure the DOM is updated before running axe(container):
importReactfrom'react';import{render,screen,waitFor}from'@testing-library/react';import{axe}from'jest-axe';importContainerfrom'./Container';importconfigfrom'../Testing/Mocks/config.json';importcardsfrom'../Testing/Mocks/cards.json';// 1) Mock fetch → return non-empty cards arraybeforeAll(()=>{global.fetch=jest.fn(()=>Promise.resolve({ok: true,status: 200,json: ()=>Promise.resolve({ cards }),}));});afterAll(()=>{deleteglobal.fetch;});describe('Container accessibility',()=>{it('renders without a11y violations when API returns cards',async()=>{const{ container }=render(<Containerconfig={config}/>);// Wait until the grid is in the DOMawaitwaitFor(()=>screen.getByTestId('consonant-CardsGrid'));constresults=awaitaxe(container);expect(results).toHaveNoViolations();});it('renders accessible no-results view when API returns empty array',async()=>{// Override fetch to return no cardsglobal.fetch.mockImplementationOnce(()=>Promise.resolve({ok: true,status: 200,json: ()=>Promise.resolve({cards: []}),}));const{ container }=render(<Containerconfig={config}/>);awaitwaitFor(()=>screen.getByTestId('consonant-NoResultsView'));constresults=awaitaxe(container);expect(results).toHaveNoViolations();});it('renders accessible error view when API fails',async()=>{// Override fetch to simulate a network errorglobal.fetch.mockImplementationOnce(()=>Promise.resolve({ok: false,status: 404,statusText: 'Not Found',json: ()=>Promise.resolve({}),}));const{ container }=render(<Containerconfig={config}/>);awaitwaitFor(()=>screen.getByTestId('consonant-NoResultsView'));constresults=awaitaxe(container);expect(results).toHaveNoViolations();});});
Why this matters:
By mocking fetch and waiting for the relevant elements (CardsGrid or NoResultsView) to appear, we guarantee that axe inspects the final DOM (including dynamic content).
This pattern ensures we catch missing ARIA labels, focus issues, or color-contrast problems even when the component's contents change based on API responses.
3. Component Accessibility Fixes
Card.jsx heading level logic
Ensures headingLevel is always a valid number (defaults to 2 if invalid), preventing invalid aria-level values:
- const headingLevel = getConfig('collection.i18n', 'cardTitleAccessibilityLevel');+ let headingLevel = getConfig('collection.i18n', 'cardTitleAccessibilityLevel');++ // Support alternate key "titleHeadingLevel" if supplied+ if (!headingLevel) {+ const titleLevel = getConfig('collection.i18n', 'titleHeadingLevel');+ if (typeof titleLevel === 'string') {+ const parsed = parseInt(titleLevel.replace(/[^0-9]/g, ''), 10);+ headingLevel = Number.isNaN(parsed) ? undefined : parsed;+ }+ }++ // Default to level 2 if still undefined or invalid+ if (!headingLevel || typeof headingLevel !== 'number') {+ headingLevel = 2;+ }
const additionalParams = getConfig('collection', 'additionalRequestParams');
// …
const lastModified = getConfig('collection', 'i18n.lastModified');
PanelFooter.jsx (mobile filters)
Added an aria-label on the "Done" button so screen readers announce it correctly:
Running axe(container) in JSDOM gives immediate feedback on missing alt text, improper ARIA roles, focus-order issues, and color-contrast failures (to the extent JSDOM can evaluate them).
By mocking API calls and using waitFor, we test dynamic states—for example, opening a filters panel or handling empty/error responses—without launching a full browser.
Component-level fixes
Ensuring every interactive element or landmark has an accessible name (aria-label, valid aria-level, etc.) reduces the chance of keyboard/navigation or screen-reader issues.
Defaulting to a valid headingLevel prevents rendering invalid aria-level attributes.
CI integration
The new npm run test:a11y command runs only a11y tests (matching *.a11y.test.js(x)), so our CI can block merges if any accessibility violations occur.
Unit tests now automatically catch regressions—no need for a separate Pa11y or Puppeteer job on every PR.
5. Next Steps
Extend coverage as new components are built
Follow the same pattern (render parent, fire events, wait for dynamic DOM changes, await axe(container)).
Consider full-browser audits later
For screenshots, color-contrast based on compiled CSS, and real focus-navigation tests, we can add an AxePuppeteer or Pa11y job against our staging environment in a separate PR.
Summary
This PR:
✅ Sets up Jest-axe for unit-level a11y tests
✅ Adds three new accessibility tests for Container (normal, empty, error states)
✅ Fixes missing aria-label and invalid heading levels in key components
✅ Introduces test:a11y for CI to verify no regressions in accessibility
Please review and approve so we can catch a11y issues automatically on every merge.
raissanjay
changed the title
test(mwpw-1234): some descr
test(mwpw-1234): add jest‐axe accessibility testing, container unit tests, and component a11y fixes
Jun 4, 2025
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR introduces a unit-level accessibility testing framework using jest-axe, adds new a11y tests for the Container component (covering multiple API states), and fixes several accessibility gaps in existing components. Below is a summary of the changes:
1. Accessibility Test Setup
jest-axe-setup.js
Registers the
toHaveNoViolations()matcher from jest-axe, so we can assert that rendered DOM snapshots have zero a11y violations:jest.a11y.config.js
A specialized Jest config that:
*.a11y.test.js(x)files onlyUpdates to jest.config.js
Added
jest-axe-setup.jsundersetupTestFrameworkScriptFileso thatexpect(...).toHaveNoViolations()is available globally:module.exports = { // … moduleFileExtensions: ['js', 'json', 'jsx'], - setupFiles: ['<rootDir>/enzyme.config.js'], + setupFiles: ['<rootDir>/enzyme.config.js'], + setupTestFrameworkScriptFile: '<rootDir>/jest-axe-setup.js', testEnvironment: 'jest-environment-jsdom-fifteen', // … };package.json modifications
Added
react-axe,axe-core, andjest-axeto devDependencies.Introduced a new npm script to run only accessibility tests:
"scripts": { "test:unit": "jest", + "test:a11y": "jest --config=jest.a11y.config.js", }Now, running
npm run test:a11yexecutes only files matching*.a11y.test.js(x)and skips coverage.2. New Container a11y Tests
File:
react/src/js/components/Consonant/Container/Container.a11y.test.jsxCovers three API-driven states for the Container component, mocking fetch and using waitFor to ensure the DOM is updated before running
axe(container):Why this matters:
3. Component Accessibility Fixes
Card.jsx heading level logic
Ensures
headingLevelis always a valid number (defaults to 2 if invalid), preventing invalid aria-level values:PanelFooter.jsx (mobile filters)
Added an
aria-labelon the "Done" button so screen readers announce it correctly:Title.jsx (mobile filter header)
Added an explicit
aria-label="Back"on the back-button element so assistive technologies know its purpose:4. Why These Changes Matter
Unit-level a11y coverage with jest-axe
axe(container)in JSDOM gives immediate feedback on missing alt text, improper ARIA roles, focus-order issues, and color-contrast failures (to the extent JSDOM can evaluate them).waitFor, we test dynamic states—for example, opening a filters panel or handling empty/error responses—without launching a full browser.Component-level fixes
aria-label, validaria-level, etc.) reduces the chance of keyboard/navigation or screen-reader issues.headingLevelprevents rendering invalidaria-levelattributes.CI integration
npm run test:a11ycommand runs only a11y tests (matching*.a11y.test.js(x)), so our CI can block merges if any accessibility violations occur.5. Next Steps
Extend coverage as new components are built
Follow the same pattern (render parent, fire events, wait for dynamic DOM changes,
await axe(container)).Consider full-browser audits later
For screenshots, color-contrast based on compiled CSS, and real focus-navigation tests, we can add an AxePuppeteer or Pa11y job against our staging environment in a separate PR.
Summary
This PR:
test:a11yfor CI to verify no regressions in accessibilityPlease review and approve so we can catch a11y issues automatically on every merge.