Testing accessibility with RSpec and Capybara

October 2, 2012 Cameron Cundiff

Edit 04/07/13: See the followup article for an alternative to using skip navigation links.

An exploration in automated accessibility testing

Today Grant Hutchins and I took on several stories to enhance the accessibility of a site. One of them was to add a skip-navigation link to the application.

To understand why skip-nav links are important, visit Jim Thatcher’s explanation.

Our immediate inclination was to write a request spec with Capybara to drive out the solution. We came up with the following.

The Test

require "spec_helper"

feature "Keyboard Navigation" do
  scenario "hidden skip navigation link shows when focused and jumps to content", js: true do
    login_as(users(:user))
    visit root_path

    skip_link = page.find("#skip-navigation a")
    skip_link.should have_content "Skip navigation"
    skip_link.native.location.y.should be < 0

    body_element = page.find("body")
    body_element.native.send_keys(:tab)
    skip_link.native.location.y.should == 0

    skip_link.native.send_keys(:return)
    skip_link.native.location.y.should be < 0

    current_url.should match(/#content$/)
  end
end

The Markup

%body
  #skip-navigation
    %p= link_to "Skip navigation", "#content", tabindex: 0
    ...
  #content
    ...

The Styles

body {
  #skip-navigation {
    a, a:hover, a:visited {
      position:absolute;
      left:0px;
      top:-500px;
      overflow:hidden;
    }

    a:active, a:focus {
      position:absolute;
      left:0;
      top:0;
    }
  }
}

What’s happening here?

We are asserting that the “hidden skip navigation link shows when focused and jumps to content” when clicked.

The most important aspect of what we did was emulating keyboard navigation. We’re using js: true so we have access to Selenium’s native methods and thus the send_keys method. This allows us to send keypress messages to the browser.

Since we’ve used positioning to hide the element, we also have an assertion around that property.

Problems in CI!

The application behaved as expected and the tests passed locally.

When we ran the tests in CI however, the tests failed. The reason is that _the browser must retain foremost focus in the OS in order for the :focus css pseudo-selector to fire on the skip-nav element. Without the :focus style rules applied, the skip-nav element remained invisible, and the tests failed.

We tried a workaround using within_window and forcing browser focus, but couldn’t get it to work. We’ve got a few more tricks up our sleeve that we’re going to try, and will report back here.

Edit:
We added within_window to the test to force focus on the browser. This makes the test less brittle locally (because it won’t fail if you click out of the window).

window = page.driver.browser.window_handles.last
page.within_window(window) do
  skip_link.native.location.y.should == 0
end

About the Author

Biography

Previous
Polishing Cloud Foundry's Ruby Gem Support
Polishing Cloud Foundry's Ruby Gem Support

The Cloud Foundry team has released new features to improve management of Ruby gems in apps running on Clou...

Next
Google Hangouts an improvement over Skype
Google Hangouts an improvement over Skype

The Problem Skype connectivity sometimes drops calls. It is also challenging to schedule with multiple pe...