Skip to content

KevinBongart/automated_accessibility_testing_with_ruby_on_rails

Repository files navigation

✨ 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, running bundle install and rails generate rspec:install.
  • The Capybara setup consists of adding the capybara and selenium-webdriver gems then requiring the right Capybara modules in rails_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:

  1. <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.
  2. 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

About

The shortest path to get set up with automated accessibility testing in Ruby on Rails

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published