Web Accessibility: A Necessity for Everyone

Internet accessibility is a fundamental requirement which affects not only people with disabilities, but all of us. It provides seamless access to online content for seniors, children and people with visual impairments, but also for those with temporary limitations, such as a broken arm.

Surprisingly, most disabilities are not congenital, but are caused by illness or accidents.1 This means that every one of us could be in need of accessible websites at some point. Therefore, it is imperative that websites facilitate the participation of people with disabilities in daily life. From 28 June 2025, accessible products and services will become mandatory for most online shops and e-book providers. This is when the EU Accessibility Act (EAA) comes into force. Accessibility will then become compulsory for many website operators. This can be difficult to implement without automated accessibility testing.

Automated Accessibility Testing with Axe

To ensure that a website is accessible, regular checks for potential barriers are essential – ideally automated, as even minor changes can exclude some users by affecting the user experience.

In this article, I’m going to share two ways to run frontend accessibility testing using Axe, a tool from Deque. Not only is Axe free, but it’s also well-suited for automated testing and is one of the leading tools in the field. It is important to note that in creating this example, we expect errors to showcase the functionality of Axe. It’s important to note that Axe alone may not fully detect all barriers, but it does provide an easy entry point to identify and fix errors. Axe has a JavaScript library that easily integrates with Java and Selenium.

Axe’s integration into automated tests

The integration of Axe into automated testing is a powerful accessibility testing tool that is easy to install and use. In the following section, I’ll show how Axe can be used in conjunction with Selenium and Cucumber.

In the first example, I’m using Java, but you can also use Axe in other programming languages and testing frameworks, such as Playwright (I’ll explain this in the second example below). Of course, all the necessary dependencies must be present in the project, including Axe-Selenium integration or the Axe-Playwright package.

To illustrate the structure of the project, here are examples of the directory structure for both Java/Selenium and TypeScript/Playwright: Here for Java with Selenium:

└───a11y-minimal-selenium
    │
    ├───axeReports
    │ AccessibilityViolations_XYZ.txt
    │
    └───src
        ├───main
        │   ├───java
        │   └───resources
        │
        └───test
            ├───java
            │ AccessibilityTestSteps.java
            │ TestRunner.java
            │
            └───resources
                    a11y.feature

And here for TypeScript with Playwright:

└───a11y-minimal-playwright
    │
    ├───axeReports
    │   └───artifacts
    │           accessibility-scan-results-XYZ.html
    │
    ├───test
    │   ├───features
    │   │       a11y.feature
    │   │
    │   └───steps
    │           a11y.steps.ts
    │
    └───test-results
            cucumber-report.html

In both cases, I use Cucumber to describe our requirement in a specific scenario. In our case, this scenario is that our website is to be barrier-free.

#  a11y.feature
Feature: Accessibility Testing

  Scenario: Verify accessibility of a web page
    Given I open the web page "www.cronn.de/"
    When I check the accessibility of the page
    Then accessibility violations should be found
    And a report is generated

Now we will take a closer look at the individual steps in both variants: Java/Selenium and TypeScript/Playwright.

Accessibility testing with Java/Selenium

After the webpage has been opened and the axeBuilder has been defined as new AxeBuilder(), the accessibility analysis is performed in the When I check the accessibility of the page step. This can look like this:

AccessibilityTestSteps.java

@When("I check the accessibility of the page")
public void i_check_the_accessibility_of_the_page() {
    axeResults = axeBuilder.analyze(driver);
}

Of course, it is also possible to check only for certain options or check only for specific rules or tags, which can be found on the Axe Rules page. You can also include and exclude certain elements and combine everything as you wish. It might look like this (where the individual options, rules, etc. have been defined beforehand):

var axeResults = axeBuilder.withOptions(options)
                .withRules(axeRules)
                .withTags(axeTags)
                .disableRules(disabledRules)
                .exclude("#excludedElement")
                .analyze(driver);

Then we check to see if we can find accessibility issues in our instance of Results called axeResults. This is also very convenient with:

AccessibilityTestSteps.java

