Cypress Accessibility Testing: Practical Tips and Updates
Add accessibility checks to Cypress, cover important UI states, and understand what automated results can—and cannot—tell you.
Cypress accessibility testing works best as a repeatable feedback and regression process: exercise meaningful application states with Cypress, run automated accessibility checks on those states, add assertions for requirements a generic scan cannot infer, and manually evaluate interaction with keyboards and assistive technology. A clean automated report means the checker found no reportable issues in the tested scope; it does not prove WCAG conformance or usability for disabled people.
This guide covers two practical routes: an in-test Axe Core scan with the community cypress-axe plugin, and Cypress Accessibility reports from recorded Cypress Cloud test runs. Choose based on where you want results and how much control you need over test code.
1. Decide what the test results should cover
Start with the journeys that matter: signing in, completing a purchase, submitting a form, or using a core workflow. List states that change content or available actions, such as:
- Dialogs open and closed, including focus placement and return focus.
- Menus and disclosures expanded and collapsed.
- Forms before submission, with validation errors, and after success.
- Loading, empty, and error states.
- Responsive layouts or other variants that change navigation or content.
Automated coverage follows test coverage. A page, state, or journey the tests never reach is outside the evidence provided by that run. Treat accessibility checks as a layer added to end-to-end and component tests, not a substitute for them. Cypress describes these approaches in its accessibility testing guide.
2. Add an Axe Core scan to an open-source Cypress test
The cypress-axe community plugin injects Axe Core into the page and adds Cypress commands for scanning. This route lets you decide exactly where scans run and how detected violations affect the test.
Install and configure
npm install --save-dev cypress-axe
Import the commands once in the Cypress support file used by your specs. For a typical end-to-end setup:
// cypress/support/e2e.js
import 'cypress-axe';
For component tests, put the import in the support file configured for the component testing type instead. Keep the setup in the support file so every relevant spec can use the commands.
Run a scan after the page is ready
// cypress/e2e/checkout.cy.js
describe('checkout accessibility', () => {
it('checks the initial checkout page', () => {
cy.visit('/checkout');
cy.injectAxe();
cy.checkA11y();
});
});
Run it with your normal Cypress command, for example npx cypress run --spec "cypress/e2e/checkout.cy.js". The scan examines the current DOM state. Put it after the page or component is mounted and after the relevant content has rendered.
Scan important states, not just the initial page
describe('checkout accessibility states', () => {
beforeEach(() => {
cy.visit('/checkout');
cy.injectAxe();
});
it('checks validation feedback', () => {
cy.get('button[type="submit"]').click();
cy.get('[role="alert"]').should('be.visible');
cy.checkA11y();
});
it('checks the address dialog', () => {
cy.contains('button', 'Add address').click();
cy.get('[role="dialog"]').should('be.visible');
cy.checkA11y('[role="dialog"]');
});
});
The optional context argument scopes a scan to a selector or element. Use it when a focused check is useful, but retain page-level scans where appropriate: a narrow scope will not report issues elsewhere on the page. Ensure asynchronous content has appeared before scanning; otherwise, you may check the wrong state or miss content.
Choose rules and impact levels deliberately
cy.checkA11y() supports options passed to Axe Core, including rule configuration and result handling. A targeted example:
cy.checkA11y(null, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
}
});
Use a project-wide rule policy rather than adding ad hoc exceptions in many tests. Axe rule tags, defaults, and plugin behavior can change with dependency versions; consult the cypress-axe documentation and the Axe Core guidance for the version installed. Do not disable a rule simply to make CI green without documenting the reason, owner, and review date.
By default, a failing assertion is useful for preventing regressions, but a large existing backlog can make adoption difficult. One workable rollout is to establish a baseline, focus new checks on changed or high-value areas, fix findings in manageable groups, and progressively widen enforcement. If you temporarily collect results without failing, make sure violations remain visible and assigned; a report nobody reviews is not a regression control.
3. Use Cypress Accessibility reports from recorded runs
Cypress Accessibility is a Cypress Cloud workflow that analyzes recorded test activity and produces reports based on the states your tests reach. Cypress says this feature uses Axe Core and has a configured default target; check the current product documentation for supported rules and configuration. This managed route is optional: open-source Cypress tests and plugins remain an option.
For local feedback, record only the specs exercising the code you changed:
npx cypress run --record --key "$CYPRESS_RECORD_KEY" \
--spec "cypress/e2e/checkout.cy.js"
Set CYPRESS_RECORD_KEY in the shell or CI environment, or pass the key directly with --key. Cypress documents that interactive cypress open does not record a Cloud run and therefore does not produce this accessibility report. Its current local workflow requires a Chromium-based or Electron browser. See Cypress’s local development instructions for details.
After the recorded run finishes, open its Cypress Cloud run and review the Accessibility tab. Record the same relevant specs before and after a fix when you want to compare findings. A recorded report is useful feedback about its test snapshots, not evidence for unvisited routes.
4. Add assertions for requirements a scan cannot know
A rule-based scan can detect many markup issues, but it cannot infer every intended name, message, or interaction. Add Cypress assertions for user-facing requirements that matter to the product. For example:
it('gives the submit control a useful accessible name', () => {
cy.visit('/checkout');
cy.findByRole('button', { name: 'Place order' }).should('be.visible');
});
it('connects invalid email feedback to the field', () => {
cy.visit('/signup');
cy.findByRole('button', { name: 'Create account' }).click();
cy.findByLabelText('Email').should('have.attr', 'aria-invalid', 'true');
cy.findByText('Enter a valid email address').should('be.visible');
});
The second example assumes the application uses the shown label and validation design. Adapt it to the actual semantics. These checks verify specific expectations; they do not establish that all labels, messages, or flows are accessible.
Also test keyboard behavior where it is part of the interaction contract: can a user reach and activate the control, does a dialog manage focus appropriately, and can focus leave the component as expected? Assertions should encode observable requirements rather than implementation details that can change without affecting users.
5. Triage results and verify remediation
- Set a target. Agree on the conformance target and initial scope with the people responsible for accessibility.
- Review each finding. Identify the affected element, state, rule, and whether the result is a confirmed violation, a manual review item, or inconclusive.
- Prioritize user impact and ownership. Start with issues in code your team owns and workflows users need to complete.
- Fix the cause. Prefer correcting the semantic structure, accessible name, relationship, or behavior over hiding the finding with a rule exception.
- Re-run the relevant test states. Confirm the original finding is resolved and check whether the change introduced a new issue.
- Expand coverage incrementally. Add important pages, component states, and journeys as the process becomes routine.
Cypress’s guidance recommends treating automation as a detector rather than a verdict: its documentation cites an estimate that automation can catch up to 57% of issues that would appear in a manual audit. That is Cypress’s published estimate, not a guarantee for a particular application. Human review remains necessary.
6. Complete the evaluation with people and assistive technology
Include manual keyboard testing and relevant screen-reader evaluation. Check whether the workflow is understandable, focus moves appropriately in context, and assistive technology communicates the right information. Where possible, involve disabled users in usability validation. Automated rules cannot judge every aspect of conformance or real use; Cypress states this limitation in its automation principles.
7. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
cy.injectAxe or cy.checkA11y is undefined |
The plugin import did not run for this test type, or the package is missing. | Install cypress-axe and import it from the configured end-to-end or component support file. |
| The scan passes but the page has accessibility problems | The tested state or scope is incomplete, or the issue requires human judgment. | Exercise more states, remove overly narrow scan context where suitable, add explicit assertions, and conduct manual checks. |
| A dialog or dynamic region is absent from findings | The scan ran before asynchronous content appeared or before the interaction opened the region. | Assert the state is visible or ready, then run the scan. |
| Many violations appear at once | The application has an existing backlog or the scan reached a new state. | Group findings by shared root cause, establish ownership, and prioritize core journeys. Preserve visibility while rolling out enforcement. |
| The Cypress Cloud Accessibility tab is missing | The run was not recorded, interactive mode was used, the browser is unsupported for the report, or Cloud setup is incomplete. | Use cypress run --record, a supported Chromium-based or Electron browser, and verify the project and recording key configuration. |
| Local report does not reflect a code change | The recorded spec did not exercise the changed page or state. | Record the relevant spec and ensure the test traverses the changed interaction. |
| A check fails intermittently | Tests scan before the app reaches a stable state, or content varies between runs. | Wait for a meaningful readiness condition, control test data, and avoid arbitrary delays where a specific selector or state assertion can be used. |
8. Performance, reliability, and cost considerations
In-test scans add work to the test run: the page DOM is evaluated against applicable rules. Cypress’s accessibility testing guide notes that this has overhead, so scan useful states rather than redundantly scanning an unchanged page after every action. No universal runtime cost applies; it depends on the page, rules, browser, and test setup.
Recorded Cypress Accessibility reports require the Cypress Cloud recording workflow, while cypress-axe runs within tests. Check current Cypress Cloud plan terms and feature availability directly before budgeting; the supplied sources do not establish current pricing. For reliable feedback, keep state setup deterministic, run the relevant specs locally or in CI, and retain a manual review process for questions automation cannot answer.
Or skip the browser setup
If you need a screenshot as visual evidence for a bug report or review, ScreenshotNeo can capture a page with one GET request. It is a website screenshot API and MCP server from ScreenshotNeo; it does not replace Cypress accessibility tests, keyboard checks, screen-reader evaluation, or accessibility reports.
For the do-it-yourself route, use Cypress and an Axe Core integration as shown above. To capture a page without setting up a browser automation script, call ScreenshotNeo:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python and Node.js examples, plus the available capture options, are in the ScreenshotNeo API documentation.
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed; response headers report the page verdict and billing status.
- An MCP server gives Claude, Cursor, and other MCP clients screenshot, page-info, and PDF tools.
- The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.
FAQ
Can Cypress tests prove WCAG compliance?
No. They can find reportable issues in the states and scope tested. Conformance assessment requires human evaluation.
Do I need Cypress Cloud to run accessibility checks?
No. An open-source Cypress test can use a community integration such as cypress-axe. Cypress Accessibility is an optional Cloud reporting workflow.
Should every accessibility finding fail CI?
That depends on your rollout and baseline. Make findings visible and owned; then enforce checks in a way that prevents regressions without burying the team in an unmanaged backlog.
Does a screenshot count as an accessibility test?
No. A screenshot can help document visual state, but it cannot replace DOM checks, keyboard interaction, assistive-technology evaluation, or user testing.


