/** * @fileoverview Enforce that an element does not have an unsupported ARIA attribute. * @author Ethan Cohen */ // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- import { aria, roles, } from 'aria-query'; import { RuleTester } from 'eslint'; import { version as eslintVersion } from 'eslint/package.json'; import semver from 'semver'; import iterFrom from 'es-iterator-helpers/Iterator.from'; import filter from 'es-iterator-helpers/Iterator.prototype.filter'; import map from 'es-iterator-helpers/Iterator.prototype.map'; import toArray from 'es-iterator-helpers/Iterator.prototype.toArray'; import parserOptionsMapper from '../../__util__/parserOptionsMapper'; import parsers from '../../__util__/helpers/parsers'; import rule from '../../../src/rules/role-supports-aria-props'; // ----------------------------------------------------------------------------- // Tests // ----------------------------------------------------------------------------- const ruleTester = new RuleTester(); const generateErrorMessage = (attr, role, tag, isImplicit) => { if (isImplicit) { return `The attribute ${attr} is not supported by the role ${role}. This role is implicit on the element ${tag}.`; } return `The attribute ${attr} is not supported by the role ${role}.`; }; const errorMessage = (attr, role, tag, isImplicit) => ({ message: generateErrorMessage(attr, role, tag, isImplicit), type: 'JSXOpeningElement', }); const componentsSettings = { 'jsx-a11y': { components: { Link: 'a', }, }, }; const nonAbstractRoles = toArray(filter(iterFrom(roles.keys()), (role) => roles.get(role).abstract === false)); const createTests = (rolesNames) => rolesNames.reduce((tests, role) => { const { props: propKeyValues, } = roles.get(role); const validPropsForRole = Object.keys(propKeyValues); const invalidPropsForRole = filter( map(iterFrom(aria.keys()), (attribute) => attribute.toLowerCase()), (attribute) => validPropsForRole.indexOf(attribute) === -1, ); const normalRole = role.toLowerCase(); return [ tests[0].concat(validPropsForRole.map((prop) => ({ code: `
`, }))), tests[1].concat(toArray(map(invalidPropsForRole, (prop) => ({ code: `
`, errors: [errorMessage(prop.toLowerCase(), normalRole, 'div', false)], })))), ]; }, [[], []]); const [validTests, invalidTests] = createTests(nonAbstractRoles); ruleTester.run('role-supports-aria-props', rule, { valid: parsers.all([].concat( { code: '' }, { code: '
' }, { code: '
' }, { code: '
' }, { code: '
' }, { code: '' }, { code: '' }, // IMPLICIT ROLE TESTS // A TESTS - implicit role is `link` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // this will have global { code: '' }, // AREA TESTS - implicit role is `link` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // this will have global { code: '' }, // LINK TESTS - implicit role is `link` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // this will have global { code: '' }, // IMG TESTS - no implicit role { code: '' }, // this will have role of `img` { code: 'foobar' }, // MENU TESTS - implicit role is `toolbar` when `type="toolbar"` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // this will have global { code: '' }, // MENUITEM TESTS // when `type="command`, the implicit role is `menuitem` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="checkbox`, the implicit role is `menuitemcheckbox` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="radio`, the implicit role is `menuitemradio` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // these will have global { code: '' }, { code: '' }, // INPUT TESTS // when `type="button"`, the implicit role is `button` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="image"`, the implicit role is `button` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="reset"`, the implicit role is `button` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="submit"`, the implicit role is `button` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="checkbox"`, the implicit role is `checkbox` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="radio"`, the implicit role is `radio` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // when `type="range"`, the implicit role is `slider` { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // these will have role of `textbox`, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, { code: '' }, // Allow null/undefined values regardless of role { code: '

' }, { code: '

' }, // OTHER TESTS { code: '