From cb3fbab18fa13caded8240e9275c363dd6875414 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 17 Jul 2019 16:09:18 -0600 Subject: [PATCH 01/10] fix(aria-required-children): allow combobox to own a searchbox --- lib/checks/aria/required-children.js | 9 +++++---- test/checks/aria/required-children.js | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index afbf76f822..5e6c99c8d9 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -1,5 +1,6 @@ const requiredOwned = axe.commons.aria.requiredOwned; const implicitNodes = axe.commons.aria.implicitNodes; +const getRole = axe.commons.aria.getRole; const matchesSelector = axe.utils.matchesSelector; const idrefs = axe.commons.dom.idrefs; const reviewEmpty = @@ -62,13 +63,13 @@ function missingRequiredChildren(node, childRoles, all, role) { // combobox exceptions if (role === 'combobox') { - // remove 'textbox' from missing roles if combobox is a native text-type input + // remove 'textbox' from missing roles if combobox is a native text-type input or owns a 'searchbox' var textboxIndex = missing.indexOf('textbox'); - var textTypeInputs = ['text', 'search', 'email', 'url', 'tel']; if ( textboxIndex >= 0 && - node.nodeName.toUpperCase() === 'INPUT' && - textTypeInputs.includes(node.type) + (getRole(node) === 'textbox' || + owns(node, virtualNode, 'searchbox') || + ariaOwns(ownedElements, 'searchbox')) ) { missing.splice(textboxIndex, 1); } diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index cc6bfe00ad..ebceadeced 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -287,6 +287,15 @@ describe('aria-required-children', function() { ); }); + it('should pass role comboxbox when child is native "search" input type', function() { + var params = checkSetup( + '

Textbox

' + ); + assert.isTrue( + checks['aria-required-children'].evaluate.apply(checkContext, params) + ); + }); + describe('options', function() { it('should return undefined instead of false when the role is in options.reviewEmpty', function() { var params = checkSetup('
'); From 46c711b74cac261e4086dcd91674f53555069ef8 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 23 Jul 2019 11:44:32 -0600 Subject: [PATCH 02/10] add subclass and superclass roles --- lib/checks/aria/required-children.js | 14 +++-- lib/commons/aria/index.js | 2 + lib/commons/aria/subclass-implicit-nodes.js | 30 ++++++++++ test/commons/aria/subclass-implicit-nodes.js | 63 ++++++++++++++++++++ 4 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 lib/commons/aria/subclass-implicit-nodes.js create mode 100644 test/commons/aria/subclass-implicit-nodes.js diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 5e6c99c8d9..ad3d8d896e 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -1,6 +1,6 @@ const requiredOwned = axe.commons.aria.requiredOwned; const implicitNodes = axe.commons.aria.implicitNodes; -const getRole = axe.commons.aria.getRole; +const subclassImplicitNodes = axe.commons.aria.subclassImplicitNodes; const matchesSelector = axe.utils.matchesSelector; const idrefs = axe.commons.dom.idrefs; const reviewEmpty = @@ -11,11 +11,15 @@ function owns(node, virtualTree, role, ariaOwned) { return false; } var implicit = implicitNodes(role), + subclass = subclassImplicitNodes(role), selector = ['[role="' + role + '"]']; if (implicit) { selector = selector.concat(implicit); } + if (subclass) { + selector = selector.concat(subclass); + } selector = selector.join(','); return ariaOwned @@ -63,13 +67,13 @@ function missingRequiredChildren(node, childRoles, all, role) { // combobox exceptions if (role === 'combobox') { - // remove 'textbox' from missing roles if combobox is a native text-type input or owns a 'searchbox' + // remove 'textbox' from missing roles if combobox is a native text-type input var textboxIndex = missing.indexOf('textbox'); + var textTypeInputs = ['text', 'search', 'email', 'url', 'tel']; if ( textboxIndex >= 0 && - (getRole(node) === 'textbox' || - owns(node, virtualNode, 'searchbox') || - ariaOwns(ownedElements, 'searchbox')) + node.nodeName.toUpperCase() === 'INPUT' && + textTypeInputs.includes(node.type) ) { missing.splice(textboxIndex, 1); } diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 9e39efe0b9..bdf2366cfb 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -1732,6 +1732,7 @@ lookupTable.role = { nameFrom: ['author'], context: null, implicit: ['input[type="search"]'], + superclassRole: 'textbox', unsupported: false, allowedElements: { nodeName: 'input', @@ -1974,6 +1975,7 @@ lookupTable.role = { 'input:not([type])', 'textarea' ], + subclassRoles: ['searchbox'], unsupported: false }, timer: { diff --git a/lib/commons/aria/subclass-implicit-nodes.js b/lib/commons/aria/subclass-implicit-nodes.js new file mode 100644 index 0000000000..7e0bfa16de --- /dev/null +++ b/lib/commons/aria/subclass-implicit-nodes.js @@ -0,0 +1,30 @@ +/* global aria */ + +/** + * Get a list of CSS selectors of subclass nodes that have an implicit role + * @method subclassImplicitNodes + * @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.subclassImplicitNodes = function(role) { + const roles = aria.lookupTable.role[role]; + let subclassRoles = []; + + if (roles && roles.subclassRoles) { + roles.subclassRoles.forEach(subclassRole => { + const implicit = aria.implicitNodes(subclassRole); + + if (implicit) { + subclassRoles = subclassRoles.concat(implicit); + } + }); + } + + if (subclassRoles.length > 0) { + return subclassRoles; + } + + return null; +}; diff --git a/test/commons/aria/subclass-implicit-nodes.js b/test/commons/aria/subclass-implicit-nodes.js new file mode 100644 index 0000000000..5694b44d57 --- /dev/null +++ b/test/commons/aria/subclass-implicit-nodes.js @@ -0,0 +1,63 @@ +describe('aria.subclassImplicitNodes', function() { + 'use strict'; + + var orig = axe.commons.aria.lookupTable.role; + afterEach(function() { + axe.commons.aria.lookupTable.role = orig; + }); + + it('should return an array of implicit CSS selectors', function() { + axe.commons.aria.lookupTable.role = { + planes: { + subclassRoles: ['trains'] + }, + trains: { + implicit: ['[class=trains]'] + } + }; + + var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); + assert.deepEqual(subclassRoles, ['[class=trains]']); + }); + + it('should return CSS selectors for all subclass roles', function() { + axe.commons.aria.lookupTable.role = { + planes: { + subclassRoles: ['trains', 'automobiles'] + }, + trains: { + implicit: ['[class=trains]'] + }, + automobiles: { + implicit: ['[class=automobiles]'] + } + }; + + var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); + assert.deepEqual(subclassRoles, ['[class=trains]', '[class=automobiles]']); + }); + + it("should not add roles that don't have implicit nodes", function() { + axe.commons.aria.lookupTable.role = { + planes: { + subclassRoles: ['trains', 'automobiles'] + }, + trains: {}, + automobiles: { + implicit: ['[class=automobiles]'] + } + }; + + var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); + assert.deepEqual(subclassRoles, ['[class=automobiles]']); + }); + + it('should return null if the role does not have a subclass role', function() { + axe.commons.aria.lookupTable.role = { + planes: {} + }; + + var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); + assert.isNull(subclassRoles); + }); +}); From e5b72224debbe37d479fd5cd77d237295a3be848 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 31 Jul 2019 15:21:06 -0600 Subject: [PATCH 03/10] determine subclassRole from superclassRole --- lib/commons/aria/index.js | 1 - lib/commons/aria/subclass-implicit-nodes.js | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index bdf2366cfb..539b52d64b 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -1975,7 +1975,6 @@ lookupTable.role = { 'input:not([type])', 'textarea' ], - subclassRoles: ['searchbox'], unsupported: false }, timer: { diff --git a/lib/commons/aria/subclass-implicit-nodes.js b/lib/commons/aria/subclass-implicit-nodes.js index 7e0bfa16de..63482d6792 100644 --- a/lib/commons/aria/subclass-implicit-nodes.js +++ b/lib/commons/aria/subclass-implicit-nodes.js @@ -9,17 +9,16 @@ * @return {Mixed} Either an Array of CSS selectors or `null` if there are none */ aria.subclassImplicitNodes = function(role) { - const roles = aria.lookupTable.role[role]; + const roles = aria.lookupTable.role; let subclassRoles = []; - if (roles && roles.subclassRoles) { - roles.subclassRoles.forEach(subclassRole => { - const implicit = aria.implicitNodes(subclassRole); - + for (const roleName in roles) { + if (roles[roleName].superclassRole === role) { + const implicit = aria.implicitNodes(roleName); if (implicit) { subclassRoles = subclassRoles.concat(implicit); } - }); + } } if (subclassRoles.length > 0) { From 97b73a30841f6ab0e050c8c203b3abec28a8ac9b Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 31 Jul 2019 15:30:22 -0600 Subject: [PATCH 04/10] fix tests --- test/commons/aria/subclass-implicit-nodes.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/commons/aria/subclass-implicit-nodes.js b/test/commons/aria/subclass-implicit-nodes.js index 5694b44d57..c8ac814666 100644 --- a/test/commons/aria/subclass-implicit-nodes.js +++ b/test/commons/aria/subclass-implicit-nodes.js @@ -8,10 +8,8 @@ describe('aria.subclassImplicitNodes', function() { it('should return an array of implicit CSS selectors', function() { axe.commons.aria.lookupTable.role = { - planes: { - subclassRoles: ['trains'] - }, trains: { + superclassRole: 'planes', implicit: ['[class=trains]'] } }; @@ -22,13 +20,12 @@ describe('aria.subclassImplicitNodes', function() { it('should return CSS selectors for all subclass roles', function() { axe.commons.aria.lookupTable.role = { - planes: { - subclassRoles: ['trains', 'automobiles'] - }, trains: { + superclassRole: 'planes', implicit: ['[class=trains]'] }, automobiles: { + superclassRole: 'planes', implicit: ['[class=automobiles]'] } }; @@ -39,11 +36,11 @@ describe('aria.subclassImplicitNodes', function() { it("should not add roles that don't have implicit nodes", function() { axe.commons.aria.lookupTable.role = { - planes: { - subclassRoles: ['trains', 'automobiles'] + trains: { + superclassRole: 'planes' }, - trains: {}, automobiles: { + superclassRole: 'planes', implicit: ['[class=automobiles]'] } }; From 80acb7bdc6420b4441e8802a6b857b6ceeaddbab Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 6 Aug 2019 13:34:14 -0600 Subject: [PATCH 05/10] get role inheritance --- lib/checks/aria/required-children.js | 23 +++---- ...mplicit-nodes.js => getRoleInheritance.js} | 17 ++---- test/checks/aria/required-children.js | 9 +++ test/commons/aria/getRoleInheritance.js | 56 +++++++++++++++++ test/commons/aria/subclass-implicit-nodes.js | 60 ------------------- 5 files changed, 83 insertions(+), 82 deletions(-) rename lib/commons/aria/{subclass-implicit-nodes.js => getRoleInheritance.js} (56%) create mode 100644 test/commons/aria/getRoleInheritance.js delete mode 100644 test/commons/aria/subclass-implicit-nodes.js diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index ad3d8d896e..8f5b1f8449 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -1,6 +1,6 @@ const requiredOwned = axe.commons.aria.requiredOwned; const implicitNodes = axe.commons.aria.implicitNodes; -const subclassImplicitNodes = axe.commons.aria.subclassImplicitNodes; +const getRoleInheritance = axe.commons.aria.getRoleInheritance; const matchesSelector = axe.utils.matchesSelector; const idrefs = axe.commons.dom.idrefs; const reviewEmpty = @@ -10,16 +10,19 @@ function owns(node, virtualTree, role, ariaOwned) { if (node === null) { return false; } - var implicit = implicitNodes(role), - subclass = subclassImplicitNodes(role), - selector = ['[role="' + role + '"]']; - if (implicit) { - selector = selector.concat(implicit); - } - if (subclass) { - selector = selector.concat(subclass); - } + const roles = getRoleInheritance(role); + let selector = []; + roles.forEach(roleName => { + const implicit = implicitNodes(roleName).map( + implicitSelector => implicitSelector + ':not([role])' + ); + selector.push('[role="' + roleName + '"]'); + + if (implicit) { + selector = selector.concat(implicit); + } + }); selector = selector.join(','); return ariaOwned diff --git a/lib/commons/aria/subclass-implicit-nodes.js b/lib/commons/aria/getRoleInheritance.js similarity index 56% rename from lib/commons/aria/subclass-implicit-nodes.js rename to lib/commons/aria/getRoleInheritance.js index 63482d6792..af40f3da77 100644 --- a/lib/commons/aria/subclass-implicit-nodes.js +++ b/lib/commons/aria/getRoleInheritance.js @@ -2,28 +2,21 @@ /** * Get a list of CSS selectors of subclass nodes that have an implicit role - * @method subclassImplicitNodes + * @method getRoleInheritance * @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.subclassImplicitNodes = function(role) { +aria.getRoleInheritance = function(role) { const roles = aria.lookupTable.role; - let subclassRoles = []; + let roleInheritance = [role]; for (const roleName in roles) { if (roles[roleName].superclassRole === role) { - const implicit = aria.implicitNodes(roleName); - if (implicit) { - subclassRoles = subclassRoles.concat(implicit); - } + roleInheritance = roleInheritance.concat(aria.getRoleInheritance(roleName)); } } - if (subclassRoles.length > 0) { - return subclassRoles; - } - - return null; + return roleInheritance; }; diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index ebceadeced..959d561a4f 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -296,6 +296,15 @@ describe('aria-required-children', function() { ); }); + it('should not accept implicit nodes with a different role', function() { + var params = checkSetup( + '

Textbox

' + ); + assert.isFalse( + checks['aria-required-children'].evaluate.apply(checkContext, params) + ); + }); + describe('options', function() { it('should return undefined instead of false when the role is in options.reviewEmpty', function() { var params = checkSetup('
'); diff --git a/test/commons/aria/getRoleInheritance.js b/test/commons/aria/getRoleInheritance.js new file mode 100644 index 0000000000..bae19152eb --- /dev/null +++ b/test/commons/aria/getRoleInheritance.js @@ -0,0 +1,56 @@ +describe('aria.getRoleInheritance', function() { + 'use strict'; + + var orig = axe.commons.aria.lookupTable.role; + afterEach(function() { + axe.commons.aria.lookupTable.role = orig; + }); + + it('should return an array with the role if there is no inheritance', function() { + axe.commons.aria.lookupTable.role = { + trains: {} + }; + + var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); + assert.deepEqual(subclassRoles, ['planes']); + }); + + it('should return an array of role names that inherit from the role', function() { + axe.commons.aria.lookupTable.role = { + trains: { + superclassRole: 'planes' + } + }; + + var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); + assert.deepEqual(subclassRoles, ['planes', 'trains']); + }); + + it('should return all roles that inherit from the role', function() { + axe.commons.aria.lookupTable.role = { + trains: { + superclassRole: 'planes' + }, + automobiles: { + superclassRole: 'planes' + } + }; + + var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); + assert.deepEqual(subclassRoles, ['planes', 'trains', 'automobiles']); + }); + + it('should return roles that are grandchildren of the role', function() { + axe.commons.aria.lookupTable.role = { + trains: { + superclassRole: 'planes' + }, + automobiles: { + superclassRole: 'trains' + } + }; + + var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); + assert.deepEqual(subclassRoles, ['planes', 'trains', 'automobiles']); + }); +}); diff --git a/test/commons/aria/subclass-implicit-nodes.js b/test/commons/aria/subclass-implicit-nodes.js deleted file mode 100644 index c8ac814666..0000000000 --- a/test/commons/aria/subclass-implicit-nodes.js +++ /dev/null @@ -1,60 +0,0 @@ -describe('aria.subclassImplicitNodes', function() { - 'use strict'; - - var orig = axe.commons.aria.lookupTable.role; - afterEach(function() { - axe.commons.aria.lookupTable.role = orig; - }); - - it('should return an array of implicit CSS selectors', function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes', - implicit: ['[class=trains]'] - } - }; - - var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); - assert.deepEqual(subclassRoles, ['[class=trains]']); - }); - - it('should return CSS selectors for all subclass roles', function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes', - implicit: ['[class=trains]'] - }, - automobiles: { - superclassRole: 'planes', - implicit: ['[class=automobiles]'] - } - }; - - var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); - assert.deepEqual(subclassRoles, ['[class=trains]', '[class=automobiles]']); - }); - - it("should not add roles that don't have implicit nodes", function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes' - }, - automobiles: { - superclassRole: 'planes', - implicit: ['[class=automobiles]'] - } - }; - - var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); - assert.deepEqual(subclassRoles, ['[class=automobiles]']); - }); - - it('should return null if the role does not have a subclass role', function() { - axe.commons.aria.lookupTable.role = { - planes: {} - }; - - var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes'); - assert.isNull(subclassRoles); - }); -}); From 69491a93d12f8187d6d2ace1a5b8fefafa0521f5 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 6 Aug 2019 13:36:05 -0600 Subject: [PATCH 06/10] fix map --- lib/checks/aria/required-children.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 8f5b1f8449..0d9c3a2905 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -14,13 +14,13 @@ function owns(node, virtualTree, role, ariaOwned) { const roles = getRoleInheritance(role); let selector = []; roles.forEach(roleName => { - const implicit = implicitNodes(roleName).map( - implicitSelector => implicitSelector + ':not([role])' - ); + const implicit = implicitNodes(roleName); selector.push('[role="' + roleName + '"]'); if (implicit) { - selector = selector.concat(implicit); + selector = selector.concat( + implicit.map(implicitSelector => implicitSelector + ':not([role])') + ); } }); From cbb1d5240a4930afb53e7df0e3f2d92b10c3c047 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 6 Aug 2019 13:38:42 -0600 Subject: [PATCH 07/10] fix comment --- lib/commons/aria/getRoleInheritance.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/commons/aria/getRoleInheritance.js b/lib/commons/aria/getRoleInheritance.js index af40f3da77..d2b25746f4 100644 --- a/lib/commons/aria/getRoleInheritance.js +++ b/lib/commons/aria/getRoleInheritance.js @@ -1,12 +1,12 @@ /* global aria */ /** - * Get a list of CSS selectors of subclass nodes that have an implicit role + * Recursively get a list of role names that inherit from the given role. * @method getRoleInheritance * @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 + * @return {String[]} An array of role names including the given role */ aria.getRoleInheritance = function(role) { const roles = aria.lookupTable.role; @@ -14,7 +14,9 @@ aria.getRoleInheritance = function(role) { for (const roleName in roles) { if (roles[roleName].superclassRole === role) { - roleInheritance = roleInheritance.concat(aria.getRoleInheritance(roleName)); + roleInheritance = roleInheritance.concat( + aria.getRoleInheritance(roleName) + ); } } From c2dc3c1ea8131eb85df5027c21438a76d6cd6ae6 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Thu, 15 Aug 2019 16:12:34 -0600 Subject: [PATCH 08/10] revert superclass role changes --- lib/checks/aria/required-children.js | 27 +++++------- lib/commons/aria/getRoleInheritance.js | 24 ----------- lib/commons/aria/index.js | 1 - test/commons/aria/getRoleInheritance.js | 56 ------------------------- 4 files changed, 10 insertions(+), 98 deletions(-) delete mode 100644 lib/commons/aria/getRoleInheritance.js delete mode 100644 test/commons/aria/getRoleInheritance.js diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 0d9c3a2905..5e6c99c8d9 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -1,6 +1,6 @@ const requiredOwned = axe.commons.aria.requiredOwned; const implicitNodes = axe.commons.aria.implicitNodes; -const getRoleInheritance = axe.commons.aria.getRoleInheritance; +const getRole = axe.commons.aria.getRole; const matchesSelector = axe.utils.matchesSelector; const idrefs = axe.commons.dom.idrefs; const reviewEmpty = @@ -10,19 +10,12 @@ function owns(node, virtualTree, role, ariaOwned) { if (node === null) { return false; } + var implicit = implicitNodes(role), + selector = ['[role="' + role + '"]']; - const roles = getRoleInheritance(role); - let selector = []; - roles.forEach(roleName => { - const implicit = implicitNodes(roleName); - selector.push('[role="' + roleName + '"]'); - - if (implicit) { - selector = selector.concat( - implicit.map(implicitSelector => implicitSelector + ':not([role])') - ); - } - }); + if (implicit) { + selector = selector.concat(implicit); + } selector = selector.join(','); return ariaOwned @@ -70,13 +63,13 @@ function missingRequiredChildren(node, childRoles, all, role) { // combobox exceptions if (role === 'combobox') { - // remove 'textbox' from missing roles if combobox is a native text-type input + // remove 'textbox' from missing roles if combobox is a native text-type input or owns a 'searchbox' var textboxIndex = missing.indexOf('textbox'); - var textTypeInputs = ['text', 'search', 'email', 'url', 'tel']; if ( textboxIndex >= 0 && - node.nodeName.toUpperCase() === 'INPUT' && - textTypeInputs.includes(node.type) + (getRole(node) === 'textbox' || + owns(node, virtualNode, 'searchbox') || + ariaOwns(ownedElements, 'searchbox')) ) { missing.splice(textboxIndex, 1); } diff --git a/lib/commons/aria/getRoleInheritance.js b/lib/commons/aria/getRoleInheritance.js deleted file mode 100644 index d2b25746f4..0000000000 --- a/lib/commons/aria/getRoleInheritance.js +++ /dev/null @@ -1,24 +0,0 @@ -/* global aria */ - -/** - * Recursively get a list of role names that inherit from the given role. - * @method getRoleInheritance - * @memberof axe.commons.aria - * @instance - * @param {String} role The role to check - * @return {String[]} An array of role names including the given role - */ -aria.getRoleInheritance = function(role) { - const roles = aria.lookupTable.role; - let roleInheritance = [role]; - - for (const roleName in roles) { - if (roles[roleName].superclassRole === role) { - roleInheritance = roleInheritance.concat( - aria.getRoleInheritance(roleName) - ); - } - } - - return roleInheritance; -}; diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 539b52d64b..9e39efe0b9 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -1732,7 +1732,6 @@ lookupTable.role = { nameFrom: ['author'], context: null, implicit: ['input[type="search"]'], - superclassRole: 'textbox', unsupported: false, allowedElements: { nodeName: 'input', diff --git a/test/commons/aria/getRoleInheritance.js b/test/commons/aria/getRoleInheritance.js deleted file mode 100644 index bae19152eb..0000000000 --- a/test/commons/aria/getRoleInheritance.js +++ /dev/null @@ -1,56 +0,0 @@ -describe('aria.getRoleInheritance', function() { - 'use strict'; - - var orig = axe.commons.aria.lookupTable.role; - afterEach(function() { - axe.commons.aria.lookupTable.role = orig; - }); - - it('should return an array with the role if there is no inheritance', function() { - axe.commons.aria.lookupTable.role = { - trains: {} - }; - - var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); - assert.deepEqual(subclassRoles, ['planes']); - }); - - it('should return an array of role names that inherit from the role', function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes' - } - }; - - var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); - assert.deepEqual(subclassRoles, ['planes', 'trains']); - }); - - it('should return all roles that inherit from the role', function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes' - }, - automobiles: { - superclassRole: 'planes' - } - }; - - var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); - assert.deepEqual(subclassRoles, ['planes', 'trains', 'automobiles']); - }); - - it('should return roles that are grandchildren of the role', function() { - axe.commons.aria.lookupTable.role = { - trains: { - superclassRole: 'planes' - }, - automobiles: { - superclassRole: 'trains' - } - }; - - var subclassRoles = axe.commons.aria.getRoleInheritance('planes'); - assert.deepEqual(subclassRoles, ['planes', 'trains', 'automobiles']); - }); -}); From 49e6e3a3665bbf180b12728a08aae6e11aa97286 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Thu, 15 Aug 2019 16:25:56 -0600 Subject: [PATCH 09/10] fix test --- lib/checks/aria/required-children.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 5e6c99c8d9..5718e69254 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -1,6 +1,5 @@ const requiredOwned = axe.commons.aria.requiredOwned; const implicitNodes = axe.commons.aria.implicitNodes; -const getRole = axe.commons.aria.getRole; const matchesSelector = axe.utils.matchesSelector; const idrefs = axe.commons.dom.idrefs; const reviewEmpty = @@ -10,7 +9,9 @@ function owns(node, virtualTree, role, ariaOwned) { if (node === null) { return false; } - var implicit = implicitNodes(role), + var implicit = implicitNodes(role).map( + implicitSelector => implicitSelector + ':not([role])' + ), selector = ['[role="' + role + '"]']; if (implicit) { @@ -65,10 +66,12 @@ function missingRequiredChildren(node, childRoles, all, role) { if (role === 'combobox') { // remove 'textbox' from missing roles if combobox is a native text-type input or owns a 'searchbox' var textboxIndex = missing.indexOf('textbox'); + var textTypeInputs = ['text', 'search', 'email', 'url', 'tel']; if ( - textboxIndex >= 0 && - (getRole(node) === 'textbox' || - owns(node, virtualNode, 'searchbox') || + (textboxIndex >= 0 && + (node.nodeName.toUpperCase() === 'INPUT' && + textTypeInputs.includes(node.type))) || + (owns(node, virtualNode, 'searchbox') || ariaOwns(ownedElements, 'searchbox')) ) { missing.splice(textboxIndex, 1); From 1cea55cfa055abf04637860befb83280a75f3a5d Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Fri, 16 Aug 2019 08:54:27 -0600 Subject: [PATCH 10/10] fix broken code --- lib/checks/aria/required-children.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 5718e69254..8183bc1d4f 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -9,13 +9,13 @@ function owns(node, virtualTree, role, ariaOwned) { if (node === null) { return false; } - var implicit = implicitNodes(role).map( - implicitSelector => implicitSelector + ':not([role])' - ), + var implicit = implicitNodes(role), selector = ['[role="' + role + '"]']; if (implicit) { - selector = selector.concat(implicit); + selector = selector.concat( + implicit.map(implicitSelector => implicitSelector + ':not([role])') + ); } selector = selector.join(',');