Axe is an accessibility testing engine for websites and other HTML-based user interfaces. You can use it as a Chrome extension, Firefox extension, VSCode extension, as part of Google Lighthouse, and in many other ways.

All of those methods are good, but you need to remember to use them, and also all of your teammates need to remember to use them. Wouldn’t it be great if accessibility checks could run automatically on CI?

To achieve that, I wrote an Elixir library called A11yAudit that integrates axe-core into ExUnit assertions, which you can use in your browser-based tests.

#Installation

The library can be installed by adding a11y_audit to your list of dependencies in mix.exs:

def deps do
  [
    {:a11y_audit, "~> 0.1.0", only: :test}
  ]
end

#Usage

A11yAudit provides assertions for browser-based tests. It has no-setup-required assertions for the two most popular browser testing tools in Elixir, Wallaby and Hound.

A11yAudit does not crawl your website. Instead, it relies on you adding the assertions to your tests in the right places. Ideally, you would run the assertion on each page at least once. On pages that can be modified by user interaction with JavaScript or LiveView, you might want to run the assertion more than once.

Expected page to have no accessibility violations, but got 1 violation. Form elements must have labels.
Accessibility violations will cause your tests to fail, printing all violations sorted by severity.

#Usage with Wallaby

Call A11yAudit.Wallaby.assert_no_violations/1 in your Wallaby tests.

defmodule MyAppWeb.HomeTest do
  use ExUnit.Case, async: true
  use Wallaby.Feature

  feature "home page", %{session: session} do
    session
    |> visit("/")
    |> assert_has(Query.css("h1", text: "My App"))
    |> A11yAudit.Wallaby.assert_no_violations()
  end
end

#Usage with Hound

Call A11yAudit.Hound.assert_no_violations/0 in your Hound tests.

defmodule MyAppWeb.HomeTest do
  use ExUnit.Case
  use Hound.Helpers

  test "home page" do
    navigate_to("#{MyAppWeb.Endpoint.url()}/")

    heading = find_element(:css, "h1")
    assert inner_text(heading) == "My App"

    A11yAudit.Hound.assert_no_violations()
  end
end

#For other environments

If you’re running browser-based tests in Elixir without using Wallaby or Hound, you can still use A11yAudit. You will need a way to execute JavaScript snippets, and to get their return values back into your Elixir code. Assuming you have an execute_script function that can do that, you can use the test assertions like so:

get_audit_result =
  fn ->
    execute_script(A11yAudit.JS.axe_core())
    axe_result_map = execute_script(A11yAudit.JS.await_audit_results())
    A11yAudit.Results.from_json(axe_result_map)
  end

A11yAudit.Assertions.assert_no_violations(get_audit_result.())

#Feedback welcome

Please create a GitHub issue if you’re having trouble with using this library, found a bug, or have suggestions for improvements. I’m looking forward to your feedback!

#Disclaimer

There are two kinds of accessibility issues, objective and subjective, and automated testing can only detect the first kind. Thus, it should always be used together with manual testing.

#Need more?

If you’re looking for more automated quality assurance for your Elixir web apps, check out my Elixir client for the W3C HTML validator.

If you’re looking for more high-quality information about web accessibility, check out The A11y Project.