@Then("accessibility violations should be found")
public void accessibility_violations_should_be_found() {
    assertFalse("Accessibility violations found", axeResults.getViolations().isEmpty());
}

We can then write the accessibility problems we find in our report. This can be designed completely according to your own requirements and the following is only a minimal example of a text file with an output during the test:

AccessibilityTestSteps.java

@And("a report is generated")
public void a_report_is_generated() {
    List<Rule> violations = axeResults.getViolations();
    if (!violations.isEmpty()) {
        logger.info(() -> "Violations: " + violations);
        String AxeReportPath = System.getProperty("user.dir") + File.separator + "axeReports" + File.separator;
        String timeStamp = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new java.util.Date());
        String AxeViolationReportPath = AxeReportPath + "AccessibilityViolations_" + timeStamp;

        assertTrue(getReadableAxeResults(ResultType.Violations.getKey(), driver, violations));
        AxeReporter.writeResultsToTextFile(AxeViolationReportPath, AxeReporter.getAxeResultString());
    } else {
        logger.warn(() -> "No Accessibility violations found");
    }
}

This accessibility test uses the webdriver setup of this test project along with Axe-webdriverjs and calls AxeBuilder to then parse the page with Axe-core. As soon as the test results are returned, they can be output directly in the console or via generated reports. There is also a direct explanation of what the problem is and where it is located. In addition, reference is made directly to the appropriate rule for further explanations:

ACCESSIBILITY CHECK
VIOLATIONS check for: www.cronn.de/
Found 1 items

1: Heading levels should only increase by one
Description: Ensures the order of headings is semantically correct
Help URL: https://dequeuniversity.com/rules/axe/4.7/heading-order?application=axeAPI
Impact: moderate
Tags: cat.semantics, best-practice
        HTML element: <h4 class="title text-center">agile</h4>
        Selector: [.col-md-3.col-lg-2.col-6:nth-child(2) > .title]

Accessibility testing with TypeScript/Playwright and Cucumber

Firstly, the necessary packages need to be installed. Secondly, the accessibility tests can then be implemented. Here is a simple example using TypeScript and Playwright.

After the website has opened and the axeBuilder has been defined in our class as the new AxeBuilder(), we want to perform the accessibility analysis in the When I check the accessibility of the page step. This is done easily with:

// a11y.steps.ts

When("I check the accessibility of the page", async function () {
  accessibilityScanResults = await new AxeBuilder({ page }).analyze();
});

Of course, it is also possible to include and exclude certain elements in this way, as already shown above.

In the last step, it is checked if we find any problems, in other words: if our checklist is complete.

// a11y.steps.ts

Then("accessibility violations should be found", async function () {
  expect(accessibilityScanResults.violations.length).toBeGreaterThan(0);
});

Here, too, we generate reports from the accessibility problems found, this time as an HTML report, and check that it is complete.

// a11y.steps.ts

Then("a report is generated", async function () {
  const now = new Date().toISOString().replace(/[-T:]/g, "_").slice(0, -5);
  const filename = `accessibility-scan-results-${now}.html`;
  const axeReportPath = "axeReports";
  createHtmlReport({
    results: accessibilityScanResults,
    options: {
      outputDirPath: axeReportPath,
      reportFileName: filename,
    },
  });

  expect(
    accessibilityScanResults.violations.map((v) => v.help),
    `A11y report in ${filename}`
  ).not.toEqual([]);
});

Here, you can view the clear, HTML-formatted report:

Accessibility test results as HTML-formatted report

Summary

Ensuring the accessibility of websites for people of different abilities is of fundamental importance. Using Axe simplifies the process of performing automated accessibility testing and provides an excellent starting point for identifying and resolving issues.

Axe’s integration into automated testing enables effective accessibility monitoring, regardless of whether Java/Selenium or TypeScript/Playwright is being used. Clear reporting and customization options make it easier to identify accessibility violations, which is particularly important in the context of societal obligations and ever-increasing legal requirements.

  1. Further information on disabilities in Germany can be found in the press release from the federal statistical office