How to test for accessibility with Cypress

How to test for accessibility with Cypress

Cypress is a complete end-to-end testing tool. It reduces complexity by offering an all-inclusive testing platform, rather than requiring you to select and piece together individual libraries. Creating, writing, running, and debugging become simple processes with Cypress. And, by integrating Deque’s axe accessibility testing tools with Cypress, you can then increase testing coverage and automate the accessibility testing process.

In this article, we’re going to discuss how to:

  1. Create test cases in Cypress
  2. Integrate and use axe to check for accessibility violations
  3. Enhance accessibility tests

Let’s get started!

Creating test cases in Cypress

In the example below, I’ll focus on testing the login form for my app. I want to ensure that my login form is functional before creating a release build. Cypress tests can be used to verify the correct classes, IDs, elements, and more. We can also simulate user actions such as clicks, drags, drops, and hovers.

Starting off, we need to create a file which will contain all of our test case logic. In the project directory, I created a file at this location –  `./cypress/integration/login.js` .

Next, I constructed some simple conditions for a successful test suite. This will help cover the basics and ensure that my application remains functional every time I make a build.

// ./integration/Login.js

describe('Login', function () {

    it('Should load the correct URL', function () {});
    
    it('Has a valid login form.', function () {});

    it('Should display an error message after login failure.', function () {});

    it('Should redirect to the dashboard after login success.', function () {});

});

After a quick setup and some time digging through the docs, we land on a solid set of test cases that cover the main scenarios and a few edge cases, making sure everything holds up as expected.

// ./integration/Login.js

describe('Login', function () {

    before( function () {
        cy.visit('http://localhost:9999/');
    });

    it('Should load the correct URL', function () {
        cy.url().should('eq', 'http://localhost:9999/#/login');
    });
    
    it('Has a valid login form.', function () {
        cy.get('form').within(() => {
            cy.get('input#email').should('be.visible');

            cy.get('input#password').should('be.visible');

            cy.get('button').should('be.visible');
        });
    });

    it('Should display an error message after login failure.', function () {
        cy.get('input#email').type('fail@test.com');

        cy.get('input#password').type('swordfish');

        cy.get('button').click();

        // wait for the server to respond, then test for the error
        cy.wait(1000)
            .get('div.alert')
            .should('be.visible');

        
    });

    it('Should redirect to the dashboard after login success.', function () {
        cy.get('input#email')
            .clear()
            .type('test@test.com')
            .should('have.value', 'test@test.com');

        cy.get('input#password')
            .clear()
            .type('123@123')
            .should('have.value', '123@123');

        cy.get('button').click();

        cy.wait(1000)
            .url().should('eq', 'http://localhost:9999/#/');
    });

});

Integrating and using axe

With a few simple additions, we can increase the amount of coverage that our test suite performs. We can also automate the accessibility testing process and easily capture a large percentage of common and easy to address accessibility issues of the Web Content Accessibility Guidelines (WCAG).

Let’s dive in!

As I write this, I’m presuming that you’re familiar with axe-core—the most widely used open source accessibility rules library. If not, head over here and check out what it’s all about: axe – Accessibility for Development Teams.

An awesome web citizen named Andy Van Slaars did all of the heavy lifting for an axe and Cypress integration. Now, all we have to do is install the plugin and fire off the commands to test for accessibility.

Cypress-axe – npm

First, we install the package using NPM or Yarn.
`npm i cypress-axe` or `yarn add cypress-axe`

Then, follow the documentation to integrate into your Cypress test cases.

In my example, I am using the `before()` hook load the URL for the login page. This is a good spot for me to inject the axe-core library. I can do that using the `cy.injectAxe()` command.

Now I can place the `cy.checkA11y()` command in various locations of my test script to validate or expose accessibility violations.

To really make use of the violation results, you’re going to need to toggle the developer tools in the Cypress test runner window. Once you have the dev tools console open, you can get a bit more detail about what the issues are, why they are issues, and how to resolve them.

Here is the completed test logic, with axe-core integration.

// ./integration/Login.js

describe('Login', function () {

    before( function () {
        cy.visit('http://localhost:9999/');

        // Inject the axe-core library
        cy.injectAxe();
    });

    it('Should load the correct URL', function () {
        cy.url().should('eq', 'http://localhost:9999/#/login');
    });
    
    it('Has a valid login form.', function () {
        cy.get('form').within(() => {
            cy.get('input#email').should('be.visible');

            cy.get('input#password').should('be.visible');

            cy.get('button').should('be.visible');
        });

        // first a11y test
        cy.checkA11y();
    });

    it('Should display an error message after login failure.', function () {
        cy.get('input#email').type('fail@test.com');

        cy.get('input#password').type('swordfish');

        cy.get('button').click();

        // wait for the server to respond, then test for the error
        cy.wait(1000)
            .get('div.alert')
            .should('be.visible');

        // test a11y again, but only the alert container.
        cy.checkA11y('div.alert');
    });

    it('Should redirect to the dashboard after login success.', function () {
        cy.get('input#email')
            .clear()
            .type('test@test.com')
            .should('have.value', 'test@test.com');

        cy.get('input#password')
            .clear()
            .type('123@123')
            .should('have.value', '123@123');

        cy.get('button').click();

        cy.wait(1000)
            .url().should('eq', 'http://localhost:9999/#/');
    });

});

