From ae85f884367596fe8cbea3ea6d6c2c7b4d1818c0 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Mon, 15 Jun 2020 15:09:17 -0600 Subject: [PATCH 1/5] feat(get-explicit-role): add get-explicit-role and make get-role the semantic role function. deprecate noImplicit option from getRole. --- lib/checks/lists/dlitem-evaluate.js | 6 +- lib/checks/lists/only-dlitems-evaluate.js | 4 +- lib/checks/navigation/region-evaluate.js | 2 +- lib/commons/aria/get-explicit-role.js | 26 ++++ lib/commons/aria/get-role.js | 31 ++-- lib/commons/aria/implicit-role.js | 9 +- lib/commons/aria/index.js | 1 + lib/commons/forms/is-aria-combobox.js | 4 +- lib/commons/forms/is-aria-listbox.js | 4 +- lib/commons/forms/is-aria-range.js | 4 +- lib/commons/forms/is-aria-textbox.js | 4 +- lib/commons/matches/explicit-role.js | 4 +- lib/commons/matches/implicit-role.js | 2 +- lib/commons/text/native-text-alternative.js | 2 +- lib/rules/aria-allowed-role-matches.js | 5 +- lib/rules/aria-form-field-name-matches.js | 4 +- test/commons/aria/get-explicit-role.js | 154 ++++++++++++++++++++ test/commons/aria/get-role.js | 4 +- test/commons/forms/is-aria-combobox.js | 4 + test/commons/forms/is-aria-listbox.js | 5 + test/commons/forms/is-aria-range.js | 5 + test/commons/forms/is-aria-textbox.js | 5 + test/commons/matches/explicit-role.js | 4 +- test/commons/matches/implicit-role.js | 11 +- test/commons/matches/semantic-role.js | 4 +- 25 files changed, 247 insertions(+), 61 deletions(-) create mode 100644 lib/commons/aria/get-explicit-role.js create mode 100644 test/commons/aria/get-explicit-role.js diff --git a/lib/checks/lists/dlitem-evaluate.js b/lib/checks/lists/dlitem-evaluate.js index 78eb3f7148..b30fe5ddd8 100644 --- a/lib/checks/lists/dlitem-evaluate.js +++ b/lib/checks/lists/dlitem-evaluate.js @@ -1,10 +1,10 @@ import { getComposedParent } from '../../commons/dom'; -import { getRole } from '../../commons/aria'; +import { getExplicitRole } from '../../commons/aria'; function dlitemEvaluate(node) { let parent = getComposedParent(node); let parentTagName = parent.nodeName.toUpperCase(); - let parentRole = getRole(parent, { noImplicit: true }); + let parentRole = getExplicitRole(parent); if ( parentTagName === 'DIV' && @@ -12,7 +12,7 @@ function dlitemEvaluate(node) { ) { parent = getComposedParent(parent); parentTagName = parent.nodeName.toUpperCase(); - parentRole = getRole(parent, { noImplicit: true }); + parentRole = getExplicitRole(parent); } // Unlike with UL|OL+LI, DT|DD must be in a DL diff --git a/lib/checks/lists/only-dlitems-evaluate.js b/lib/checks/lists/only-dlitems-evaluate.js index 1c2872b85b..158857b1c3 100644 --- a/lib/checks/lists/only-dlitems-evaluate.js +++ b/lib/checks/lists/only-dlitems-evaluate.js @@ -1,5 +1,5 @@ import { isVisible } from '../../commons/dom'; -import { getRole } from '../../commons/aria'; +import { getRole, getExplicitRole } from '../../commons/aria'; function onlyDlitemsEvaluate(node, options, virtualNode) { const ALLOWED_ROLES = ['definition', 'term', 'list']; @@ -24,7 +24,7 @@ function onlyDlitemsEvaluate(node, options, virtualNode) { const tagName = actualNode.nodeName.toUpperCase(); if (actualNode.nodeType === 1 && isVisible(actualNode, true, false)) { - const explicitRole = getRole(actualNode, { noImplicit: true }); + const explicitRole = getExplicitRole(actualNode); if ((tagName !== 'DT' && tagName !== 'DD') || explicitRole) { if (!ALLOWED_ROLES.includes(explicitRole)) { diff --git a/lib/checks/navigation/region-evaluate.js b/lib/checks/navigation/region-evaluate.js index 45a6d576be..30b9d636e4 100644 --- a/lib/checks/navigation/region-evaluate.js +++ b/lib/checks/navigation/region-evaluate.js @@ -16,7 +16,7 @@ const implicitLandmarks = landmarkRoles // Check if the current element is a landmark function isRegion(virtualNode, options) { const node = virtualNode.actualNode; - const explicitRole = aria.getRole(node, { noImplicit: true }); + const explicitRole = aria.getExplicitRole(node); const ariaLive = (node.getAttribute('aria-live') || '').toLowerCase().trim(); // Ignore content inside of aria-live diff --git a/lib/commons/aria/get-explicit-role.js b/lib/commons/aria/get-explicit-role.js new file mode 100644 index 0000000000..1f23c884cb --- /dev/null +++ b/lib/commons/aria/get-explicit-role.js @@ -0,0 +1,26 @@ +import isValidRole from './is-valid-role'; +import { getNodeFromTree, tokenList } from '../../core/utils'; +import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; + +function getExplicitRole(vNode, { fallback, abstracts, dpub } = {}) { + vNode = vNode instanceof AbstractVirtuaNode ? vNode : getNodeFromTree(vNode); + + if (vNode.props.nodeType !== 1) { + return null; + } + + const roleAttr = (vNode.attr('role') || '').trim().toLowerCase(); + const roleList = fallback ? tokenList(roleAttr) : [roleAttr]; + + // Get the first valid role: + const validRoles = roleList.filter(role => { + if (!dpub && role.substr(0, 4) === 'doc-') { + return false; + } + return isValidRole(role, { allowAbstract: abstracts }); + }); + + return validRoles[0] || null; +} + +export default getExplicitRole; diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index 030ea47683..f22f16e91f 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -1,42 +1,35 @@ -import isValidRole from './is-valid-role'; +import getExplicitRole from './get-explicit-role'; import getImplicitRole from './implicit-role'; +import { getNodeFromTree } from '../../core/utils'; +import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; /** - * Return the accessible role of an element + * Return the semantic role of an element * * @method getRole * @memberof axe.commons.aria * @instance - * @param {Element} node + * @param {Element|VirtualNode} node * @param {Object} options * @param {boolean} options.noImplicit Do not return the implicit role * @param {boolean} options.fallback Allow fallback roles * @param {boolean} options.abstracts Allow role to be abstract * @param {boolean} options.dpub Allow role to be any (valid) doc-* roles * @returns {string|null} Role or null + * + * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead. */ function getRole(node, { noImplicit, fallback, abstracts, dpub } = {}) { - node = node.actualNode || node; - if (node.nodeType !== 1) { + const vNode = + node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); + if (vNode.props.nodeType !== 1) { return null; } - const roleAttr = (node.getAttribute('role') || '').trim().toLowerCase(); - // TODO: es-module-utils.tokenList - const roleList = fallback ? axe.utils.tokenList(roleAttr) : [roleAttr]; - - // Get the first valid role: - const validRoles = roleList.filter(role => { - if (!dpub && role.substr(0, 4) === 'doc-') { - return false; - } - return isValidRole(role, { allowAbstract: abstracts }); - }); - - const explicitRole = validRoles[0]; + const explicitRole = getExplicitRole(vNode, { fallback, abstracts, dpub }); // Get the implicit role, if permitted if (!explicitRole && !noImplicit) { - return getImplicitRole(node); + return getImplicitRole(vNode); } return explicitRole || null; diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index eab0cc29b3..a0480d5af8 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -1,16 +1,19 @@ import lookupTable from './lookup-table'; import { getNodeFromTree } from '../../core/utils'; +import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; /** * Get the implicit role for a given node * @method implicitRole * @memberof axe.commons.aria * @instance - * @param {HTMLElement} node The node to test + * @param {HTMLElement|VirtualNode} node The node to test * @return {Mixed} Either the role or `null` if there is none */ function implicitRole(node) { - const vNode = getNodeFromTree(node); + const vNode = + node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); + node = vNode.actualNode; // this error is only thrown if the virtual tree is not a // complete tree, which only happens in linting and if a @@ -24,7 +27,7 @@ function implicitRole(node) { // until we have proper implicit role lookups for svgs we will // avoid giving them one - if (node.namespaceURI === 'http://www.w3.org/2000/svg') { + if (node && node.namespaceURI === 'http://www.w3.org/2000/svg') { return null; } diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 9c96fa80db..0ae5994acf 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -7,6 +7,7 @@ export { default as allowedAttr } from './allowed-attr'; export { default as arialabelText } from './arialabel-text'; export { default as arialabelledbyText } from './arialabelledby-text'; export { default as getElementUnallowedRoles } from './get-element-unallowed-roles'; +export { default as getExplicitRole } from './get-explicit-role'; export { default as getOwnedVirtual } from './get-owned-virtual'; export { default as getRoleType } from './get-role-type'; export { default as getRole } from './get-role'; diff --git a/lib/commons/forms/is-aria-combobox.js b/lib/commons/forms/is-aria-combobox.js index 1f8cad1cc4..bdcf996dba 100644 --- a/lib/commons/forms/is-aria-combobox.js +++ b/lib/commons/forms/is-aria-combobox.js @@ -1,4 +1,4 @@ -import getRole from '../aria/get-role'; +import getExplicitRole from '../aria/get-explicit-role'; /** * Determines if an element is an aria combobox element @@ -8,7 +8,7 @@ import getRole from '../aria/get-role'; * @returns {Bool} */ function isAriaCombobox(node) { - const role = getRole(node, { noImplicit: true }); + const role = getExplicitRole(node); return role === 'combobox'; } diff --git a/lib/commons/forms/is-aria-listbox.js b/lib/commons/forms/is-aria-listbox.js index 3a073e0cdd..63749e8859 100644 --- a/lib/commons/forms/is-aria-listbox.js +++ b/lib/commons/forms/is-aria-listbox.js @@ -1,4 +1,4 @@ -import getRole from '../aria/get-role'; +import getExplicitRole from '../aria/get-explicit-role'; /** * Determines if an element is an aria listbox element @@ -8,7 +8,7 @@ import getRole from '../aria/get-role'; * @returns {Bool} */ function isAriaListbox(node) { - const role = getRole(node, { noImplicit: true }); + const role = getExplicitRole(node); return role === 'listbox'; } diff --git a/lib/commons/forms/is-aria-range.js b/lib/commons/forms/is-aria-range.js index b3ec2b79f2..bbe9ac9e66 100644 --- a/lib/commons/forms/is-aria-range.js +++ b/lib/commons/forms/is-aria-range.js @@ -1,4 +1,4 @@ -import getRole from '../aria/get-role'; +import getExplicitRole from '../aria/get-explicit-role'; const rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton']; @@ -10,7 +10,7 @@ const rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton']; * @returns {Bool} */ function isAriaRange(node) { - const role = getRole(node, { noImplicit: true }); + const role = getExplicitRole(node); return rangeRoles.includes(role); } diff --git a/lib/commons/forms/is-aria-textbox.js b/lib/commons/forms/is-aria-textbox.js index 3ac14795cd..b86ae2ba9c 100644 --- a/lib/commons/forms/is-aria-textbox.js +++ b/lib/commons/forms/is-aria-textbox.js @@ -1,4 +1,4 @@ -import getRole from '../aria/get-role'; +import getExplicitRole from '../aria/get-explicit-role'; /** * Determines if an element is an aria textbox element @@ -8,7 +8,7 @@ import getRole from '../aria/get-role'; * @returns {Bool} */ function isAriaTextbox(node) { - const role = getRole(node, { noImplicit: true }); + const role = getExplicitRole(node); return role === 'textbox'; } diff --git a/lib/commons/matches/explicit-role.js b/lib/commons/matches/explicit-role.js index 48d32aad7a..9d4ea2eb41 100644 --- a/lib/commons/matches/explicit-role.js +++ b/lib/commons/matches/explicit-role.js @@ -1,5 +1,5 @@ import fromPrimative from './from-primative'; -import getRole from '../aria/get-role'; +import getExplicitRole from '../aria/get-explicit-role'; /** * Check if a virtual node matches an explicit role(s) @@ -18,7 +18,7 @@ import getRole from '../aria/get-role'; * @returns {Boolean} */ function explicitRole(vNode, matcher) { - return fromPrimative(getRole(vNode, { noImplicit: true }), matcher); + return fromPrimative(getExplicitRole(vNode), matcher); } export default explicitRole; diff --git a/lib/commons/matches/implicit-role.js b/lib/commons/matches/implicit-role.js index 2ff949ceae..c53cb999d0 100644 --- a/lib/commons/matches/implicit-role.js +++ b/lib/commons/matches/implicit-role.js @@ -18,7 +18,7 @@ import getImplicitRole from '../aria/implicit-role'; * @returns {Boolean} */ function implicitRole(vNode, matcher) { - return fromPrimative(getImplicitRole(vNode.actualNode), matcher); + return fromPrimative(getImplicitRole(vNode), matcher); } export default implicitRole; diff --git a/lib/commons/text/native-text-alternative.js b/lib/commons/text/native-text-alternative.js index 21bc7b6035..56102f4ce7 100644 --- a/lib/commons/text/native-text-alternative.js +++ b/lib/commons/text/native-text-alternative.js @@ -14,7 +14,7 @@ function nativeTextAlternative(virtualNode, context = {}) { const { actualNode } = virtualNode; if ( actualNode.nodeType !== 1 || - ['presentation', 'none'].includes(getRole(actualNode)) + ['presentation', 'none'].includes(getRole(virtualNode)) ) { return ''; } diff --git a/lib/rules/aria-allowed-role-matches.js b/lib/rules/aria-allowed-role-matches.js index 463e0b8e08..12d0bf119b 100644 --- a/lib/rules/aria-allowed-role-matches.js +++ b/lib/rules/aria-allowed-role-matches.js @@ -1,9 +1,8 @@ -import { getRole } from '../commons/aria'; +import { getExplicitRole } from '../commons/aria'; function ariaAllowedRoleMatches(node) { return ( - getRole(node, { - noImplicit: true, + getExplicitRole(node, { dpub: true, fallback: true }) !== null diff --git a/lib/rules/aria-form-field-name-matches.js b/lib/rules/aria-form-field-name-matches.js index 0440b26bba..bbd2ea4bd3 100644 --- a/lib/rules/aria-form-field-name-matches.js +++ b/lib/rules/aria-form-field-name-matches.js @@ -1,4 +1,4 @@ -import { getRole } from '../commons/aria'; +import { getExplicitRole } from '../commons/aria'; import { querySelectorAll } from '../core/utils'; function ariaFormFieldNameMatches(node, virtualNode) { @@ -8,7 +8,7 @@ function ariaFormFieldNameMatches(node, virtualNode) { * see relevant rule spec for details of 'role(s)' being filtered. */ const nodeName = node.nodeName.toUpperCase(); - const role = getRole(node, { noImplicit: true }); + const role = getExplicitRole(node); /** * Ignore elements from rule -> 'area-alt' diff --git a/test/commons/aria/get-explicit-role.js b/test/commons/aria/get-explicit-role.js new file mode 100644 index 0000000000..057e1b8215 --- /dev/null +++ b/test/commons/aria/get-explicit-role.js @@ -0,0 +1,154 @@ +describe('aria.getExplicitRole', function() { + 'use strict'; + var aria = axe.commons.aria; + var roleDefinitions = aria.lookupTable.role; + var flatTreeSetup = axe.testUtils.flatTreeSetup; + + var orig; + beforeEach(function() { + orig = axe.commons.aria.lookupTable.role; + }); + + afterEach(function() { + axe.commons.aria.lookupTable.role = orig; + }); + + it('returns valid roles', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'button'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode), 'button'); + }); + + it('handles case sensitivity', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'BUTTON'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode), 'button'); + }); + + it('handles whitespacing', function() { + var node = document.createElement('div'); + node.setAttribute('role', ' button '); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode), 'button'); + }); + + it('returns null when there is no role', function() { + var node = document.createElement('div'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode)); + }); + + it('returns the explicit role if it is valid and non-abstract', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'menuitem'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode), 'menuitem'); + }); + + it('ignores fallback roles by default', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'spinbutton button'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode)); + }); + + it('returns null if the node is not an element', function() { + var node = document.createTextNode('foo bar baz'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode)); + }); + + describe('abstracts', function() { + it('ignores abstract roles by default', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'section'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(roleDefinitions.section.type, 'abstract'); + assert.isNull(aria.getExplicitRole(vNode)); + }); + + it('returns abstract roles with `abstracts: true`', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'section'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(roleDefinitions.section.type, 'abstract'); + assert.equal(aria.getExplicitRole(vNode, { abstracts: true }), 'section'); + }); + + it('does not returns abstract roles with `abstracts: false`', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'section'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(roleDefinitions.section.type, 'abstract'); + assert.isNull(aria.getExplicitRole(vNode, { abstracts: false })); + }); + }); + + describe('dpub', function() { + it('ignores DPUB roles by default', function() { + var node = document.createElement('section'); + node.setAttribute('role', 'doc-chapter'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode)); + }); + + it('returns DPUB roles with `dpub: true`', function() { + var node = document.createElement('section'); + node.setAttribute('role', 'doc-chapter'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode, { dpub: true }), 'doc-chapter'); + }); + + it('does not returns DPUB roles with `dpub: false`', function() { + var node = document.createElement('section'); + node.setAttribute('role', 'doc-chapter'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode, { dpub: false })); + }); + }); + + describe('fallback', function() { + it('returns the first valid item in the list', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'link button'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode, { fallback: true }), 'link'); + }); + + it('skips over invalid roles', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'foobar button'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getExplicitRole(vNode, { fallback: true }), 'button'); + }); + + it('returns the null if all roles are invalid and there is no implicit role', function() { + var node = document.createElement('div'); + node.setAttribute('role', 'foo bar baz'); + var vNode = flatTreeSetup(node)[0]; + assert.isNull(aria.getExplicitRole(vNode, { fallback: true })); + }); + + it('respect the `abstracts` option', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'doc-chapter section'); + var vNode = flatTreeSetup(node)[0]; + assert.equal( + aria.getExplicitRole(vNode, { fallback: true, abstracts: true }), + 'section' + ); + }); + + it('respect the `dpub` option', function() { + var node = document.createElement('li'); + node.setAttribute('role', 'doc-chapter section'); + var vNode = flatTreeSetup(node)[0]; + assert.equal( + aria.getExplicitRole(vNode, { fallback: true, dpub: true }), + 'doc-chapter' + ); + }); + }); +}); diff --git a/test/commons/aria/get-role.js b/test/commons/aria/get-role.js index daeb97994a..9d59679dcf 100644 --- a/test/commons/aria/get-role.js +++ b/test/commons/aria/get-role.js @@ -64,8 +64,8 @@ describe('aria.getRole', function() { it('accepts virtualNode objects', function() { var node = document.createElement('div'); node.setAttribute('role', 'button'); - flatTreeSetup(node); - assert.equal(aria.getRole({ actualNode: node }), 'button'); + var vNode = flatTreeSetup(node)[0]; + assert.equal(aria.getRole(vNode), 'button'); }); it('returns null if the node is not an element', function() { diff --git a/test/commons/forms/is-aria-combobox.js b/test/commons/forms/is-aria-combobox.js index ff1a2c6c8e..bf01ed1349 100644 --- a/test/commons/forms/is-aria-combobox.js +++ b/test/commons/forms/is-aria-combobox.js @@ -1,21 +1,25 @@ describe('forms.isAriaCombobox', function() { 'use strict'; var isAriaCombobox = axe.commons.forms.isAriaCombobox; + var flatTreeSetup = axe.testUtils.flatTreeSetup; it('returns true for an element with role=combobox', function() { var node = document.createElement('div'); node.setAttribute('role', 'combobox'); + flatTreeSetup(node); assert.isTrue(isAriaCombobox(node)); }); it('returns false for elements without role', function() { var node = document.createElement('div'); + flatTreeSetup(node); assert.isFalse(isAriaCombobox(node)); }); it('returns false for elements with incorrect role', function() { var node = document.createElement('div'); node.setAttribute('role', 'main'); + flatTreeSetup(node); assert.isFalse(isAriaCombobox(node)); }); }); diff --git a/test/commons/forms/is-aria-listbox.js b/test/commons/forms/is-aria-listbox.js index 1da9fa0e20..00a231ebf4 100644 --- a/test/commons/forms/is-aria-listbox.js +++ b/test/commons/forms/is-aria-listbox.js @@ -1,26 +1,31 @@ describe('forms.isAriaListbox', function() { 'use strict'; var isAriaListbox = axe.commons.forms.isAriaListbox; + var flatTreeSetup = axe.testUtils.flatTreeSetup; it('returns true for an element with role=listbox', function() { var node = document.createElement('div'); node.setAttribute('role', 'listbox'); + flatTreeSetup(node); assert.isTrue(isAriaListbox(node)); }); it('returns false for elements without role', function() { var node = document.createElement('div'); + flatTreeSetup(node); assert.isFalse(isAriaListbox(node)); }); it('returns false for elements with incorrect role', function() { var node = document.createElement('div'); node.setAttribute('role', 'main'); + flatTreeSetup(node); assert.isFalse(isAriaListbox(node)); }); it('returns false for native select', function() { var node = document.createElement('select'); + flatTreeSetup(node); assert.isFalse(isAriaListbox(node)); }); }); diff --git a/test/commons/forms/is-aria-range.js b/test/commons/forms/is-aria-range.js index 1322cd14c1..407714545d 100644 --- a/test/commons/forms/is-aria-range.js +++ b/test/commons/forms/is-aria-range.js @@ -1,6 +1,7 @@ describe('forms.isAriaRange', function() { 'use strict'; var isAriaRange = axe.commons.forms.isAriaRange; + var flatTreeSetup = axe.testUtils.flatTreeSetup; it('returns true for an element with range roles', function() { var rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton']; @@ -8,6 +9,7 @@ describe('forms.isAriaRange', function() { var node = document.createElement('div'); node.setAttribute('role', role); node.setAttribute('aria-valuenow', '0'); + flatTreeSetup(node); assert.isTrue( isAriaRange(node), 'role="' + role + '" is not an aria range role' @@ -17,12 +19,14 @@ describe('forms.isAriaRange', function() { it('returns false for elements without role', function() { var node = document.createElement('div'); + flatTreeSetup(node); assert.isFalse(isAriaRange(node)); }); it('returns false for elements with incorrect role', function() { var node = document.createElement('div'); node.setAttribute('role', 'main'); + flatTreeSetup(node); assert.isFalse(isAriaRange(node)); }); @@ -45,6 +49,7 @@ describe('forms.isAriaRange', function() { if (elm.type) { node.setAttribute('type', elm.type); } + flatTreeSetup(node); assert.isFalse( isAriaRange(node), node.outterHTML + ' is not an aria range element' diff --git a/test/commons/forms/is-aria-textbox.js b/test/commons/forms/is-aria-textbox.js index 047cec9117..fa63108d69 100644 --- a/test/commons/forms/is-aria-textbox.js +++ b/test/commons/forms/is-aria-textbox.js @@ -1,27 +1,32 @@ describe('forms.isAriaTextbox', function() { 'use strict'; var isAriaTextbox = axe.commons.forms.isAriaTextbox; + var flatTreeSetup = axe.testUtils.flatTreeSetup; it('returns true for an element with role=textbox', function() { var node = document.createElement('div'); node.setAttribute('role', 'textbox'); + flatTreeSetup(node); assert.isTrue(isAriaTextbox(node)); }); it('returns false for elements without role', function() { var node = document.createElement('div'); + flatTreeSetup(node); assert.isFalse(isAriaTextbox(node)); }); it('returns false for elements with incorrect role', function() { var node = document.createElement('div'); node.setAttribute('role', 'main'); + flatTreeSetup(node); assert.isFalse(isAriaTextbox(node)); }); it('returns false for native textbox inputs', function() { var node = document.createElement('input'); node.setAttribute('type', 'text'); + flatTreeSetup(node); assert.isFalse(isAriaTextbox(node)); }); }); diff --git a/test/commons/matches/explicit-role.js b/test/commons/matches/explicit-role.js index 09446b54e4..a9c2f3cffd 100644 --- a/test/commons/matches/explicit-role.js +++ b/test/commons/matches/explicit-role.js @@ -27,9 +27,7 @@ describe('matches.explicitRole', function() { assert.isFalse(explicitRole(virtualNode, 'listitem')); }); - // TODO: will only work when get-role works exclusively with virtual - // nodes - it.skip('works with SerialVirtualNode', function() { + it('works with SerialVirtualNode', function() { var serialNode = new axe.SerialVirtualNode({ nodeName: 'span', attributes: { diff --git a/test/commons/matches/implicit-role.js b/test/commons/matches/implicit-role.js index a5a5151900..7487375f41 100644 --- a/test/commons/matches/implicit-role.js +++ b/test/commons/matches/implicit-role.js @@ -29,15 +29,10 @@ describe('matches.implicitRole', function() { assert.isFalse(implicitRole(virtualNode, 'menuitem')); }); - // TODO: will only work when get-role works exclusively with virtual - // nodes - it.skip('works with SerialVirtualNode', function() { + it('works with SerialVirtualNode', function() { var serialNode = new axe.SerialVirtualNode({ - nodeName: 'span', - attributes: { - role: 'textbox' - } + nodeName: 'li' }); - assert.isTrue(implicitRole(serialNode, 'textbox')); + assert.isTrue(implicitRole(serialNode, 'listitem')); }); }); diff --git a/test/commons/matches/semantic-role.js b/test/commons/matches/semantic-role.js index e3b6b57005..bd98732bc2 100644 --- a/test/commons/matches/semantic-role.js +++ b/test/commons/matches/semantic-role.js @@ -27,9 +27,7 @@ describe('matches.semanticRole', function() { assert.isFalse(semanticRole(virtualNode, 'textbox')); }); - // TODO: will only work when get-role works exclusively with virtual - // nodes - it.skip('works with SerialVirtualNode', function() { + it('works with SerialVirtualNode', function() { var serialNode = new axe.SerialVirtualNode({ nodeName: 'span', attributes: { From 2c41787a8e8c45a1114502fdfc13c0384c003122 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Mon, 15 Jun 2020 15:16:04 -0600 Subject: [PATCH 2/5] fix test --- test/rule-matches/aria-allowed-role-matches.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/rule-matches/aria-allowed-role-matches.js b/test/rule-matches/aria-allowed-role-matches.js index cb8d383122..f5936e465b 100644 --- a/test/rule-matches/aria-allowed-role-matches.js +++ b/test/rule-matches/aria-allowed-role-matches.js @@ -2,6 +2,7 @@ describe('aria-allowed-role-matches', function() { 'use strict'; var fixture = document.getElementById('fixture'); + var flatTreeSetup = axe.testUtils.flatTreeSetup; var rule; beforeEach(function() { @@ -23,6 +24,7 @@ describe('aria-allowed-role-matches', function() { node.setAttribute('role', 'invalid-role'); node.href = '\\example.com'; fixture.appendChild(node); + flatTreeSetup(node); assert.isFalse(rule.matches(node)); }); @@ -32,6 +34,7 @@ describe('aria-allowed-role-matches', function() { node.setAttribute('role', 'textbox'); node.href = '\\example.com'; fixture.appendChild(node); + flatTreeSetup(node); assert.isTrue(rule.matches(node)); }); @@ -40,6 +43,7 @@ describe('aria-allowed-role-matches', function() { node.setAttribute('role', 'listbox'); node.href = '\\example.com'; fixture.appendChild(node); + flatTreeSetup(node); assert.isTrue(rule.matches(node)); }); }); From a5e40cd8ee45aa4dbb80f82c91ff1153d5fd445e Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Tue, 16 Jun 2020 07:58:01 -0600 Subject: [PATCH 3/5] Update lib/commons/aria/get-explicit-role.js Co-authored-by: Wilco Fiers --- lib/commons/aria/get-explicit-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commons/aria/get-explicit-role.js b/lib/commons/aria/get-explicit-role.js index 1f23c884cb..a396c52528 100644 --- a/lib/commons/aria/get-explicit-role.js +++ b/lib/commons/aria/get-explicit-role.js @@ -13,7 +13,7 @@ function getExplicitRole(vNode, { fallback, abstracts, dpub } = {}) { const roleList = fallback ? tokenList(roleAttr) : [roleAttr]; // Get the first valid role: - const validRoles = roleList.filter(role => { + const firstValidRole = roleList.find(role => { if (!dpub && role.substr(0, 4) === 'doc-') { return false; } From be105e26d5b7f587e30d517e79a3962f5dac55ee Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Tue, 16 Jun 2020 07:58:07 -0600 Subject: [PATCH 4/5] Update lib/commons/aria/get-explicit-role.js Co-authored-by: Wilco Fiers --- lib/commons/aria/get-explicit-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commons/aria/get-explicit-role.js b/lib/commons/aria/get-explicit-role.js index a396c52528..cb18b034ee 100644 --- a/lib/commons/aria/get-explicit-role.js +++ b/lib/commons/aria/get-explicit-role.js @@ -20,7 +20,7 @@ function getExplicitRole(vNode, { fallback, abstracts, dpub } = {}) { return isValidRole(role, { allowAbstract: abstracts }); }); - return validRoles[0] || null; + return firstValidRole || null; } export default getExplicitRole; From cbcba4c81c7f6838a2cc7b7e31e445521a2769a1 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Tue, 16 Jun 2020 07:58:37 -0600 Subject: [PATCH 5/5] Update lib/commons/aria/get-role.js Co-authored-by: Wilco Fiers --- lib/commons/aria/get-role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index f22f16e91f..b729d65932 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -11,7 +11,7 @@ import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-no * @instance * @param {Element|VirtualNode} node * @param {Object} options - * @param {boolean} options.noImplicit Do not return the implicit role + * @param {boolean} options.noImplicit Do not return the implicit role // @deprecated * @param {boolean} options.fallback Allow fallback roles * @param {boolean} options.abstracts Allow role to be abstract * @param {boolean} options.dpub Allow role to be any (valid) doc-* roles