From 40025ad09bac49ee56a0d2cd1d45668dc90b54db Mon Sep 17 00:00:00 2001 From: Dan Tripp Date: Sat, 27 Nov 2021 20:19:49 -0500 Subject: [PATCH] work in progress --- .../keyboard/no-focusable-content-evaluate.js | 4 ++- test/checks/keyboard/no-focusable-content.js | 34 +++++++++++++------ .../rules/aria-text/aria-text.html | 14 ++++++-- .../rules/aria-text/aria-text.json | 4 +-- .../nested-interactive.html | 14 ++++---- .../nested-interactive.json | 9 ++--- .../virtual-rules/nested-interactive.js | 8 ++--- 7 files changed, 55 insertions(+), 32 deletions(-) diff --git a/lib/checks/keyboard/no-focusable-content-evaluate.js b/lib/checks/keyboard/no-focusable-content-evaluate.js index 27824fc4f4..8fc8aa3b34 100644 --- a/lib/checks/keyboard/no-focusable-content-evaluate.js +++ b/lib/checks/keyboard/no-focusable-content-evaluate.js @@ -1,4 +1,5 @@ import isFocusable from '../../commons/dom/is-focusable'; +import { getRole, getRoleType } from '../../commons/aria'; export default function noFocusableContentEvaluate(node, options, virtualNode) { if (!virtualNode.children) { @@ -40,7 +41,8 @@ function getFocusableDescendants(vNode) { const retVal = []; vNode.children.forEach(child => { - if (isFocusable(child)) { + const role = getRole(child); + if(getRoleType(role) === 'widget' && isFocusable(child)) { retVal.push(child); } else { retVal.push(...getFocusableDescendants(child)); diff --git a/test/checks/keyboard/no-focusable-content.js b/test/checks/keyboard/no-focusable-content.js index fdea6a0b05..871169f202 100644 --- a/test/checks/keyboard/no-focusable-content.js +++ b/test/checks/keyboard/no-focusable-content.js @@ -28,17 +28,22 @@ describe('no-focusable-content tests', function() { assert.isTrue(noFocusableContent(null, null, vNode)); }); - it('should return false if element has focusable content', function() { + it('should return true if element has content which is focusable (tabindex=0) and does not have a widget role', function() { var params = checkSetup( '' ); - assert.isFalse(noFocusableContent.apply(checkContext, params)); - assert.deepEqual(checkContext._data, null); - assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]); + assert.isTrue(noFocusableContent.apply(checkContext, params)); + }); + + it('should return true if element has content which has negative tabindex and non-widget role', function() { + var vNode = queryFixture( + '' + ); + assert.isTrue(noFocusableContent(null, null, vNode)); }); - it('should return false if element has natively focusable content', function() { + it('should return false if element has content which is natively focusable and has a widget role', function() { var params = checkSetup( '' ); @@ -50,7 +55,7 @@ describe('no-focusable-content tests', function() { it('should add each focusable child as related nodes', function() { var params = checkSetup( - '' + '' ); assert.isFalse(noFocusableContent.apply(checkContext, params)); @@ -61,7 +66,7 @@ describe('no-focusable-content tests', function() { ]); }); - it('should return false if element has natively focusable content with negative tabindex', function() { + it('should return false if element has natively focusable widget role content with negative tabindex', function() { var params = checkSetup( '' ); @@ -71,25 +76,32 @@ describe('no-focusable-content tests', function() { assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]); }); - it('should return true on span with tabindex=-1', function() { + it('should return true if element has content which is natively focusable and has a widget role but is disabled', function() { + var vNode = queryFixture( + '' + ); + assert.isTrue(noFocusableContent(null, null, vNode)); + }); + + it('should return true on span with negative tabindex (focusable, does not have a widget role)', function() { var vNode = queryFixture(' some text ' +'JavaScript is able to focus this ' +''); assert.isTrue(noFocusableContent(null, null, vNode)); }); - it('should return true on aria-hidden span with tabindex=-1', function() { + it('should return true on aria-hidden span with negative tabindex (focusable, does not have a widget role)', function() { var vNode = queryFixture(' some text ' +' ' +''); assert.isTrue(noFocusableContent(null, null, vNode)); }); - it('should return false on span with tabindex=0', function() { + it('should return true on nested span with tabindex=0 (focusable, does not have a widget role)', function() { var vNode = queryFixture(' some text ' +'anyone is able to focus this ' +''); - assert.isFalse(noFocusableContent(null, null, vNode)); + assert.isTrue(noFocusableContent(null, null, vNode)); }); }); diff --git a/test/integration/rules/aria-text/aria-text.html b/test/integration/rules/aria-text/aria-text.html index 4059f1e53b..54809b6bd5 100644 --- a/test/integration/rules/aria-text/aria-text.html +++ b/test/integration/rules/aria-text/aria-text.html @@ -15,8 +15,16 @@

Flattened text because of the explicit role.
-
+
Flattened text because of the - explicit role. Considered non-focusable because of tabindex=-1... + explicit role. +
+

+

+

Hello

+ + -

diff --git a/test/integration/rules/aria-text/aria-text.json b/test/integration/rules/aria-text/aria-text.json index 3aa00ff23b..9443502a16 100644 --- a/test/integration/rules/aria-text/aria-text.json +++ b/test/integration/rules/aria-text/aria-text.json @@ -1,6 +1,6 @@ { "description": "aria-text tests", "rule": "aria-text", - "violations": [["#fail1"], ["#fail2"], ["#fail3"]], - "passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"]] + "violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"], ["#fail5"]], + "passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"], ["#pass6"]] } diff --git a/test/integration/rules/nested-interactive/nested-interactive.html b/test/integration/rules/nested-interactive/nested-interactive.html index a11161f964..1af908b261 100644 --- a/test/integration/rules/nested-interactive/nested-interactive.html +++ b/test/integration/rules/nested-interactive/nested-interactive.html @@ -6,13 +6,13 @@ - -
- - - - - + +
+ + + + + ignored ignored diff --git a/test/integration/rules/nested-interactive/nested-interactive.json b/test/integration/rules/nested-interactive/nested-interactive.json index 7a9c0df654..4db1a12cdb 100644 --- a/test/integration/rules/nested-interactive/nested-interactive.json +++ b/test/integration/rules/nested-interactive/nested-interactive.json @@ -6,9 +6,7 @@ ["#fail2"], ["#fail3"], ["#fail4"], - ["#fail5"], - ["#fail6"], - ["#fail7"] + ["#fail5"] ], "passes": [ ["#pass1"], @@ -18,6 +16,9 @@ ["#pass5"], ["#pass6"], ["#pass7"], - ["#pass8"] + ["#pass8"], + ["#pass9"], + ["#pass10"], + ["#pass11"] ] } diff --git a/test/integration/virtual-rules/nested-interactive.js b/test/integration/virtual-rules/nested-interactive.js index cd364e2b22..fbb94c136b 100644 --- a/test/integration/virtual-rules/nested-interactive.js +++ b/test/integration/virtual-rules/nested-interactive.js @@ -74,7 +74,7 @@ describe('nested-interactive virtual-rule', function() { assert.lengthOf(results.incomplete, 0); }); - it('should fail for element with focusable content', function() { + it('should pass for element with non-widget content', function() { var node = new axe.SerialVirtualNode({ nodeName: 'button' }); @@ -89,12 +89,12 @@ describe('nested-interactive virtual-rule', function() { var results = axe.runVirtualRule('nested-interactive', node); - assert.lengthOf(results.passes, 0); - assert.lengthOf(results.violations, 1); + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); assert.lengthOf(results.incomplete, 0); }); - it('should fail for element with natively focusable content', function() { + it('should fail for element with native widget content', function() { var node = new axe.SerialVirtualNode({ nodeName: 'div', attributes: {