UNPKG

react-a11y

Version:

Warns about potential accessibility issues with your React elements.

152 lines (128 loc) 4.6 kB
import defRules from './rules'; import * as util from './util'; const allDevices = Object.keys(util.devices).map(key => util.devices[key]); const severity = (val) => { switch (val) { case 0: case 'off': return 'off'; case 1: case 'warn': return 'warn'; case 2: case 'error': return 'error'; default: throw new Error(`react-a11y: invalid severity ${val}`); } }; const normalize = (opts = 'off') => { if (Array.isArray(opts)) { opts[0] = severity(opts[0]); return opts; } return [severity(opts)]; }; const getTests = defns => (Array.isArray(defns) ? defns : [defns]); export default class Suite { constructor(React, ReactDOM, options) { this.options = options; this.React = React; this.ReactDOM = ReactDOM; if (!this.React && !this.React.createElement) { throw new Error('Missing parameter: React'); } const { plugins = [] } = this.options; // prepare all rules by including every plugin and saving their rules // namespaced like plugin/rule this.rules = plugins .map((name) => { try { // eslint-disable-next-line global-require,import/no-dynamic-require const mod = require(`react-a11y-plugin-${name}`); const rules = 'default' in mod ? mod.default.rules : mod.rules; return Object.keys(rules).reduce((acc, key) => ({ ...acc, [`${name}/${key}`]: rules[key] }), {}); } catch (err) { throw new Error(`Could not find react-a11y-plugin-${name}`); } }) .reduce((acc, next) => ({ ...acc, ...next }), defRules); } test(tagName, props, children, done) { Object.keys(this.options.rules).forEach((key) => { const rule = this.rules[key]; // ensure that the rule exists if (!rule) { throw new Error(`react-a11y: rule ${key} not found, ` + 'maybe you\'re missing a plugin?'); } // get options for rule const [ sev, ...options ] = normalize(this.options.rules[key]); if (sev !== 'off') { const ctx = { options, React: this.React, ReactDOM: this.ReactDOM }; getTests(rule).reduce(async (pprev, defn) => { // only fail once per rule // so check if previous test failed // already, if this is true, they havn't- const prev = await pprev; if (!prev) { return prev; } const { tagName: tagNames, msg, url, AX, test, affects = allDevices } = defn; // filter by tagName if (Array.isArray(tagNames)) { if (tagNames.indexOf(tagName) < 0) { return prev; } } else if (tagNames) { if (tagName !== tagNames) { return prev; } } // perform the test let pass; // try/catch so that exceptions are not silently swallowed by await try { pass = await test(tagName, props, children, ctx); } catch (error) { console.log(error); pass = false; } if (!pass) { done({ tagName, msg, url, AX, props, children, severity: sev, rule: key, affects }); } return prev && pass; }, true); } }); } }