From 63b6c7b04c6837a313251f1621be6032933c8289 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Wed, 21 Jul 2021 10:28:19 -0600 Subject: [PATCH] fix(aria-required-attr): only require aria-controls if aria-expanded=true (#3089) * fix(aria-required-attr): only require aria-controls if aria-expanded=true * fix tests * fix for modified standads --- .../aria/aria-required-attr-evaluate.js | 14 +++--- test/checks/aria/aria-required-attr.js | 44 ++++++++++++++++--- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/lib/checks/aria/aria-required-attr-evaluate.js b/lib/checks/aria/aria-required-attr-evaluate.js index fb9db7481e..f8285efd9a 100644 --- a/lib/checks/aria/aria-required-attr-evaluate.js +++ b/lib/checks/aria/aria-required-attr-evaluate.js @@ -55,15 +55,17 @@ function ariaRequiredAttrEvaluate(node, options = {}, virtualNode) { } } - // aria 1.2 combobox requires aria-controls, but aria-owns is acceptable instead in earlier versions of the guidelines + // aria 1.2 combobox requires aria-controls, but aria-owns is acceptable instead in earlier versions of the guidelines. also either is only required if the element has aria-expanded=true // https://github.com/dequelabs/axe-core/issues/2505#issuecomment-788703942 + // https://github.com/dequelabs/axe-core/issues/2505#issuecomment-881947373 + const comboboxMissingControls = + role === 'combobox' && missing.includes('aria-controls'); if ( - missing.length === 1 && - role === 'combobox' && - missing[0] === 'aria-controls' && - virtualNode.attr('aria-owns') + comboboxMissingControls && + (virtualNode.hasAttr('aria-owns') || + virtualNode.attr('aria-expanded') !== 'true') ) { - return true; + missing.splice(missing.indexOf('aria-controls', 1)); } if (missing.length) { diff --git a/test/checks/aria/aria-required-attr.js b/test/checks/aria/aria-required-attr.js index 9474715053..8c50ff3ea7 100644 --- a/test/checks/aria/aria-required-attr.js +++ b/test/checks/aria/aria-required-attr.js @@ -58,9 +58,21 @@ describe('aria-required-attr', function() { }); describe('combobox special case', function() { + it('should pass comboboxes that have aria-expanded="false"', function() { + var vNode = queryFixture( + '' + ); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-required-attr') + .call(checkContext, vNode.actualNode, options, vNode) + ); + }); + it('should pass comboboxes that have aria-owns and aria-expanded', function() { var vNode = queryFixture( - '' + '
' ); assert.isTrue( @@ -72,7 +84,7 @@ describe('aria-required-attr', function() { it('should pass comboboxes that have aria-controls and aria-expanded', function() { var vNode = queryFixture( - '' + '
' ); assert.isTrue( @@ -82,9 +94,19 @@ describe('aria-required-attr', function() { ); }); + it('should fail comboboxes that have no required attributes', function() { + var vNode = queryFixture('
'); + + assert.isFalse( + axe.testUtils + .getCheckEvaluate('aria-required-attr') + .call(checkContext, vNode.actualNode, options, vNode) + ); + }); + it('should fail comboboxes that have aria-expanded only', function() { var vNode = queryFixture( - '' + '
' ); assert.isFalse( @@ -94,14 +116,26 @@ describe('aria-required-attr', function() { ); }); - it('should fail comboboxes that have no required attributes', function() { - var vNode = queryFixture('
'); + it('should report missing of multiple attributes correctly', function() { + axe.configure({ + standards: { + ariaRoles: { + combobox: { + requiredAttrs: ['aria-expanded', 'aria-label', 'aria-controls'] + } + } + } + }); + var vNode = queryFixture( + '' + ); assert.isFalse( axe.testUtils .getCheckEvaluate('aria-required-attr') .call(checkContext, vNode.actualNode, options, vNode) ); + assert.deepEqual(checkContext._data, ['aria-label']); }); });