diff --git a/lib/checks/keyboard/focusable-no-name-evaluate.js b/lib/checks/keyboard/focusable-no-name-evaluate.js index 5da1dc08a8..eb8b2c014f 100644 --- a/lib/checks/keyboard/focusable-no-name-evaluate.js +++ b/lib/checks/keyboard/focusable-no-name-evaluate.js @@ -2,12 +2,13 @@ import { isFocusable } from '../../commons/dom'; import { accessibleTextVirtual } from '../../commons/text'; function focusableNoNameEvaluate(node, options, virtualNode) { + const tabIndex = virtualNode.attr('tabindex'); + const inFocusOrder = isFocusable(virtualNode) && tabIndex > -1; + if (!inFocusOrder) { + return false; + } + try { - const tabIndex = virtualNode.attr('tabindex'); - const inFocusOrder = isFocusable(virtualNode) && tabIndex > -1; - if (!inFocusOrder) { - return false; - } return !accessibleTextVirtual(virtualNode); } catch (e) { return undefined; diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index bc92469e8e..972d6805b6 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -106,7 +106,7 @@ function resolveImplicitRole(vNode, explicitRoleOptions) { // See also: https://github.com/w3c/aria/issues/1270 function hasConflictResolution(vNode) { const hasGlobalAria = getGlobalAriaAttrs().some(attr => vNode.hasAttr(attr)); - return hasGlobalAria || isFocusable(vNode.actualNode); + return hasGlobalAria || isFocusable(vNode); } /** diff --git a/lib/commons/standards/implicit-html-roles.js b/lib/commons/standards/implicit-html-roles.js index d759484e96..890e31afca 100644 --- a/lib/commons/standards/implicit-html-roles.js +++ b/lib/commons/standards/implicit-html-roles.js @@ -93,7 +93,7 @@ const implicitHtmlRoles = { vNode.hasAttr(attr) ); - return emptyAlt && !hasGlobalAria && !isFocusable(vNode.actualNode) + return emptyAlt && !hasGlobalAria && !isFocusable(vNode) ? 'presentation' : 'img'; }, diff --git a/test/integration/virtual-rules/index.html b/test/integration/virtual-rules/index.html index 39a3fd7043..c3b9722259 100644 --- a/test/integration/virtual-rules/index.html +++ b/test/integration/virtual-rules/index.html @@ -34,6 +34,7 @@ + diff --git a/test/integration/virtual-rules/link-name.js b/test/integration/virtual-rules/link-name.js new file mode 100644 index 0000000000..74db1542ee --- /dev/null +++ b/test/integration/virtual-rules/link-name.js @@ -0,0 +1,172 @@ +describe('link-name', function() { + it('should pass for aria-label', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + 'aria-label': 'foobar' + } + }); + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should incomplete for aria-labelledby', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + 'aria-labelledby': 'foobar' + } + }); + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 1); + }); + + it('should pass for role=presentation', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + tabindex: '-1', + role: 'presentation' + } + }); + node.children = []; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass for role=none', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + tabindex: '-1', + role: 'none' + } + }); + node.children = []; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass for visible text content', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'span', + attributes: { + role: 'link' + } + }); + var child = new axe.SerialVirtualNode({ + nodeName: '#text', + nodeType: 3, + nodeValue: 'foobar' + }); + node.children = [child]; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should incomplete when aria-label and children are missing', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html' + } + }); + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 1); + }); + + it('should fail when aria-label contains only whitespace', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + 'aria-label': ' \t \n ' + } + }); + node.children = []; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); + + it('should fail when aria-label is empty', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + 'aria-label': '' + } + }); + node.children = []; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); + + it('should incomplete if anchor is still focusable and missing children', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + role: 'presentation' + } + }); + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 1); + }); + + it('should fail if anchor is still focusable and no children', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'a', + attributes: { + href: '/foo.html', + role: 'presentation' + } + }); + node.children = []; + + var results = axe.runVirtualRule('link-name', node); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); +});