UNPKG

axe-core

Version:

Accessibility engine for automated Web UI testing

198 lines (169 loc) • 5.36 kB
/* global aria, axe */ /** * Check if a given role is valid * @method isValidRole * @memberof axe.commons.aria * @instance * @param {String} role The role to check * @return {Boolean} */ aria.isValidRole = function (role) { 'use strict'; if (aria.lookupTable.role[role]) { return true; } return false; }; /** * Get the roles that get name from the element's contents * @method getRolesWithNameFromContents * @memberof axe.commons.aria * @instance * @return {Array} Array of roles that match the type */ aria.getRolesWithNameFromContents = function () { return Object.keys(aria.lookupTable.role).filter(function (r) { return aria.lookupTable.role[r].nameFrom && aria.lookupTable.role[r].nameFrom.indexOf('contents') !== -1; }); }; /** * Get the roles that have a certain "type" * @method getRolesByType * @memberof axe.commons.aria * @instance * @param {String} roleType The roletype to check * @return {Array} Array of roles that match the type */ aria.getRolesByType = function (roleType) { return Object.keys(aria.lookupTable.role).filter(function (r) { return aria.lookupTable.role[r].type === roleType; }); }; /** * Get the "type" of role; either widget, composite, abstract, landmark or `null` * @method getRoleType * @memberof axe.commons.aria * @instance * @param {String} role The role to check * @return {Mixed} String if a matching role and its type are found, otherwise `null` */ aria.getRoleType = function (role) { var r = aria.lookupTable.role[role]; return (r && r.type) || null; }; /** * Get the required owned (children) roles for a given role * @method requiredOwned * @memberof axe.commons.aria * @instance * @param {String} role The role to check * @return {Mixed} Either an Array of required owned elements or `null` if there are none */ aria.requiredOwned = function (role) { 'use strict'; var owned = null, roles = aria.lookupTable.role[role]; if (roles) { owned = axe.utils.clone(roles.owned); } return owned; }; /** * Get the required context (parent) roles for a given role * @method requiredContext * @memberof axe.commons.aria * @instance * @param {String} role The role to check * @return {Mixed} Either an Array of required context elements or `null` if there are none */ aria.requiredContext = function (role) { 'use strict'; var context = null, roles = aria.lookupTable.role[role]; if (roles) { context = axe.utils.clone(roles.context); } return context; }; /** * Get a list of CSS selectors of nodes that have an implicit role * @method implicitNodes * @memberof axe.commons.aria * @instance * @param {String} role The role to check * @return {Mixed} Either an Array of CSS selectors or `null` if there are none */ aria.implicitNodes = function (role) { 'use strict'; var implicit = null, roles = aria.lookupTable.role[role]; if (roles && roles.implicit) { implicit = axe.utils.clone(roles.implicit); } return implicit; }; /** * Get the implicit role for a given node * @method implicitRole * @memberof axe.commons.aria * @instance * @param {HTMLElement} node The node to test * @return {Mixed} Either the role or `null` if there is none */ aria.implicitRole = function (node) { 'use strict'; /* * Filter function to reduce a list of roles to a valid list of roles for a nodetype */ var isValidImplicitRole = function (set, role) { var validForNodeType = function (implicitNodeTypeSelector) { return axe.utils.matchesSelector(node, implicitNodeTypeSelector); }; if (role.implicit && role.implicit.some(validForNodeType)) { set.push(role.name); } return set; }; /* * Score a set of roles and aria-attributes by its optimal score * E.g. [{score: 2, name: button}, {score: 1, name: main}] */ var sortRolesByOptimalAriaContext = function (roles, ariaAttributes) { var getScore = function (role) { var allowedAriaAttributes = aria.allowedAttr(role); return allowedAriaAttributes.reduce( function (score, attribute) { return score + (ariaAttributes.indexOf(attribute) > -1 ? 1 : 0); }, 0); }; var scored = roles.map( function (role) { return { score: getScore(role), name: role }; }); var sorted = scored.sort( function (scoredRoleA, scoredRoleB) { return scoredRoleB.score - scoredRoleA.score; }); return sorted.map( function (sortedRole) { return sortedRole.name; }); }; /* * Create a list of { name / implicit } role mappings to filter on */ var roles = Object.keys(aria.lookupTable.role).map( function (role) { var lookup = aria.lookupTable.role[role]; return { name: role, implicit: lookup && lookup.implicit }; }); /* Build a list of valid implicit roles for this node */ var availableImplicitRoles = roles.reduce(isValidImplicitRole, []); if (!availableImplicitRoles.length) { return null; } var nodeAttributes = node.attributes; var ariaAttributes = []; /* Get all aria-attributes defined for this node */ /* Should be a helper function somewhere */ for (var i = 0, j = nodeAttributes.length; i < j; i++) { var attr = nodeAttributes[i]; if (attr.name.match(/^aria-/)) { ariaAttributes.push( attr.name ); } } /* Sort roles by highest score, return the first */ return sortRolesByOptimalAriaContext(availableImplicitRoles, ariaAttributes).shift(); };