Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(aria-required-children): set related nodes for invalid children #4033

Merged
merged 1 commit into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions lib/core/utils/check-helper.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
}
});
}
};
Expand Down
139 changes: 96 additions & 43 deletions test/core/utils/check-helper.js
Original file line number Diff line number Diff line change
@@ -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);

Expand All @@ -63,47 +64,52 @@ 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 = '<div id="t1"></div><div id="t2"></div>';
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);
assert.instanceOf(target.relatedNodes[1], DqElement);
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 = '<div id="t1"></div><div id="t2"></div>';
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 = '<div id="t1"></div><div id="t2"></div>';
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);
assert.instanceOf(target.relatedNodes[1], DqElement);
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 = '<div id="t1"></div><div id="t2"></div>';
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
Expand All @@ -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('<a id="target"></a>');
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(`
<div id="target"><a></a><b></b></div>
`);
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(`
<div><a id="target"></a><b></b></div>
`);
const target = {};
const helper = axe.utils.checkHelper(target, noop);
const nodes = [
null,
vNode,
true,
fixture.querySelector('b'),
'hello world',
new axe.SerialVirtualNode({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad you added this one. At first I thought it would go through so nice to see it in a test to ensure it doesn't.

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);
Expand Down