As a developer or QA engineer, this can significantly increase your ability to find and resolve accessibility issues within the end-to-end testing process. We now have a singular test suite which will inform us when our application is not doing what we expect it to do, or when it has accessibility violations. Because we’re using the axe-core rules engine, the accessibility violations will contain an explanation of why they failed and offer notes on how we can remediate the issue.

Enhanced accessibility testing

To now take this process to the next level, let’s explore axe Watcher. It’s a powerful library designed to integrate automated accessibility scans into your existing end-to-end tests, with very little effort.

With just a few configuration tweaks, axe Watcher integrates into your existing testing setup, adding accessibility scans without overhauling your workflow. The package supports popular testing frameworks such as Cypress, Playwright, Puppeteer, and Webdriver, making it versatile and powerful for developers and QA teams.

Here are the standout features that make axe Watcher a must-have tool for accessibility testing:

1. Automated reports with axe Developer Hub

All scan results are sent to axe Developer Hub, where you can access comprehensive, actionable reports. The integration is seamless, ensuring your test results are stored and analyzed in a centralized hub.

2. Deduplicated accessibility issues

Accessibility issues are deduplicated, saving you from drowning in technical debt or legacy code problems. You can focus on the issues that truly matter, rather than wading through repetitive ones.

3. Git-aware reporting

Axe Watcher reports are git aware. The tool highlights the new issues introduced by the latest code changes, allowing you to quickly pinpoint how and where accessibility standards might have been violated. This prevents older, pre-existing issues from cluttering the results, which helps developers focus on their most recent changes.

4. Prioritized issues by impact

Reports prioritize issues based on the impact they would have on real humans, enabling teams to focus on fixing the most critical problems first and removing accessibility barriers earlier in the development process.

5. CI/CD integration for fast feedback loops

Axe Watcher supports integration with GitHub Actions, and provides a REST endpoint for other CI/CD systems. This flexibility makes it easy for teams to leverage the results from the tests in their existing pipelines and receive feedback about the accessibility health of their application or even create quality gates to ensure new issues are not introduced into the production deployment.

Conclusion

Axe Watcher provides an effortless way to add accessibility scans to your end-to-end testing suite, offering insights that are both comprehensive and actionable. By supporting multiple frameworks and integrating with popular CI/CD tools, it allows you to automate accessibility testing in a scalable way. Its deduplication and git-aware reporting features significantly cut down the noise and let teams focus on what truly matters: building inclusive, accessible software.

Start using axe Watcher and Developer Hub today, and make your app more accessible with just a few lines of code!

photo of Josh McClure

About Josh McClure

Josh helps organizations discover technical solutions to challenging business problems. During his career, he has worn many hats and played various parts on product engineering teams to help fill the gaps. Some of his roles include: developer, consultant, devops engineer, project manager, and sales engineer. Apart from work, Joshua enjoys making music, hiking, camping, and kicking back with his family.
update selasa

Comments 12 responses

  1. Hi Josh. This is a really helpful article. Thank you for sharing. I am interested in `Enhanced Accessibility Testing` and I tried to implement it, however, I got some error. If you have any repository to see the whole code, it can be perfect.
    Thank you in advance.

  2. @Eyup Aydin, I’m glad you’re finding this helpful! If you aren’t a paying WorldSpace Attest customer, you’ll probably encounter an error attempting to install the Attest package. I am assuming this is where you are seeing the error, but let me know if my assumption is incorrect. Thanks!

  3. I’ve installed the Axe plugin, but when I run the test I get

    “TypeError: cy.injectAxe is not a function

    Because this error occurred during a ‘before all’ hook we are skipping the remaining tests in the current suite: ‘filling in forms test'”

    It said it was installed, do I need to add some custom commands to recognize Axe ? Or? What can I check?

  4. @Catherine Anderson-Karena – Did you also install `cypress-axe`? It sounds like that package may be missing.

  5. How did you get it to report actual problems encountered by checka11y? I’m getting things like “1) Accessibility Tests Global Component Assessments Desktop Assessments Petition Petition form should be accessible.: AssertionError: 1 accessibility violation was detected: expected 1 to equal 0” which literally tells me nothing about what’s wrong, let alone how to fix it =/

  6. Can we specify the accessibility standard we want the test to run for?
    Ex:
    WCAG2A
    WCAG2AA
    WCAG2AAA

    And what are the differences between this and using Pally tool for accessibility testing? Thnx

  7. Hi,

    I would love to use Cypress and Axe to perform Accessibility testing on our app. But is it feasible ? The current approach requires to do a cy.visit(myURL) first before injecting Axe. The problem is with my web APP is that I don’t have proper URL for each page. You get to each page through clicks on different menus.

    Can this still work somehow, is there any workaround ?

    Thanks
    Kevin

Leave a Reply

Your email address will not be published. Required fields are marked *