From 6f3fbd610fde6745752c4800d19da9bf84e3e2ae Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 23 May 2023 16:06:12 +0200 Subject: [PATCH] fix(aria-required-children): set related nodes for invalid children --- lib/core/utils/check-helper.js | 23 ++++-- test/core/utils/check-helper.js | 139 ++++++++++++++++++++++---------- 2 files changed, 111 insertions(+), 51 deletions(-) diff --git a/lib/core/utils/check-helper.js b/lib/core/utils/check-helper.js index 68e39f6a4b..0156e60e5a 100644 --- a/lib/core/utils/check-helper.js +++ b/lib/core/utils/check-helper.js @@ -1,5 +1,6 @@ import toArray from './to-array'; import DqElement from './dq-element'; +import AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node'; /** * Helper to denote which checks are asyncronous and provide callbacks and pass data back to the CheckResult @@ -28,17 +29,23 @@ function checkHelper(checkResult, options, resolve, reject) { if (!window.Node) { return; } - - nodes = nodes instanceof window.Node ? [nodes] : toArray(nodes); - if ( - !nodes.every(node => node instanceof window.Node || node.actualNode) + nodes instanceof window.Node || + nodes instanceof AbstractVirtualNode ) { - return; + nodes = [nodes]; + } else { + nodes = toArray(nodes); } - - checkResult.relatedNodes = nodes.map(element => { - return new DqElement(element, options); + checkResult.relatedNodes = []; + nodes.forEach(node => { + if (node instanceof AbstractVirtualNode) { + node = node.actualNode; + } + if (node instanceof window.Node) { + const dqElm = new DqElement(node, options); + checkResult.relatedNodes.push(dqElm); + } }); } }; diff --git a/test/core/utils/check-helper.js b/test/core/utils/check-helper.js index c4608684e3..b40948859e 100644 --- a/test/core/utils/check-helper.js +++ b/test/core/utils/check-helper.js @@ -1,60 +1,61 @@ -describe('axe.utils.checkHelper', function () { - 'use strict'; - - var DqElement = axe.utils.DqElement; +describe('axe.utils.checkHelper', () => { + const { queryFixture } = axe.testUtils; + const DqElement = axe.utils.DqElement; function noop() {} - it('should be a function', function () { + it('should be a function', () => { assert.isFunction(axe.utils.checkHelper); }); - it('should accept 4 named parameters', function () { + it('should accept 4 named parameters', () => { assert.lengthOf(axe.utils.checkHelper, 4); }); - it('should return an object', function () { + it('should return an object', () => { assert.isObject(axe.utils.checkHelper()); }); - describe('return value', function () { - describe('async', function () { - it('should set isAsync property on returned object to `true` when called', function () { - var target = {}, + describe('return value', () => { + describe('async', () => { + it('should set isAsync property on returned object to `true` when called', () => { + const target = {}, helper = axe.utils.checkHelper(target, noop); helper.async(); assert.isTrue(helper.isAsync); }); - it('should call the third parameter of `axe.utils.checkHelper` when invoked', function () { + + it('should call the third parameter of `axe.utils.checkHelper` when invoked', () => { function fn() { success = true; } - var success = false, - helper = axe.utils.checkHelper({}, {}, fn); + let success = false; + const helper = axe.utils.checkHelper({}, {}, fn); - var done = helper.async(); + const done = helper.async(); done(); assert.isTrue(success); }); - it('should call the fourth parameter of `axe.utils.checkHelper` when returning an error', function () { - var success = false; + it('should call the fourth parameter of `axe.utils.checkHelper` when returning an error', () => { + let success = false; function reject(e) { success = true; assert.equal(e.message, 'Concrete donkey!'); } - var helper = axe.utils.checkHelper({}, {}, noop, reject); - var done = helper.async(); + const helper = axe.utils.checkHelper({}, {}, noop, reject); + const done = helper.async(); done(new Error('Concrete donkey!')); assert.isTrue(success); }); }); - describe('data', function () { - it('should set data property on target when called', function () { - var target = {}, + + describe('data', () => { + it('should set data property on target when called', () => { + const target = {}, expected = { monkeys: 'bananas' }, helper = axe.utils.checkHelper(target, noop); @@ -63,15 +64,17 @@ describe('axe.utils.checkHelper', function () { assert.equal(target.data, expected); }); }); - describe('relatedNodes', function () { - var fixture = document.getElementById('fixture'); - afterEach(function () { + + describe('relatedNodes', () => { + const fixture = document.getElementById('fixture'); + afterEach(() => { fixture.innerHTML = ''; }); - it('should accept NodeList', function () { + + it('should accept NodeList', () => { fixture.innerHTML = '
'; - var target = {}, - helper = axe.utils.checkHelper(target, noop); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); helper.relatedNodes(fixture.children); assert.lengthOf(target.relatedNodes, 2); assert.instanceOf(target.relatedNodes[0], DqElement); @@ -79,19 +82,21 @@ 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 accept a single Node', function () { + + it('should accept a single Node', () => { fixture.innerHTML = '
'; - var target = {}, - helper = axe.utils.checkHelper(target, noop); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); helper.relatedNodes(fixture.firstChild); assert.lengthOf(target.relatedNodes, 1); assert.instanceOf(target.relatedNodes[0], DqElement); assert.equal(target.relatedNodes[0].element, fixture.firstChild); }); - it('should accept an Array', function () { + + it('should accept an Array', () => { fixture.innerHTML = '
'; - var target = {}, - helper = axe.utils.checkHelper(target, noop); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); helper.relatedNodes(Array.prototype.slice.call(fixture.children)); assert.lengthOf(target.relatedNodes, 2); assert.instanceOf(target.relatedNodes[0], DqElement); @@ -99,11 +104,12 @@ 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 accept an array-like Object', function () { + + it('should accept an array-like Object', () => { fixture.innerHTML = '
'; - var target = {}, - helper = axe.utils.checkHelper(target, noop); - var nodes = { + const target = {}; + const helper = axe.utils.checkHelper(target, noop); + const nodes = { 0: fixture.children[0], 1: fixture.children[1], length: 2 @@ -115,13 +121,60 @@ 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({ + + it('should accept a VirtualNode', () => { + const vNode = queryFixture(''); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); + helper.relatedNodes(vNode); + assert.lengthOf(target.relatedNodes, 1); + assert.instanceOf(target.relatedNodes[0], DqElement); + assert.equal(target.relatedNodes[0].element.nodeName, 'A'); + }); + + it('should accept an array of VirtualNodes', () => { + const vNode = queryFixture(` +
+ `); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); + helper.relatedNodes(vNode.children); + assert.lengthOf(target.relatedNodes, 2); + assert.instanceOf(target.relatedNodes[0], DqElement); + assert.equal(target.relatedNodes[0].element.nodeName, 'A'); + assert.equal(target.relatedNodes[1].element.nodeName, 'B'); + }); + + it('should filter out non-nodes', () => { + const vNode = queryFixture(` +
+ `); + const target = {}; + const helper = axe.utils.checkHelper(target, noop); + const nodes = [ + null, + vNode, + true, + fixture.querySelector('b'), + 'hello world', + new axe.SerialVirtualNode({ + nodeName: 's' + }) + ]; + helper.relatedNodes(nodes); + assert.lengthOf(target.relatedNodes, 2); + assert.instanceOf(target.relatedNodes[0], DqElement); + assert.equal(target.relatedNodes[0].element.nodeName, 'A'); + assert.equal(target.relatedNodes[1].element.nodeName, 'B'); + }); + + it('should noop for non-node-like objects', () => { + const target = {}; + const helper = axe.utils.checkHelper(target, noop); + const nodes = new axe.SerialVirtualNode({ nodeName: 'div' }); - assert.doesNotThrow(function () { + assert.doesNotThrow(() => { helper.relatedNodes(nodes); }); assert.lengthOf(target.relatedNodes, 0);