From 4685270af0d1f737d38a0c06b59137c967679227 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Fri, 3 Jun 2022 08:26:28 -0600 Subject: [PATCH] fix(aria-required-parent): allow nested group and presentational roles (#3492) * fix(aria-required-parent): allow nested group and presentational roles * update * remove comments --- .../aria/aria-required-parent-evaluate.js | 25 +++++++++---------- test/checks/aria/required-parent.js | 11 ++++++++ .../aria-required-parent.html | 12 +++++++++ .../aria-required-parent.json | 3 ++- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/checks/aria/aria-required-parent-evaluate.js b/lib/checks/aria/aria-required-parent-evaluate.js index d29e0789a0..c985cedc27 100644 --- a/lib/checks/aria/aria-required-parent-evaluate.js +++ b/lib/checks/aria/aria-required-parent-evaluate.js @@ -18,31 +18,30 @@ function getMissingContext( return null; } + const allowsGroup = reqContext.includes('group'); let vNode = includeElement ? virtualNode : virtualNode.parent; + while (vNode) { - const parentRole = getRole(vNode); + const role = getRole(vNode, { noPresentational: true }); - // if parent node has role=group and role=group is an allowed - // context, check next parent - if (reqContext.includes('group') && parentRole === 'group') { + // if parent node has no role or is presentational, or if role + // allows group, we keep parsing the parent tree. + // this means intermediate roles between a required parent and + // child will fail the check + if (!role) { + vNode = vNode.parent; + } else if (role === 'group' && allowsGroup) { // Allow the own role; i.e. tree > treeitem > group > treeitem if (ownGroupRoles.includes(explicitRole)) { reqContext.push(explicitRole); } reqContext = reqContext.filter(r => r !== 'group'); vNode = vNode.parent; - continue; - } - - // if parent node has a role that is not the required role and not - // presentational we will fail the check - if (reqContext.includes(parentRole)) { + } else if (reqContext.includes(role)) { return null; - } else if (parentRole && !['presentation', 'none'].includes(parentRole)) { + } else { return reqContext; } - - vNode = vNode.parent; } return reqContext; diff --git a/test/checks/aria/required-parent.js b/test/checks/aria/required-parent.js index 1933be5992..adbb3ea33a 100644 --- a/test/checks/aria/required-parent.js +++ b/test/checks/aria/required-parent.js @@ -243,6 +243,17 @@ describe('aria-required-parent', function() { ); }); + it('should pass for multiple group and presentational roles', function() { + var params = checkSetup( + '
Nothing here.
' + ); + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-required-parent') + .apply(checkContext, params) + ); + }); + (shadowSupported ? it : xit)( 'should pass when required parent is present across shadow boundary', function() { diff --git a/test/integration/rules/aria-required-parent/aria-required-parent.html b/test/integration/rules/aria-required-parent/aria-required-parent.html index be9ef5a4d9..d28fab7d80 100644 --- a/test/integration/rules/aria-required-parent/aria-required-parent.html +++ b/test/integration/rules/aria-required-parent/aria-required-parent.html @@ -64,3 +64,15 @@
option
+ +
+ +
diff --git a/test/integration/rules/aria-required-parent/aria-required-parent.json b/test/integration/rules/aria-required-parent/aria-required-parent.json index 0092d17059..aa833134c7 100644 --- a/test/integration/rules/aria-required-parent/aria-required-parent.json +++ b/test/integration/rules/aria-required-parent/aria-required-parent.json @@ -23,6 +23,7 @@ ["#pass10"], ["#pass11"], ["#pass12"], - ["#pass13"] + ["#pass13"], + ["#pass14"] ] }