Skip to content

Commit

Permalink
Add a11y specs
Browse files Browse the repository at this point in the history
Right now, no adaptions have been make to get
specs to pass...

tl;dr: Use axe through Google Chrome to audit
accessibility of all pages.

* add jekyll-sitemap necessary to iterate over all pages
* add a new GitHub CI workflow to run specs
* add Gems necessary to run specs
* add support files for specs

Run specs with: `bundle exec rspec`
This runs two "specs" on each page:
* WCAG 2.0 and 2.1 A and AA tests
* WCAG 2.2 and all best practices tests

This separation is done to make it easier to see
what's often necessary for legal a11y compliance
in both the US and EU where WCAG 2.1 AA is the
typical standard.

Failing specs have a screenshot of the page
saved to tmp/capybara.

If you have elements which cannot be made
accessible, you can add them add the HTML attribute
`[data-a11y-errors=true]` to the element.
This allows tests to pass, but communicates that
the element is not accessible for some reason.

These tests have been adapted from a use of JtD:
http://github.com/berkeley-cdss/berkeley-class-site

Future work needs to be done to run the tests both
on light mode and dark mode. (This can likely done
by calling JS via Capybara to change the theme.)
  • Loading branch information
cycomachead committed Aug 15, 2024
1 parent ac78bfe commit 9d9ba9e
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 1 deletion.
34 changes: 34 additions & 0 deletions .github/workflows/rspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Run all page tests

# Run when a PR is opened, any branch is pushed
# Also allow manually triggering workflows, and running if a release is created.
on:
- pull_request
- push
- workflow_dispatch
- deployment
- release

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
# The Ruby version is determined by either a `.ruby-version` or a `.tool-versions` file in root of the repo.
bundler-cache: true
- name: Run a11y tests
run: |
bundle exec rspec
- name: summary
if: failure()
run: ruby spec/support/spec_summary.rb
- name: Keep screenshots from failed tests
uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
path: ${{ github.workspace }}/tmp/capybara
if-no-files-found: ignore
retention-days: 7
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ node_modules
*.gem
.bundle
.ruby-version

# Used for automated test results and metadata.
tmp/
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-f documentation -f documentation --out tmp/rspec_output.txt -f json --out tmp/rspec_output.json -f html --out tmp/rspec_output.html
18 changes: 17 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,21 @@ gemspec
gem "jekyll-github-metadata", ">= 2.15"

gem "jekyll-include-cache", group: :jekyll_plugins
gem "jekyll-sitemap", group: :jekyll_plugins

gem "html-proofer", "~> 5.0", :group => :development
group :development, :test do
gem "html-proofer", "~> 5.0"

# Test Infrastructure
gem 'rack'
gem 'rackup'
gem 'rspec'
gem 'webrick'

