Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React A11y Testing
import {expect} from 'chai';
import App from '../app/components/App';
import a11yHelper from "./a11yHelper";
describe('Accessibility', function () {
this.timeout(10000);
it('Has no errors', function () {
let config = {};
// let config = {
// "rules": {
// "color-contrast": { enabled: false }
// }
// };
a11yHelper.testEnzymeComponent(<App/>, config, function (results) {
expect(results.violations.length).to.equal(0);
});
});
});
import React from 'react';
import {findDOMNode, render} from 'react-dom';
import {mount} from 'enzyme';
import axeCore from 'axe-core';
var a11yHelper = {};
/**
* Test a component with React's Test Utils.
*
* @param {any} app - Your app reference.
* @param {object} config (optional) - An aXe config object to enable/disable rules. See
* [axe.a11yCheck](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#options-parameter).
* @param {function} callback - A callback function to execute when aXe returns.
*/
a11yHelper.testReactComponent = function(app, config, callback) {
let div = document.createElement('div');
document.body.appendChild(div);
this.wrapper = render(app, div);
let node = findDOMNode(div);
if (typeof config === 'function') {
config = {};
}
this.run(node, config, callback);
document.body.removeChild(div);
}
/**
* Test a component with Enzyme.
*
* @param {any} app - Your app reference.
* @param {object} config (optional) - An aXe config object to enable/disable rules. See
* [axe.a11yCheck](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#options-parameter).
* @param {function} callback - A callback function to execute when aXe returns.
*/
a11yHelper.testEnzymeComponent = function (app, config, callback) {
let div = document.createElement('div');
document.body.appendChild(div);
let wrapper = mount(app, { attachTo: div });
let node = findDOMNode(wrapper.component);
if (typeof config === 'function') {
config = {};
}
this.run(node, config, callback);
document.body.removeChild(div);
}
/**
* Run an aXe audit.
* @private
*
* @param {object} node - A node reference from your app.
* @param {object} config - An aXe config or empty object.
* @param {function} callback - A callback function to execute when aXe returns.
*/
a11yHelper.run = function(node, config, callback) {
var oldNode = global.Node;
global.Node = node.ownerDocument.defaultView.Node;
axeCore.run(node, config, function(err, results) {
global.Node = oldNode;
if (err instanceof Error) {
return err;
}
a11yHelper.report(results);
// return to the test expectation
callback(results);
});
}
/**
* Report results in a readable fashion.
* @private
* @param {object} results - The aXe [results object](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#results-object).
*/
a11yHelper.report = function(results) {
// output some useful information
let failureNotice = '';
if (results.violations.length > 0) {
failureNotice += 'Accessibility violations:\n';
results.violations.forEach(function(violation) {
failureNotice += violation.description + '\n';
failureNotice += 'HTML Nodes: \n';
violation.nodes.forEach(function(node) {
failureNotice += node.html + '\n';
});
});
console.log(failureNotice);
}
}
module.exports = a11yHelper;
@goksu

This comment has been minimized.

Copy link

goksu commented Nov 9, 2017

This is a very clear and helpful. Thank you!

@rachel-church

This comment has been minimized.

Copy link

rachel-church commented Nov 28, 2018

What is global.Node and why is it being set to node.ownerDocument.defaultView.Node?

@clottman

This comment has been minimized.

Copy link

clottman commented Oct 7, 2019

I changed line 43 to let node = wrapper.getDOMNode(); and then this worked! Thanks Marcy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.