Skip to content

lessthanseventy/excessibility

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Excessibility

Hex.pm Hex Docs CI License: MIT

Accessibility Snapshot Testing for Elixir + Phoenix

Excessibility helps you test your Phoenix apps for accessibility (WCAG compliance) by taking HTML snapshots during tests and running them through Pa11y.

Why Excessibility?

  • Keep accessibility in your existing test feedback loop. Snapshots are captured inside ExUnit, Wallaby, and LiveView tests, so regressions surface together with your functional failures.
  • Ship safer refactors. Baseline comparison saves .good/.bad.html (plus screenshots when enabled) so reviewers can see exactly what changed and approve intentionally.
  • Debug CI-only failures quickly. Pa11y output points to the failing snapshot, and the saved artifacts make it easy to reproduce locally.

How It Works

  1. During tests, call html_snapshot(conn) to capture HTML from your Phoenix responses, LiveViews, or Wallaby sessions
  2. After tests, run mix excessibility to check all snapshots with Pa11y for WCAG violations
  3. When HTML changes, snapshots are diffed against approved baselines — review and approve changes with mix excessibility.approve
  4. In CI, Pa11y reports accessibility violations alongside your test failures

Features

  • Snapshot HTML from Plug.Conn, Wallaby.Session, Phoenix.LiveViewTest.View, and Phoenix.LiveViewTest.Element
  • Automatically diff against saved baselines
  • Interactive approval (good/bad) when snapshots change
  • Optional PNG screenshots via ChromicPDF
  • Mockable system/browser calls for CI
  • Pa11y configuration with sensible LiveView defaults

Installation

Add to mix.exs:

def deps do
  [
    {:excessibility, "~> 0.5", only: [:dev, :test]}
  ]
end

Fetch dependencies and run the installer:

mix deps.get
mix igniter.install excessibility

The installer will:

  • Add configuration to test/test_helper.exs
  • Create a pa11y.json with sensible defaults for Phoenix/LiveView
  • Install Pa11y via npm in your assets directory

Quick Start

  1. Configure the endpoint and helper modules in test/test_helper.exs. The installer does this automatically, or add manually:

    Application.put_env(:excessibility, :endpoint, MyAppWeb.Endpoint)
    Application.put_env(:excessibility, :system_mod, Excessibility.System)
    Application.put_env(:excessibility, :browser_mod, Wallaby.Browser)
    Application.put_env(:excessibility, :live_view_mod, Excessibility.LiveView)
  2. Add use Excessibility in tests where you want snapshots:

    defmodule MyAppWeb.PageControllerTest do
      use MyAppWeb.ConnCase, async: true
      use Excessibility
    
      test "renders home page", %{conn: conn} do
        conn = get(conn, "/")
    
        html_snapshot(conn,
          prompt_on_diff: false,
          screenshot?: true
        )
    
        assert html_response(conn, 200) =~ "Welcome!"
      end
    end
  3. Typical workflow:

    # Write tests with html_snapshot calls, then:
    mix test                    # Generates snapshots in test/excessibility/
    
    # Check accessibility
    mix excessibility           # Runs Pa11y against snapshots, reports violations
    
    # When snapshots change (after updating your UI)
    mix excessibility.approve   # Review and approve/reject changes

Usage

use Excessibility

html_snapshot(conn,
  name: "homepage.html",
  prompt_on_diff: false,
  screenshot?: true
)

The html_snapshot/2 macro works with:

  • Plug.Conn
  • Wallaby.Session
  • Phoenix.LiveViewTest.View
  • Phoenix.LiveViewTest.Element

It returns the source unchanged, so you can use it in pipelines.

Options

Option Type Default Description
:name string auto-generated Custom filename (e.g., "login_form.html"). Default is ModuleName_LineNumber.html
:prompt_on_diff boolean true Interactively choose which snapshot to keep when diff detected
:tag_on_diff boolean true Save diffs as .bad.html and .good.html files
:screenshot? boolean false Generate PNG screenshots (requires ChromicPDF)
:open_browser? boolean false Open the snapshot in your browser after writing
:cleanup? boolean false Delete existing snapshots for the current test module before writing

Snapshot Diffing