# Frontend a11y tests
gem 'axe-core-capybara'
gem 'axe-core-rspec'
gem 'capybara'
gem 'capybara-screenshot'
gem 'selenium-webdriver'
end
89 changes: 89 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,48 @@ GEM
fiber-annotation
io-event (~> 1.5, >= 1.5.1)
timers (~> 4.1)
axe-core-api (4.9.1)
dumb_delegator
virtus
axe-core-capybara (4.9.1)
axe-core-api (= 4.9.1)
dumb_delegator
axe-core-rspec (4.9.1)
axe-core-api (= 4.9.1)
dumb_delegator
virtus
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
base64 (0.2.0)
bigdecimal (3.1.8)
capybara (3.40.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.11)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
capybara-screenshot (1.0.26)
capybara (>= 1.0, < 4)
launchy
childprocess (5.1.0)
logger (~> 1.5)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
colorator (1.1.0)
concurrent-ruby (1.2.3)
console (1.25.2)
fiber-annotation
fiber-local (~> 1.1)
json
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.5.1)
dumb_delegator (1.0.0)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
Expand Down Expand Up @@ -59,6 +94,7 @@ GEM
http_parser.rb (0.8.0)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
io-event (1.5.1)
jekyll (4.3.3)
addressable (~> 2.4)
Expand All @@ -85,18 +121,26 @@ GEM
sass-embedded (~> 1.54)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
json (2.7.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
launchy (3.0.1)
addressable (~> 2.8)
childprocess (~> 5.0)
liquid (4.0.4)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.0)
matrix (0.4.2)
mercenary (0.4.0)
mini_mime (1.1.5)
net-http (0.4.1)
uri
nokogiri (1.16.5-arm64-darwin)
Expand All @@ -116,15 +160,36 @@ GEM
ttfunk
public_suffix (5.0.5)
racc (1.7.3)
rack (3.1.7)
rack-test (2.1.0)
rack (>= 1.3)
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
rainbow (3.1.1)
rake (13.2.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
regexp_parser (2.9.2)
rexml (3.3.3)
strscan
rouge (4.2.1)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.1)
ruby-rc4 (0.1.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass-embedded (1.75.0-arm64-darwin)
google-protobuf (>= 3.25, < 5.0)
Expand All @@ -133,17 +198,31 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
selenium-webdriver (4.23.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
strscan (3.1.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
thread_safe (0.3.6)
timers (4.3.5)
ttfunk (1.8.0)
bigdecimal (~> 3.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
unicode-display_width (2.5.0)
uri (0.13.0)
virtus (2.0.0)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
webrick (1.8.1)
websocket (1.2.11)
xpath (3.2.0)
nokogiri (~> 1.8)
yell (2.2.2)
zeitwerk (2.6.13)

Expand All @@ -152,11 +231,21 @@ PLATFORMS
x86_64-linux-gnu

DEPENDENCIES
axe-core-capybara
axe-core-rspec
bundler (>= 2.3.5)
capybara
capybara-screenshot
html-proofer (~> 5.0)
jekyll-github-metadata (>= 2.15)
jekyll-include-cache
jekyll-sitemap
just-the-docs!
rack
rackup
rspec
selenium-webdriver
webrick

BUNDLED WITH
2.5.9
53 changes: 53 additions & 0 deletions spec/accessibility_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

# Run accessibility specs for all pages in the webiste.
# This runs the axe accessibility checker on each page in a headless browser.

# spec_helper ensures the webiste is built and can be served locally
require 'spec_helper'

# Axe-core test standards groups
# See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#axe-core-tags
# Tests are segmented in 2.0, 2.1 and 2.2+
# In most places WCAG 2.1AA is the minimum requirement, but 2.2 is the current WCAG Standard.
required_a11y_standards = %i[wcag2a wcag2aa wcag21a wcag21aa]
complete_a11y_standards = %i[wcag22aa best-practice secion508]

# axe-core rules that are not required to be accessible / do not apply
# You may temporarily want to add rules here during development.
# See: https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
skipped_rules = []
# These are elements that are not required to be accessible
# It should be rare to add to this list. This disables all rules for an element.
# e.g. <img data-a11y-errors="true" src="..." /> would pass even though it's missing alt text.
excluded_elements = [
'[data-a11y-errors="true"]'
]

# We must call this to ensure the build it up-to-date.
build_jekyll_site!
ALL_PAGES = load_sitemap
puts "Running tests on #{ALL_PAGES.count} pages."
puts " - #{ALL_PAGES.join("\n - ")}\n\n"

ALL_PAGES.each do |path|
describe "#{path} is accessible", :js, type: :feature do
before do
visit(path)
end

it 'meets WCAG 2.1' do
expect(page).to be_axe_clean
.according_to(*required_a11y_standards)
.skipping(*skipped_rules)
.excluding(*excluded_elements)
end

it 'meets WCAG 2.2' do
expect(page).to be_axe_clean
.according_to(*complete_a11y_standards)
.skipping(*skipped_rules)
.excluding(*excluded_elements)
end
end
end
Loading

0 comments on commit 9d9ba9e

Please sign in to comment.