automated_accessibility_testing_with_ruby_on_rails
automated_accessibility_testing_with_ruby_on_rails copied to clipboard
The shortest path to get set up with automated accessibility testing in Ruby on Rails
✨ Automated accessibility testing ✨ with Ruby on Rails
This is a sample Rails app. In this README, I'll describe the shortest path to get set up with automated accessibility testing.
Web accessibility is the inclusive practice of ensuring there are no barriers that prevent interaction with, or access to, websites on the World Wide Web by people with physical disabilities, situational disabilities, and socio-economic restrictions on bandwidth and speed. When sites are correctly designed, developed and edited, generally all users have equal access to information and functionality.
In addition to Ruby on Rails, we'll use:
- RSpec as our test framework so we can write and run tests
- Capybara as our acceptance-level test framework, on top of RSpec. This way we can write tests that interact with web pages, click through links, fill out forms etc.
- Selenium as a JavaScript driver for Capybara, so that we can test webpages as rendered by an actual web browser
- axe to run accessibility tests against web pages generated through Capybara tests
Step 1
I created a very basic Rails app (b572316
) and scaffolded a Post
model and controllers (47d3776
):
rails new ally --skip-action-mailbox --skip-active-job --skip-action-cable --skip-jbuilder --skip-test --skip-system-test
rails generate scaffold post title body:text
rails db:migrate
Step 2
I added RSpec and Capybara to run acceptance tests in a JS-enabled web browser:
- The RSpec setup consists of adding the
rspec-rails
gem, runningbundle install
andrails generate rspec:install
. - The Capybara setup consists of adding the
capybara
andselenium-webdriver
gems then requiring the right Capybara modules inrails_helper.rb
:
# Gemfile, in the ":development, :test" group:
gem 'rspec-rails'
gem 'capybara'
gem 'selenium-webdriver'
# rails_helper.rb
require 'capybara/rails'
require 'capybara/rspec'
You may also need to install geckodriver
on your machine or server so that Capybara can control Firefox. If you're on a Mac and use Homebrew:
brew install geckodriver
At this point (23965d2
), I was able to run an acceptance test which opens the page in Firefox:
# spec/features/posts_index_spec.rb
require 'rails_helper'
describe 'Posts index', type: :feature do
before do
Post.create!(title: 'Hello, world!', body: 'Lorem ipsum dolor sit amet.')
end
it 'is valid HTML', js: true do
visit '/posts'
expect(page).to have_content('Hello, world!')
end
end
Note the js: true
part which runs the test in a browser.
Step 3
I added axe to be able to run accessibility checks on the HTML generated by Capybara tests. The axe-core-gems
documentation tells us we need 2 gems:
-
axe-core-rspec
for our testing framework (but there is also a gem compatible with Cucumber) -
axe-core-capybara
for our webdriver (but there is also a gem compatible with Watir and one for using Selenium without Capybara)
# Gemfile
gem 'axe-core-rspec'
gem 'axe-core-capybara'
# rails_helper.rb
require 'axe-rspec'
require 'axe-capybara'
I updated the test so that it runs axe
on the resulting web page:
# spec/features/posts_index_spec.rb
it 'is valid HTML', js: true do
visit '/posts'
expect(page).to have_content('Hello, world!')
expect(page).to be_axe_clean # 👈 this is the important part!
end
At this point (287e2b4
), the test failed because the default Rails application layout is not fully accessible according to axe
:
$ rspec
Capybara starting Puma...
* Version 5.2.2 , codename: Fettisdagsbulle
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:63442
F
Failures:
1) Posts index is valid HTML
Failure/Error: expect(page).to be_axe_clean
Found 3 accessibility violations:
1) html-has-lang: <html> element must have a lang attribute (serious)
https://dequeuniversity.com/rules/axe/4.1/html-has-lang?application=axeAPI
The following 1 node violate this rule:
Selector: html
HTML: <html>
Fix any of the following:
- The <html> element does not have a lang attribute
2) landmark-one-main: Document must have one main landmark (moderate)
https://dequeuniversity.com/rules/axe/4.1/landmark-one-main?application=axeAPI
The following 1 node violate this rule:
Selector: html
HTML: <html>
Fix all of the following:
- Document does not have a main landmark
3) region: All page content must be contained by landmarks (moderate)
https://dequeuniversity.com/rules/axe/4.1/region?application=axeAPI
The following 3 nodes violate this rule:
Selector: h1
HTML: <h1>Posts</h1>
Fix any of the following:
- Some page content is not contained by landmarks
Selector: table
HTML: <table>
Fix any of the following:
- Some page content is not contained by landmarks
Selector: a[href$="new"]
HTML: <a href="/posts/new">New Post</a>
Fix any of the following:
- Some page content is not contained by landmarks
Invocation: axe.run(callback);
# ./spec/features/posts_index_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 1.87 seconds (files took 0.47765 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/features/posts_index_spec.rb:8 # Posts index is valid HTML
The 3 failures are really about 2 important rules:
- <html> element must have a lang attribute. Without it, a screen reader assumes the default language set by the user, which becomes an issue for users who speak multiple languages and access website in more than one language.
- Page must have one main landmark. Navigating a web page is far simpler for screen reader users if all of the content splits between one or more high-level sections. Content outside of these sections is difficult to find, and its purpose may be unclear.
Step 4: Fix the failing tests
I updated the Rails application layout (404986d
) with the missing pieces and the test passed:
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index a87185d..e0331f0 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,5 +1,5 @@
<!DOCTYPE html>
-<html>
+<html lang="en">
<head>
<title>Ally</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
@@ -11,6 +11,8 @@
</head>
<body>
- <%= yield %>
+ <main>
+ <%= yield %>
+ </main>
</body>
</html>
$ rspec
Capybara starting Puma...
* Version 5.2.2 , codename: Fettisdagsbulle
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:64391
.
Finished in 4.28 seconds (files took 0.73106 seconds to load)
1 example, 0 failures
That's it! 🎉
Of course, there's more work to do to ensure our web app is fully accessible, but having these basic checks in our CI/CD pipeline is already a good start.
Bonus step: switch to a headless webdriver
A headless browser has no GUI and runs faster, which is ideal to run a test suite either locally or on a CI server.
Add this line to rails_helper.rb
:
Capybara.javascript_driver = :selenium_headless