Custom Jest matcher for aXe for testing accessibility
The GDS Accessibility team found that only ~30% of issues are found by automated testing.
Tools like aXe are similar to code linters such as eslint or sass-lint: they can find common issues but cannot guarantee what you build works for users.
You'll also need to:
- test your interface with the assistive technologies that real users use (see also WebAIM's survey results).
- include people with disabilities in user research.
npm install --save-dev jest-axe
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage', async () => {
const render = () => '<img src="#"/>'
// pass anything that outputs html to axe
const html = render()
expect(await axe(html)).toHaveNoViolations()
})
Note, you can also require
'jest-axe/extend-expect'
which will callexpect.extend
for you. This is especially helpful when using the jestsetupTestFrameworkScriptFile
configuration.
const React = require('react')
const { render } = require('react-dom')
const App = require('./app')
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with react', async () => {
render(<App/>, document.body)
const results = await axe(document.body)
expect(results).toHaveNoViolations()
})
Testing React with Enzyme
const React = require('react')
const App = require('./app')
const { mount } = require('enzyme')
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with enzyme', async () => {
const wrapper = mount(<App/>)
const results = await axe(container.getDOMNode())
expect(results).toHaveNoViolations()
})
Testing React with React Testing Library
const React = require('react')
const App = require('./app')
const { render, cleanup } = require('@testing-library/react')
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with react testing library', async () => {
const { container } = render(<App/>)
const results = await axe(container)
expect(results).toHaveNoViolations()
cleanup()
})
Note: If you're using
react testing library
you should be using thecleanup
method. This method removes the rendered application from the DOM and ensures a clean HTML Document for further testing.
Testing Vue with Vue Test Utils
const App = require('./App.vue')
const { mount } = require('@vue/test-utils')
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with vue test utils', async () => {
const wrapper = mount(Image)
const results = await axe(wrapper.element)
expect(results).toHaveNoViolations()
})
Testing Vue with Vue Testing Library
const React = require('react')
const App = require('./app')
const { render, cleanup } = require('@testing-library/vue')
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with react testing library', async () => {
const { container } = render(<App/>)
const results = await axe(container)
expect(results).toHaveNoViolations()
cleanup()
})
Note: If you're using
vue testing library
you should be using thecleanup
method. This method removes the rendered application from the DOM and ensures a clean HTML Document for further testing.
The axe
function allows options to be set with the same options as documented in axe-core:
const { axe, toHaveNoViolations } = require('jest-axe')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with a custom config', async () => {
const render = () => `
<div>
<img src="#"/>
</div>
`
// pass anything that outputs html to axe
const html = render()
const results = await axe(html, {
rules: {
// for demonstration only, don't disable rules that need fixing.
'image-alt': { enabled: false }
}
})
expect(results).toHaveNoViolations()
})
If you find yourself repeating the same options multiple times, you can export a version of the axe
function with defaults set.
Note: You can still pass additional options to this new instance; they will be merged with the defaults.
This could be done in Jest's setup step
// Global helper file (axe-helper.js)
const { configureAxe } = require('jest-axe')
const axe = configureAxe({
rules: {
// for demonstration only, don't disable rules that need fixing.
'image-alt': { enabled: false }
}
})
module.exports = axe
// Individual test file (test.js)
const { toHaveNoViolations } = require('jest-axe')
const axe = require('./axe-helper.js')
expect.extend(toHaveNoViolations)
it('should demonstrate this matcher`s usage with a default config', async () => {
const render = () => `
<div>
<img src="#"/>
</div>
`
// pass anything that outputs html to axe
const html = render()
expect(await axe(html)).toHaveNoViolations()
})
- Jest for the great test runner that allows extending matchers.
- aXe for the wonderful axe-core that makes it so easy to do this.
- Government Digital Service for making coding in the open the default.
- GOV.UK Publishing Frontend team who published the basis of the aXe reporter
- jest-image-snapshot for inspiration on README and repo setup