Skip to content

Commit

Permalink
Merge branch 'develop' into aria-deprecated-role
Browse files Browse the repository at this point in the history
  • Loading branch information
straker committed Jul 6, 2023
2 parents 72daf84 + 481ade0 commit 925d91b
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 53 deletions.
2 changes: 2 additions & 0 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,8 @@ The `assets` attribute expects an array of preload(able) constraints to be fetch

The `timeout` attribute in the object configuration is `optional` and has a fallback default value (10000ms). The `timeout` is essential for any network dependent assets that are preloaded, where-in if a given request takes longer than the specified/ default value, the operation is aborted.

Preloading is not applicable to all rules. Even if the `preload` option is enabled, preloading steps may be skipped if no enabled rules require preloading.

##### Callback Parameter

The callback parameter is a function that will be called when the asynchronous `axe.run` function completes. The callback function is passed two parameters. The first parameter will be an error thrown inside of axe if axe.run could not complete. If axe completed correctly the first parameter will be null, and the second parameter will be the results object.
Expand Down
2 changes: 1 addition & 1 deletion doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
| [definition-list](https://dequeuniversity.com/rules/axe/4.7/definition-list?application=RuleDescription) | Ensures <dl> elements are structured correctly | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | |
| [dlitem](https://dequeuniversity.com/rules/axe/4.7/dlitem?application=RuleDescription) | Ensures <dt> and <dd> elements are contained by a <dl> | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | |
| [document-title](https://dequeuniversity.com/rules/axe/4.7/document-title?application=RuleDescription) | Ensures each HTML document contains a non-empty <title> element | Serious | cat.text-alternatives, wcag2a, wcag242, TTv5, TT12.a, EN-301-549, EN-9.2.4.2, ACT | failure | [2779a5](https://act-rules.github.io/rules/2779a5) |
| [duplicate-id-aria](https://dequeuniversity.com/rules/axe/4.7/duplicate-id-aria?application=RuleDescription) | Ensures every id attribute value used in ARIA and in labels is unique | Critical | cat.parsing, wcag2a, wcag411, EN-301-549, EN-9.4.1.1 | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) |
| [duplicate-id-aria](https://dequeuniversity.com/rules/axe/4.7/duplicate-id-aria?application=RuleDescription) | Ensures every id attribute value used in ARIA and in labels is unique | Critical | cat.parsing, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | needs review | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) |
| [form-field-multiple-labels](https://dequeuniversity.com/rules/axe/4.7/form-field-multiple-labels?application=RuleDescription) | Ensures form field does not have multiple label elements | Moderate | cat.forms, wcag2a, wcag332, TTv5, TT5.c, EN-301-549, EN-9.3.3.2 | needs review | |
| [frame-focusable-content](https://dequeuniversity.com/rules/axe/4.7/frame-focusable-content?application=RuleDescription) | Ensures <frame> and <iframe> elements with focusable content do not have tabindex=-1 | Serious | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a, EN-301-549, EN-9.2.1.1 | failure, needs review | [akn7bn](https://act-rules.github.io/rules/akn7bn) |
| [frame-title-unique](https://dequeuniversity.com/rules/axe/4.7/frame-title-unique?application=RuleDescription) | Ensures <iframe> and <frame> elements contain a unique title attribute | Serious | cat.text-alternatives, wcag2a, wcag412, TTv5, TT12.d, EN-301-549, EN-9.4.1.2 | needs review | [4b1c6c](https://act-rules.github.io/rules/4b1c6c) |
Expand Down
58 changes: 35 additions & 23 deletions lib/core/utils/clone.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
/**
* Deeply clones an object or array
* Deeply clones an object or array. DOM nodes or collections of DOM nodes are not deeply cloned and are instead returned as is.
* @param {Mixed} obj The object/array to clone
* @return {Mixed} A clone of the initial object or array
* @return {Mixed} A clone of the initial object or array
*/
function clone(obj) {
/* eslint guard-for-in: 0*/
var index,
length,
out = obj;
// DOM nodes cannot be cloned.
export default function clone(obj) {
return cloneRecused(obj, new Map());
}

// internal function to hide non-user facing parameters
function cloneRecused(obj, seen) {
if (obj === null || typeof obj !== 'object') {
return obj;
}

// don't clone DOM nodes. since we can pass nodes from different window contexts
// we'll also use duck typing to determine what is a DOM node
if (
(window?.Node && obj instanceof window.Node) ||
(window?.HTMLCollection && obj instanceof window.HTMLCollection)
(window?.HTMLCollection && obj instanceof window.HTMLCollection) ||
('nodeName' in obj && 'nodeType' in obj && 'ownerDocument' in obj)
) {
return obj;
}

if (obj !== null && typeof obj === 'object') {
if (Array.isArray(obj)) {
out = [];
for (index = 0, length = obj.length; index < length; index++) {
out[index] = clone(obj[index]);
}
} else {
out = {};
for (index in obj) {
out[index] = clone(obj[index]);
}
}
// handle circular references by caching the cloned object and returning it
if (seen.has(obj)) {
return seen.get(obj);
}

if (Array.isArray(obj)) {
const out = [];
seen.set(obj, out);
obj.forEach(value => {
out.push(cloneRecused(value, seen));
});
return out;
}

const out = {};
seen.set(obj, out);
// eslint-disable-next-line guard-for-in
for (const key in obj) {
out[key] = cloneRecused(obj[key], seen);
}
return out;
}

export default clone;
3 changes: 2 additions & 1 deletion lib/rules/duplicate-id-aria.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"selector": "[id]",
"matches": "duplicate-id-aria-matches",
"excludeHidden": false,
"tags": ["cat.parsing", "wcag2a", "wcag411", "EN-301-549", "EN-9.4.1.1"],
"tags": ["cat.parsing", "wcag2a", "wcag412", "EN-301-549", "EN-9.4.1.2"],
"reviewOnFail": true,
"actIds": ["3ea0c8"],
"metadata": {
"description": "Ensures every id attribute value used in ARIA and in labels is unique",
Expand Down
108 changes: 85 additions & 23 deletions test/core/utils/clone.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
describe('utils.clone', function () {
'use strict';
var clone = axe.utils.clone;
describe('utils.clone', () => {
const clone = axe.utils.clone;
const fixture = document.querySelector('#fixture');

it('should clone an object', function () {
var obj = {
it('should clone an object', () => {
const obj = {
cats: true,
dogs: 2,
fish: [0, 1, 2]
fish: [0, 1, { one: 'two' }]
};
var c = clone(obj);
const c = clone(obj);

obj.cats = false;
obj.dogs = 1;
obj.fish[0] = 'stuff';
obj.fish[2].one = 'three';

assert.strictEqual(c.cats, true);
assert.strictEqual(c.dogs, 2);
assert.deepEqual(c.fish, [0, 1, 2]);
assert.deepEqual(c.fish, [0, 1, { one: 'two' }]);
});

it('should clone nested objects', function () {
var obj = {
it('should clone nested objects', () => {
const obj = {
cats: {
fred: 1,
billy: 2,
Expand All @@ -33,7 +33,7 @@ describe('utils.clone', function () {
},
fish: [0, 1, 2]
};
var c = clone(obj);
const c = clone(obj);

obj.cats.fred = 47;
obj.dogs = 47;
Expand All @@ -54,45 +54,107 @@ describe('utils.clone', function () {
assert.deepEqual(c.fish, [0, 1, 2]);
});

it('should clone objects with methods', function () {
var obj = {
cats: function () {
it('should clone objects with methods', () => {
const obj = {
cats: () => {
return 'meow';
},
dogs: function () {
dogs: () => {
return 'woof';
}
};
var c = clone(obj);
const c = clone(obj);

assert.strictEqual(obj.cats, c.cats);
assert.strictEqual(obj.dogs, c.dogs);

obj.cats = function () {};
obj.dogs = function () {};
obj.cats = () => {};
obj.dogs = () => {};

assert.notStrictEqual(obj.cats, c.cats);
assert.notStrictEqual(obj.dogs, c.dogs);
});

it('should clone prototypes', function () {
it('should clone prototypes', () => {
function Cat(name) {
this.name = name;
}

Cat.prototype.meow = function () {
Cat.prototype.meow = () => {
return 'meow';
};

Cat.prototype.bark = function () {
Cat.prototype.bark = () => {
return 'cats dont bark';
};

var cat = new Cat('Fred'),
const cat = new Cat('Fred'),
c = clone(cat);

assert.deepEqual(cat.name, c.name);
assert.deepEqual(Cat.prototype.bark, c.bark);
assert.deepEqual(Cat.prototype.meow, c.meow);
});

it('should clone circular objects while keeping the circular reference', () => {
const obj = { cats: true };
obj.child = obj;
const c = clone(obj);

obj.cats = false;

assert.deepEqual(c, {
cats: true,
child: c
});
assert.strictEqual(c, c.child);
});

it('should not return the same object when cloned twice', () => {
const obj = { cats: true };
const c1 = clone(obj);
const c2 = clone(obj);

assert.notStrictEqual(c1, c2);
});

it('should not return the same object when nested', () => {
const obj = { dogs: true };
const obj1 = { cats: true, child: { prop: obj } };
const obj2 = { fish: [0, 1, 2], child: { prop: obj } };

const c1 = clone(obj1);
const c2 = clone(obj2);

assert.notStrictEqual(c1.child.prop, c2.child.prop);
});

it('should not clone HTML elements', () => {
const obj = {
cats: true,
node: document.createElement('div')
};
const c = clone(obj);

obj.cats = false;

assert.equal(c.cats, true);
assert.strictEqual(c.node, obj.node);
});

it('should not clone HTML elements from different windows', () => {
fixture.innerHTML = '<iframe id="target"></iframe>';
const iframe = fixture.querySelector('#target');

const obj = {
cats: true,
node: iframe.contentDocument
};
const c = clone(obj);

obj.cats = false;

assert.equal(c.cats, true);
assert.strictEqual(c.node, obj.node);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
<select id="ignore4"></select>
<div tabindex="0" id="ignore5"></div>

<span id="fail1" class="fail1"></span>
<button id="fail1"></button>
<span id="incomplete1" class="incomplete1"></span>
<button id="incomplete1"></button>
<span id="pass1"></span>
<button id="pass2"></button>
<div aria-labelledby="fail1 pass1 pass2"></div>
<div aria-labelledby="incomplete1 pass1 pass2"></div>

<input id="ignore6" type="hidden" />
<button id="ignore7" disabled></button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"description": "duplicate-id-aria test",
"rule": "duplicate-id-aria",
"violations": [[".fail1"]],
"violations": [],
"incomplete": [[".incomplete1"]],
"passes": [["#pass1"], ["#pass2"]]
}
6 changes: 5 additions & 1 deletion test/integration/rules/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@
// check that all nodes are removed
assert.equal(JSON.stringify(targets), '[]');
} else {
assert(false, 'there are no ' + collection);
assert.lengthOf(
test[collection],
0,
'there are no ' + collection
);
}
});
});
Expand Down

0 comments on commit 925d91b

Please sign in to comment.