diff --git a/lib/checks/aria/aria-prohibited-attr-evaluate.js b/lib/checks/aria/aria-prohibited-attr-evaluate.js index e6e2f9da7d..03bbc0cb14 100644 --- a/lib/checks/aria/aria-prohibited-attr-evaluate.js +++ b/lib/checks/aria/aria-prohibited-attr-evaluate.js @@ -33,7 +33,11 @@ export default function ariaProhibitedAttrEvaluate( ) { const elementsAllowedAriaLabel = options?.elementsAllowedAriaLabel || []; const { nodeName } = virtualNode.props; - const role = getRole(virtualNode, { chromium: true }); + const role = getRole(virtualNode, { + chromium: true, + // this check allows fallback roles. For example, `
` is legal. + fallback: true + }); const prohibitedList = listProhibitedAttrs( role, @@ -51,7 +55,7 @@ export default function ariaProhibitedAttrEvaluate( return false; } - let messageKey = virtualNode.hasAttr('role') ? 'hasRole' : 'noRole'; + let messageKey = role !== null ? 'hasRole' : 'noRole'; messageKey += prohibited.length > 1 ? 'Plural' : 'Singular'; this.data({ role, nodeName, messageKey, prohibited }); diff --git a/test/checks/aria/aria-prohibited-attr.js b/test/checks/aria/aria-prohibited-attr.js index 4ae70f6bb0..116f31fc0d 100644 --- a/test/checks/aria/aria-prohibited-attr.js +++ b/test/checks/aria/aria-prohibited-attr.js @@ -138,4 +138,37 @@ describe('aria-prohibited-attr', function () { var params = checkSetup(''); assert.isFalse(checkEvaluate.apply(checkContext, params)); }); + + it('should not allow aria-label on divs that have an invalid role', function () { + var params = checkSetup( + '
' + ); + assert.isTrue(checkEvaluate.apply(checkContext, params)); + assert.deepEqual(checkContext._data, { + nodeName: 'div', + role: null, + messageKey: 'noRoleSingular', + prohibited: ['aria-label'] + }); + }); + + it('should allow aria-label on divs with a valid fallback role', function () { + var params = checkSetup( + '
' + ); + assert.isFalse(checkEvaluate.apply(checkContext, params)); + }); + + it('should not allow aria-label on divs with no valid fallback roles', function () { + var params = checkSetup( + '
' + ); + assert.isTrue(checkEvaluate.apply(checkContext, params)); + assert.deepEqual(checkContext._data, { + nodeName: 'div', + role: null, + messageKey: 'noRoleSingular', + prohibited: ['aria-label'] + }); + }); }); diff --git a/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.html b/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.html index ec354260a3..3be7f40ea3 100644 --- a/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.html +++ b/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.html @@ -3,6 +3,7 @@
Foo
+
@@ -35,6 +36,8 @@
+
+
Foo
Foo
diff --git a/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.json b/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.json index f6f9e7717b..9ac7375cbe 100644 --- a/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.json +++ b/test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.json @@ -1,7 +1,14 @@ { "description": "aria-prohibited-attr tests", "rule": "aria-prohibited-attr", - "passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"]], + "passes": [ + ["#pass1"], + ["#pass2"], + ["#pass3"], + ["#pass4"], + ["#pass5"], + ["#pass6"] + ], "incomplete": [["#incomplete1"], ["#incomplete2"], ["#incomplete3"]], "violations": [ ["#fail1"], @@ -32,6 +39,8 @@ ["#fail26"], ["#fail27"], ["#fail28"], - ["#fail29"] + ["#fail29"], + ["#fail30"], + ["#fail31"] ] } diff --git a/test/integration/virtual-rules/aria-prohibited-attr.js b/test/integration/virtual-rules/aria-prohibited-attr.js index ccacf57b33..4f7fa95891 100644 --- a/test/integration/virtual-rules/aria-prohibited-attr.js +++ b/test/integration/virtual-rules/aria-prohibited-attr.js @@ -58,4 +58,52 @@ describe('aria-prohibited-attr virtual-rule', () => { assert.lengthOf(results.violations, 1); assert.lengthOf(results.incomplete, 0); }); + + it('should fail for invalid role', () => { + const vNode = new axe.SerialVirtualNode({ + nodeName: 'div', + attributes: { + role: 'foo', + 'aria-label': 'foo' + } + }); + vNode.children = []; + + const results = axe.runVirtualRule('aria-prohibited-attr', vNode); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass for fallback roles', () => { + const results = axe.runVirtualRule('aria-prohibited-attr', { + nodeName: 'div', + attributes: { + role: 'foo dialog', + 'aria-label': 'foo' + } + }); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should fail for multiple invalid roles', () => { + const vNode = new axe.SerialVirtualNode({ + nodeName: 'div', + attributes: { + role: 'foo bar', + 'aria-label': 'foo' + } + }); + vNode.children = []; + + const results = axe.runVirtualRule('aria-prohibited-attr', vNode); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); });