Snapshots are saved to test/excessibility/html_snapshots/ and baselines live in test/excessibility/baseline/.

When a snapshot differs from its baseline:

  1. Diff files are created:

    • .good.html — the current approved baseline
    • .bad.html — the new snapshot from your test
  2. You choose which to keep:

    • If prompt_on_diff: true (default), both files open in your browser and you're prompted to keep "good" (baseline) or "bad" (new)
    • Choose "good" to reject the changes
    • Choose "bad" to approve the changes as the new baseline
  3. Baseline is updated with your choice, and the .good.html/.bad.html files are cleaned up

First run: If no baseline exists yet, the snapshot is automatically saved as the baseline.

Configuration

All configuration goes in test/test_helper.exs or config/test.exs:

Config Key Required Default Description
:endpoint Yes Your Phoenix endpoint module (e.g., MyAppWeb.Endpoint)
:system_mod No Excessibility.System Module for system commands (mockable)
:browser_mod No Wallaby.Browser Module for browser interactions
:live_view_mod No Excessibility.LiveView Module for LiveView rendering
:excessibility_output_path No "test/excessibility" Base directory for snapshots
:pa11y_path No auto-detected Path to Pa11y executable
:pa11y_config No "pa11y.json" Path to Pa11y config file
:head_render_path No "/" Route used for rendering <head> content

Example:

# test/test_helper.exs
Application.put_env(:excessibility, :endpoint, MyAppWeb.Endpoint)
Application.put_env(:excessibility, :system_mod, Excessibility.System)
Application.put_env(:excessibility, :browser_mod, Wallaby.Browser)
Application.put_env(:excessibility, :live_view_mod, Excessibility.LiveView)
Application.put_env(:excessibility, :excessibility_output_path, "test/accessibility")

ExUnit.start()

Pa11y Configuration

The installer creates a pa11y.json in your project root with sensible defaults for Phoenix/LiveView:

{
  "ignore": [
    "WCAG2AA.Principle3.Guideline3_2.3_2_2.H32.2"
  ]
}

The ignored rule (H32.2) is "Form does not contain a submit button" — a common false positive for LiveView forms that use phx-submit without traditional submit buttons.

Add additional rules to ignore as needed for your project:

{
  "ignore": [
    "WCAG2AA.Principle3.Guideline3_2.3_2_2.H32.2",
    "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
  ]
}

Screenshots

To enable PNG screenshots, start ChromicPDF in your test helper:

# test/test_helper.exs
{:ok, _} = ChromicPDF.start_link(name: ChromicPDF)

ExUnit.start()

Then use screenshot?: true in your snapshots:

html_snapshot(conn, screenshot?: true)

Screenshots are saved alongside HTML files with .png extension.

Mix Tasks

Task Description
mix igniter.install excessibility Configure test helper, create pa11y.json, install Pa11y via npm
mix excessibility Run Pa11y against all generated snapshots
mix excessibility.approve Interactively approve pending diffs
mix excessibility.approve --keep good Keep all baseline (good) versions
mix excessibility.approve --keep bad Accept all new (bad) versions as baseline

CI and Non-Interactive Environments

For CI or headless environments where you don't want interactive prompts or browser opens, mock the system module:

# test/test_helper.exs
Mox.defmock(Excessibility.SystemMock, for: Excessibility.SystemBehaviour)
Application.put_env(:excessibility, :system_mod, Excessibility.SystemMock)

Then stub in your tests:

import Mox

setup :verify_on_exit!

test "snapshot without browser open", %{conn: conn} do
  Excessibility.SystemMock
  |> stub(:open_with_system_cmd, fn _path -> :ok end)

  conn = get(conn, "/")
  html_snapshot(conn, open_browser?: true)  # Won't actually open
end

File Structure

test/
└── excessibility/
    ├── html_snapshots/          # Current test snapshots
    │   ├── MyApp_PageTest_42.html
    │   ├── MyApp_PageTest_42.png
    │   ├── MyApp_PageTest_42.bad.html
    │   └── MyApp_PageTest_42.good.html
    └── baseline/                # Approved baselines
        └── MyApp_PageTest_42.html

License

MIT © Andrew Moore

About

Library to aid in testing your application for WCAG compliance automatically using Pa11y and Wallaby.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages