-
Notifications
You must be signed in to change notification settings - Fork 765
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rule for label and name from content mismatch
- Loading branch information
Showing
10 changed files
with
431 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
const { | ||
text: { | ||
accessibleText, | ||
visible // get visible text | ||
} | ||
} = axe.commons; | ||
|
||
/** | ||
* Note: | ||
* `label-content-name-mismatch-matches` ignore `node` | ||
* if there is no `accessibleText` or `textContent` | ||
*/ | ||
const accText = accessibleText(node).toLowerCase(); | ||
const visibleTextContent = visible(node).toLowerCase(); | ||
|
||
/** | ||
* if `textContent` is not part of `accessibleText` -> fail | ||
*/ | ||
|
||
if (!accText.includes(visibleTextContent)) { | ||
return false; | ||
} | ||
|
||
// -> pass | ||
return true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"id": "label-content-name-mismatch", | ||
"evaluate": "label-content-name-mismatch.js", | ||
"metadata": { | ||
"impact": "serious", | ||
"messages": { | ||
"pass": "Element contains visible text as part of it's accessible name", | ||
"fail": "The visible text of the element is different from it's accessible name" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* Applicability: | ||
* Rule applies to any element that has | ||
* a) a semantic role that is `widget` that supports name from content | ||
* b) has visible text content | ||
* c) has an accessible name (eg: `aria-label`) | ||
*/ | ||
const { | ||
aria: { | ||
arialabelText, | ||
arialabelledbyText, | ||
getRole, | ||
getRolesWithNameFromContents | ||
}, | ||
text: { | ||
accessibleText, | ||
visible: visibleText // -> get visible text | ||
} | ||
} = axe.commons; | ||
|
||
const role = getRole(node); | ||
/** | ||
* if no `role` - ignore `node` | ||
*/ | ||
if (!role) { | ||
return false; | ||
} | ||
|
||
const rolesWithNameFromContents = getRolesWithNameFromContents(); | ||
/** | ||
* if no `rolesWithNameFromContents` - ignore `node` | ||
*/ | ||
if (!rolesWithNameFromContents) { | ||
return false; | ||
} | ||
|
||
/** | ||
* if no `aria-label` or `aria-labelledby` attribute - ignore `node` | ||
*/ | ||
const isAriaLabel = arialabelText(node); | ||
const isAriaLabelledBy = arialabelledbyText(node); | ||
if (!isAriaLabel && !isAriaLabelledBy) { | ||
return false; | ||
} | ||
|
||
const visibleTextContent = visibleText(node); | ||
/** | ||
* if no `contentText` or contains `unicode` - ignore `node` | ||
*/ | ||
if (!visibleTextContent || /[^\u0000-\u00ff]/.test(visibleTextContent)) { | ||
return false; | ||
} | ||
|
||
const accText = accessibleText(node); | ||
/** | ||
* if no `accessibleText` or contains `unicode` - ignore `node` | ||
*/ | ||
if (!accText || /[^\u0000-\u00ff]/.test(accText)) { | ||
return false; | ||
} | ||
|
||
return true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"id": "label-content-name-mismatch", | ||
"matches": "label-content-name-mismatch-matches.js", | ||
"tags": ["wcag21a", "wcag253", "experimental"], | ||
"metadata": { | ||
"description": "Ensures that elements labeled through their content must have their visible text as part of their accessible name", | ||
"help": "Elements must have their visible text as part of their accessible name" | ||
}, | ||
"all": [], | ||
"any": ["label-content-name-mismatch"], | ||
"none": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
describe('label-content-name-mismatch tests', function() { | ||
'use strict'; | ||
|
||
var fixture = document.getElementById('fixture'); | ||
var fixtureSetup = axe.testUtils.fixtureSetup; | ||
var check = checks['label-content-name-mismatch']; | ||
var options = undefined; | ||
|
||
afterEach(function() { | ||
fixture.innerHTML = ''; | ||
}); | ||
|
||
it('returns true when visible text and accessible name (`aria-label`) matches (text sanitized)', function() { | ||
var target = fixtureSetup( | ||
'<div id="target" role="link" aria-label="next page %20 ">next page</div>', | ||
'div#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isTrue(actual); | ||
}); | ||
|
||
it('returns true when visible text and accessible name (`aria-label`) matches (character insensitive)', function() { | ||
var target = fixtureSetup( | ||
'<div id="target" role="link" aria-label="Next Page">next pAge</div>', | ||
'div#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isTrue(actual); | ||
}); | ||
|
||
it('returns true when visible text and accessible name (`aria-labelledby`) matches (character insensitive & text sanitized)', function() { | ||
var target = fixtureSetup( | ||
'<div id="target" aria-labelledby="yourLabel">UNTIL THE VeRy EnD</div>' + | ||
'<div id="yourLabel">uNtIl the very end %20</div>', | ||
'div#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isTrue(actual); | ||
}); | ||
|
||
it('returns true when visible text is contained in the accessible name', function() { | ||
var target = fixtureSetup( | ||
'<button id="target" name="link" aria-label="Next Page in the list">Next Page</button>', | ||
'#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isTrue(actual); | ||
}); | ||
|
||
it('returns false when visible text doesn’t match accessible name', function() { | ||
var target = fixtureSetup( | ||
'<div id="target" role="link" aria-label="OK">Next</div>', | ||
'#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isFalse(actual); | ||
}); | ||
|
||
it('returns false when not all of visible text is included in accessible name', function() { | ||
var target = fixtureSetup( | ||
'<button id="target" name="link" aria-label="the full">The full label</button>', | ||
'#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isFalse(actual); | ||
}); | ||
|
||
it('returns false when element has non-matching accessible name (`aria-labelledby`) and text content', function() { | ||
var target = fixtureSetup( | ||
'<div role="button" id="target" aria-labelledby="foo">some content</div>' + | ||
'<div id="foo">123</div>', | ||
'div#target' | ||
); | ||
var actual = check.evaluate( | ||
target, | ||
options, | ||
axe.utils.getNodeFromTree(axe._tree[0], target) | ||
); | ||
assert.isFalse(actual); | ||
}); | ||
}); |
20 changes: 20 additions & 0 deletions
20
test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<!-- Pass --> | ||
<div id='pass1' role="link" aria-label="next page ">next page</div> | ||
<div id='pass2' role="link" aria-label="Next Page">next page</div> | ||
<button id='pass3' name="link" aria-label="Next Page in the list">Next Page</button> | ||
<div id="pass4" role="link" aria-label="next page %20 ">next page</div> | ||
<div id="pass5" role="link" aria-label="Next Page">next pAge</div> | ||
|
||
<div role='button' id="pass6" aria-labelledby="yourLabel">UNTIL THE VeRy EnD</div> | ||
<div id="yourLabel">uNtIl the very end %20</div> | ||
|
||
<button id="pass7" name="link" aria-label="Next Page in the list">Next Page</button> | ||
|
||
<!-- Fail --> | ||
<div id="fail1" role="link" aria-label="OK">Next</div> | ||
<button id="fail2" name="link" aria-label="the full">The full label</button> | ||
<div id="fail3" role="link" aria-label="OK">Next</div> | ||
<button id="fail4" name="link" aria-label="the full">The full label</button> | ||
|
||
<div role="button" id="fail5" aria-labelledby="foo">some content</div> | ||
<div id="foo">123</div> |
14 changes: 14 additions & 0 deletions
14
test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"description": "label-content-name-mismatch tests", | ||
"rule": "label-content-name-mismatch", | ||
"violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"], ["#fail5"]], | ||
"passes": [ | ||
["#pass1"], | ||
["#pass2"], | ||
["#pass3"], | ||
["#pass4"], | ||
["#pass5"], | ||
["#pass6"], | ||
["#pass7"] | ||
] | ||
} |
Oops, something went wrong.