Skip to content

Commit

Permalink
fix(aria-required-children): avoid confusing aria-busy message in fai…
Browse files Browse the repository at this point in the history
…lures (#4347)

Removes the confusing aria-busy message from aria-required-children
failures (which we've seen several reports of).

Also fixes a false negative (the new #fail13 integration test), which
was related but hasn't been reported before.

~~Marks the separate `aria-busy` check as deprecated since no rule uses
it after this change.~~ Moving this to separate PR per wilco's feedback

@straker , note that this will have a logical merge conflict with the
PRs adding new translations you're working on

Closes: #4340
  • Loading branch information
dbjorge committed Mar 5, 2024
1 parent a99cdc6 commit 591607d
Show file tree
Hide file tree
Showing 19 changed files with 111 additions and 16 deletions.
5 changes: 5 additions & 0 deletions lib/checks/aria/aria-required-children-evaluate.js
Expand Up @@ -55,6 +55,11 @@ export default function ariaRequiredChildrenEvaluate(
return true;
}

if (virtualNode.attr('aria-busy') === 'true') {
this.data({ messageKey: 'aria-busy' });
return true;
}

this.data(required);

// Only review empty nodes when a node is both empty and does not have an aria-owns relationship
Expand Down
5 changes: 4 additions & 1 deletion lib/checks/aria/aria-required-children.json
Expand Up @@ -20,7 +20,10 @@
"metadata": {
"impact": "critical",
"messages": {
"pass": "Required ARIA children are present",
"pass": {
"default": "Required ARIA children are present",
"aria-busy": "Element has an aria-busy attribute, so it is allowed to omit required children"
},
"fail": {
"singular": "Required ARIA child role not present: ${data.values}",
"plural": "Required ARIA children role not present: ${data.values}",
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/aria-required-children.json
Expand Up @@ -10,6 +10,6 @@
"help": "Certain ARIA roles must contain particular children"
},
"all": [],
"any": ["aria-required-children", "aria-busy"],
"any": ["aria-required-children"],
"none": []
}
5 changes: 4 additions & 1 deletion locales/_template.json
Expand Up @@ -497,7 +497,10 @@
}
},
"aria-required-children": {
"pass": "Required ARIA children are present",
"pass": {
"default": "Required ARIA children are present",
"aria-busy": "Element has an aria-busy attribute, so it is allowed to omit required children"
},
"fail": {
"singular": "Required ARIA child role not present: ${data.values}",
"plural": "Required ARIA children role not present: ${data.values}",
Expand Down
4 changes: 3 additions & 1 deletion locales/da.json
Expand Up @@ -385,7 +385,9 @@
}
},
"aria-required-children": {
"pass": "Krævet ARIA-under-elementer er til stede",
"pass": {
"default": "Krævet ARIA-under-elementer er til stede"
},
"fail": {
"singular": "Krævet ARIA-under-elementers rolle er ikke til stede: ${data.values}",
"plural": "Krævet ARIA-under-elements rolle er ikke til stede: ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/de.json
Expand Up @@ -497,7 +497,9 @@
}
},
"aria-required-children": {
"pass": "Alle benötigten ARIA Kinder sind vorhanden.",
"pass": {
"default": "Alle benötigten ARIA Kinder sind vorhanden."
},
"fail": {
"singular": "Benötigte ARIA Kindrolle nicht vorhanden: ${data.values}",
"plural": "Benötigte ARIA Kindrollen nicht vorhanden: ${data.values}",
Expand Down
4 changes: 3 additions & 1 deletion locales/el.json
Expand Up @@ -473,7 +473,9 @@
}
},
"aria-required-children": {
"pass": "Οι απαιτούμενοι γόνοι ARIA είναι παρόντες",
"pass": {
"default": "Οι απαιτούμενοι γόνοι ARIA είναι παρόντες"
},
"fail": {
"singular": "Ο απαιτούμενος θυγατρικός ρόλος ARIA δεν υπάρχει: ${data.values}",
"plural": "Οι απαιτούμενοι θυγατρικόι ρόλοι ARIA δεν υπάρχουν: ${data.values}",
Expand Down
4 changes: 3 additions & 1 deletion locales/es.json
Expand Up @@ -376,7 +376,9 @@
}
},
"aria-required-children": {
"pass": "Los hijos ARIA requeridos están presentes",
"pass": {
"default": "Los hijos ARIA requeridos están presentes"
},
"fail": {
"singular": "Rol de hijos requerido en ARIA no presente: ${data.values}",
"plural": "Rol de hijo requerido en ARIA no presente: ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/eu.json
Expand Up @@ -376,7 +376,9 @@
}
},
"aria-required-children": {
"pass": "Eskatutako ARIA semeak bertan daude",
"pass": {
"default": "Eskatutako ARIA semeak bertan daude"
},
"fail": {
"singular": "Esktatutako ARIARren semeak ez daude : ${data.values}",
"plural": "Esktatutako ARIARren semeaez dago : ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/fr.json
Expand Up @@ -457,7 +457,9 @@
}
},
"aria-required-children": {
"pass": "Les descendants ARIA requis sont présents",
"pass": {
"default": "Les descendants ARIA requis sont présents"
},
"fail": {
"singular": "Le descendant ARIA requis est manquant : ${data.values}",
"plural": "Les descendants ARIA requis sont manquants : ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/he.json
Expand Up @@ -465,7 +465,9 @@
}
},
"aria-required-children": {
"pass": "ילדי ARIA הדרושים נמצאים",
"pass": {
"default": "ילדי ARIA הדרושים נמצאים"
},
"fail": {
"singular": "תפקיד ילד ARIA הדרוש אינו נמצא: ${data.values}",
"plural": "תפקיד ילדי ARIA הדרושים אינם נמצאים: ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/ja.json
Expand Up @@ -497,7 +497,9 @@
}
},
"aria-required-children": {
"pass": "必須のARIA子ロールが存在します",
"pass": {
"default": "必須のARIA子ロールが存在します"
},
"fail": {
"singular": "必須のARIA子ロールが提供されていません: ${data.values}",
"plural": "必須のARIA子ロールが提供されていません: ${data.values}",
Expand Down
4 changes: 3 additions & 1 deletion locales/ko.json
Expand Up @@ -461,7 +461,9 @@
}
},
"aria-required-children": {
"pass": "필수 ARIA 하위 항목들이 존재합니다.",
"pass": {
"default": "필수 ARIA 하위 항목들이 존재합니다."
},
"fail": {
"singular": "필수 ARIA 하위 역할(role)이 없습니다: ${data.values}",
"plural": "필수 ARIA 하위 역할(role)들이 없습니다: ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/no_NB.json
Expand Up @@ -385,7 +385,9 @@
}
},
"aria-required-children": {
"pass": "Påkrevde ARIA-under-elementer er til stede",
"pass": {
"default": "Påkrevde ARIA-under-elementer er til stede"
},
"fail": {
"singular": "Påkrevd ARIA-under-element sin rolle er ikke til stede: ${data.values}",
"plural": "Påkrevde ARIA-under-elementer sine roller er ikke til stede: ${data.values}"
Expand Down
4 changes: 3 additions & 1 deletion locales/pl.json
Expand Up @@ -497,7 +497,9 @@
}
},
"aria-required-children": {
"pass": "Wymagane dzieci ARIA istnieją.",
"pass": {
"default": "Wymagane dzieci ARIA istnieją."
},
"fail": {
"singular": "Wymagana rola dziecka ARIA nie istnieje: ${data.values}.",
"plural": "Wymagane role dzieci ARIA nie istnieją: ${data.values",
Expand Down
4 changes: 3 additions & 1 deletion locales/pt_BR.json
Expand Up @@ -444,7 +444,9 @@
}
},
"aria-required-children": {
"pass": "Os ARIA filhos necessários estão presentes",
"pass": {
"default": "Os ARIA filhos necessários estão presentes"
},
"fail": {
"singular": "Função ARIA filha necessária ausente: ${data.values}",
"plural": "Funções ARIA filhas necessárias ausentes: ${data.values}"
Expand Down
55 changes: 55 additions & 0 deletions test/checks/aria/required-children.js
Expand Up @@ -111,6 +111,53 @@ describe('aria-required-children', () => {
assert.deepEqual(checkContext._data, ['listitem']);
});

it('should pass when missing required children but aria-busy', () => {
const params = checkSetup(
'<div id="target" role="list" aria-busy="true"><span>Item 1</span></div>'
);
assert.isTrue(requiredChildrenCheck.apply(checkContext, params));

assert.deepEqual(checkContext._data, { messageKey: 'aria-busy' });
});

it('should treat aria-busy="false" as not aria-busy', () => {
const params = checkSetup(
'<div id="target" role="list" aria-busy="false"><span>Item 1</span></div>'
);
assert.isFalse(requiredChildrenCheck.apply(checkContext, params));
});

it('should treat valueless aria-busy as not aria-busy', () => {
const params = checkSetup(
'<div id="target" role="list" aria-busy><span>Item 1</span></div>'
);
assert.isFalse(requiredChildrenCheck.apply(checkContext, params));
});

it('should fail list with an unallowed child', () => {
const params = checkSetup(`
<div id="target" role="list"><div role="tabpanel"></div></div>
`);
assert.isFalse(requiredChildrenCheck.apply(checkContext, params));

assert.deepEqual(checkContext._data, {
messageKey: 'unallowed',
values: '[role=tabpanel]'
});
});

it('should fail list with an unallowed child, even if aria-busy="true"', () => {
const params = checkSetup(`
<div id="target" role="list" aria-busy="true"><div role="tabpanel"></div></div>
`);
assert.isFalse(requiredChildrenCheck.apply(checkContext, params));

assert.deepEqual(checkContext._data, {
messageKey: 'unallowed',
values: '[role=tabpanel]'
});
});

it('should fail when list has intermediate child with role that is not a required role', () => {
const params = checkSetup(
'<div id="target" role="list"><div role="tabpanel"><div role="listitem">List item 1</div></div></div>'
Expand Down Expand Up @@ -378,6 +425,14 @@ describe('aria-required-children', () => {
assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));
});

it('should return true if aria-busy preempts a reviewEmpty case', () => {
const params = checkSetup(
'<div role="grid" id="target" aria-busy="true"></div>',
{ reviewEmpty: ['grid'] }
);
assert.isTrue(requiredChildrenCheck.apply(checkContext, params));
});

it('should return undefined when the element has empty children', () => {
const params = checkSetup(
'<div role="listbox" id="target"><div></div></div>',
Expand Down
Expand Up @@ -114,6 +114,10 @@
<div aria-busy="true"></div>
</div>

<div role="list" id="fail13" aria-busy="true">
<div role="alert">unallowed role</div>
</div>

<div role="doc-bibliography" id="inapplicable1"></div>
<div role="doc-endnotes" id="inapplicable2"></div>
<div role="radiogroup" id="inapplicable3">
Expand Down
Expand Up @@ -13,7 +13,8 @@
["#fail9"],
["#fail10"],
["#fail11"],
["#fail12"]
["#fail12"],
["#fail13"]
],
"passes": [
["#pass1"],
Expand Down

0 comments on commit 591607d

Please sign in to comment.