diff --git a/lib/checks/keyboard/no-focusable-content-evaluate.js b/lib/checks/keyboard/no-focusable-content-evaluate.js
index 907e32ee29..27824fc4f4 100644
--- a/lib/checks/keyboard/no-focusable-content-evaluate.js
+++ b/lib/checks/keyboard/no-focusable-content-evaluate.js
@@ -1,5 +1,34 @@
import isFocusable from '../../commons/dom/is-focusable';
+export default function noFocusableContentEvaluate(node, options, virtualNode) {
+ if (!virtualNode.children) {
+ return undefined;
+ }
+
+ try {
+ const focusableDescendants = getFocusableDescendants(virtualNode);
+
+ if (!focusableDescendants.length) {
+ return true;
+ }
+
+ const notHiddenElements = focusableDescendants.filter(
+ usesUnreliableHidingStrategy
+ );
+
+ if (notHiddenElements.length > 0) {
+ this.data({ messageKey: 'notHidden' });
+ this.relatedNodes(notHiddenElements);
+ } else {
+ this.relatedNodes(focusableDescendants);
+ }
+
+ return false;
+ } catch (e) {
+ return undefined;
+ }
+}
+
function getFocusableDescendants(vNode) {
if (!vNode.children) {
if (vNode.props.nodeType === 1) {
@@ -11,7 +40,7 @@ function getFocusableDescendants(vNode) {
const retVal = [];
vNode.children.forEach(child => {
- if(isFocusable(child)) {
+ if (isFocusable(child)) {
retVal.push(child);
} else {
retVal.push(...getFocusableDescendants(child));
@@ -24,24 +53,3 @@ function usesUnreliableHidingStrategy(vNode) {
const tabIndex = parseInt(vNode.attr('tabindex'), 10);
return !isNaN(tabIndex) && tabIndex < 0;
}
-
-function noFocusableContentEvaluate(node, options, virtualNode) {
- if (!virtualNode.children) {
- return undefined;
- }
- try {
- const focusableDescendants = getFocusableDescendants(virtualNode);
- if(focusableDescendants.length > 0) {
- const notHiddenElements = focusableDescendants.filter(usesUnreliableHidingStrategy);
- if(notHiddenElements.length > 0) {
- this.data({ messageKey: 'notHidden' });
- this.relatedNodes(notHiddenElements);
- }
- }
- return focusableDescendants.length === 0;
- } catch (e) {
- return undefined;
- }
-}
-
-export default noFocusableContentEvaluate;
diff --git a/lib/core/utils/check-helper.js b/lib/core/utils/check-helper.js
index 54bd539eb1..68e39f6a4b 100644
--- a/lib/core/utils/check-helper.js
+++ b/lib/core/utils/check-helper.js
@@ -25,7 +25,18 @@ function checkHelper(checkResult, options, resolve, reject) {
checkResult.data = data;
},
relatedNodes(nodes) {
+ if (!window.Node) {
+ return;
+ }
+
nodes = nodes instanceof window.Node ? [nodes] : toArray(nodes);
+
+ if (
+ !nodes.every(node => node instanceof window.Node || node.actualNode)
+ ) {
+ return;
+ }
+
checkResult.relatedNodes = nodes.map(element => {
return new DqElement(element, options);
});
diff --git a/test/checks/keyboard/no-focusable-content.js b/test/checks/keyboard/no-focusable-content.js
index 8c84f7da81..1389e0241a 100644
--- a/test/checks/keyboard/no-focusable-content.js
+++ b/test/checks/keyboard/no-focusable-content.js
@@ -29,19 +29,38 @@ describe('no-focusable-content tests', function() {
});
it('should return false if element has focusable content', function() {
- var vNode = queryFixture(
+ var params = checkSetup(
''
);
- assert.isFalse(noFocusableContent(null, null, vNode));
+
+ assert.isFalse(noFocusableContent.apply(checkContext, params));
+ assert.deepEqual(checkContext._data, null);
+ assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});
it('should return false if element has natively focusable content', function() {
- var vNode = queryFixture(
+ var params = checkSetup(
''
);
- assert.isFalse(noFocusableContent(null, null, vNode));
+
+ assert.isFalse(noFocusableContent.apply(checkContext, params));
+ assert.deepEqual(checkContext._data, null);
+ assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});
-
+
+ it('should add each focusable child as related nodes', function() {
+ var params = checkSetup(
+ ''
+ );
+
+ assert.isFalse(noFocusableContent.apply(checkContext, params));
+ assert.deepEqual(checkContext._data, null);
+ assert.deepEqual(checkContext._relatedNodes, [
+ params[2].children[0],
+ params[2].children[1]
+ ]);
+ });
+
it('should return false if element has natively focusable content with negative tabindex', function() {
var params = checkSetup(
''
@@ -49,6 +68,6 @@ describe('no-focusable-content tests', function() {
axe.utils.getFlattenedTree(document.documentElement);
assert.isFalse(check.evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });
+ assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});
-
});
diff --git a/test/core/utils/check-helper.js b/test/core/utils/check-helper.js
index a198ed23bb..a5c37b83b2 100644
--- a/test/core/utils/check-helper.js
+++ b/test/core/utils/check-helper.js
@@ -115,6 +115,17 @@ describe('axe.utils.checkHelper', function() {
assert.equal(target.relatedNodes[0].element, fixture.children[0]);
assert.equal(target.relatedNodes[1].element, fixture.children[1]);
});
+ it('should noop for non-node-like objects', function() {
+ var target = {},
+ helper = axe.utils.checkHelper(target, noop);
+ var nodes = new axe.SerialVirtualNode({
+ nodeName: 'div'
+ });
+ assert.doesNotThrow(function() {
+ helper.relatedNodes(nodes);
+ });
+ assert.lengthOf(target.relatedNodes, 0);
+ });
});
});
});