-
Notifications
You must be signed in to change notification settings - Fork 779
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(new-rule): aria-braille-equivalent finds incorrect uses of aria-…
…braille attributes (#4107) * feat(new-rule): aria-braille-equivalent finds incorrect uses of aria-braille attributes * Fix test * More tests * Resolve feedback
- Loading branch information
1 parent
d417630
commit 6260a2f
Showing
14 changed files
with
346 additions
and
2 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,22 @@ | ||
import { sanitize, accessibleTextVirtual } from '../../commons/text'; | ||
|
||
/** | ||
* Check that if aria-braillelabel is not empty, the element has an accessible text | ||
* @memberof checks | ||
* @return {Boolean} | ||
*/ | ||
export default function brailleLabelEquivalentEvaluate( | ||
node, | ||
options, | ||
virtualNode | ||
) { | ||
const brailleLabel = virtualNode.attr('aria-braillelabel') ?? ''; | ||
if (!brailleLabel.trim()) { | ||
return true; | ||
} | ||
try { | ||
return sanitize(accessibleTextVirtual(virtualNode)) !== ''; | ||
} catch { | ||
return undefined; | ||
} | ||
} |
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": "braille-label-equivalent", | ||
"evaluate": "braille-label-equivalent-evaluate", | ||
"metadata": { | ||
"impact": "serious", | ||
"messages": { | ||
"pass": "aria-braillelabel is used on an element with accessible text", | ||
"fail": "aria-braillelabel is used on an element with no accessible text", | ||
"incomplete": "Unable to compute accessible text" | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
lib/checks/aria/braille-roledescription-equivalent-evaluate.js
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,29 @@ | ||
import { sanitize } from '../../commons/text'; | ||
|
||
/** | ||
* Check that if aria-brailleroledescription is not empty, | ||
* the element has a non-empty aria-roledescription | ||
* @memberof checks | ||
* @return {Boolean} | ||
*/ | ||
export default function brailleRoleDescriptionEquivalentEvaluate( | ||
node, | ||
options, | ||
virtualNode | ||
) { | ||
const brailleRoleDesc = virtualNode.attr('aria-brailleroledescription') ?? ''; | ||
if (sanitize(brailleRoleDesc) === '') { | ||
return true; | ||
} | ||
const roleDesc = virtualNode.attr('aria-roledescription'); | ||
if (typeof roleDesc !== 'string') { | ||
this.data({ messageKey: 'noRoleDescription' }); | ||
return false; | ||
} | ||
|
||
if (sanitize(roleDesc) === '') { | ||
this.data({ messageKey: 'emptyRoleDescription' }); | ||
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,14 @@ | ||
{ | ||
"id": "braille-roledescription-equivalent", | ||
"evaluate": "braille-roledescription-equivalent-evaluate", | ||
"metadata": { | ||
"impact": "serious", | ||
"messages": { | ||
"pass": "aria-brailleroledescription is not used on an element with no accessible text", | ||
"fail": { | ||
"noRoleDescription": "aria-brailleroledescription is used on an element with no aria-roledescription", | ||
"emptyRoleDescription": "aria-brailleroledescription is used on an element with an empty aria-roledescription" | ||
} | ||
} | ||
} | ||
} |
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": "aria-braille-equivalent", | ||
"selector": "[aria-brailleroledescription], [aria-braillelabel]", | ||
"tags": ["cat.aria", "wcag2a", "wcag412", "EN-301-549", "EN-9.4.1.2"], | ||
"metadata": { | ||
"description": "Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent", | ||
"help": "aria-braille attributes must have a non-braille equivalent" | ||
}, | ||
"all": ["braille-roledescription-equivalent", "braille-label-equivalent"], | ||
"any": [], | ||
"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
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,51 @@ | ||
describe('braille-label-equivalent tests', () => { | ||
const { checkSetup, getCheckEvaluate } = axe.testUtils; | ||
const checkContext = axe.testUtils.MockCheckContext(); | ||
const checkEvaluate = getCheckEvaluate('braille-label-equivalent'); | ||
|
||
afterEach(() => { | ||
checkContext.reset(); | ||
}); | ||
|
||
it('returns true without aria-braillelabel', () => { | ||
const params = checkSetup('<img id="target" alt="" />'); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns true when aria-braillelabel is empty', () => { | ||
const params = checkSetup( | ||
'<img id="target" alt="" aria-braillelabel="" />' | ||
); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns true when aria-braillelabel is whitespace-only', () => { | ||
const params = checkSetup( | ||
'<img id="target" alt="" aria-braillelabel=" \r\t\n " />' | ||
); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
describe('when aria-braillelabel has text', () => { | ||
it('returns false when the accessible name is empty', () => { | ||
const params = checkSetup(` | ||
<img id="target" alt="" aria-braillelabel="foo" /> | ||
`); | ||
assert.isFalse(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns false when the accessible name has only whitespace', () => { | ||
const params = checkSetup(` | ||
<img id="target" alt=" \r\t\n " aria-braillelabel="foo" /> | ||
`); | ||
assert.isFalse(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns true when the accessible name is not empty', () => { | ||
const params = checkSetup(` | ||
<img id="target" alt="foo" aria-braillelabel="foo" /> | ||
`); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
}); | ||
}); |
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,80 @@ | ||
describe('braille-roledescription-equivalent tests', () => { | ||
const { checkSetup, getCheckEvaluate } = axe.testUtils; | ||
const checkContext = axe.testUtils.MockCheckContext(); | ||
const checkEvaluate = getCheckEvaluate('braille-roledescription-equivalent'); | ||
|
||
afterEach(() => { | ||
checkContext.reset(); | ||
}); | ||
|
||
it('returns true without aria-brailleroledescription', () => { | ||
const params = checkSetup('<div id="target"></div>'); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns true when aria-brailleroledecription is empty', () => { | ||
const params = checkSetup( | ||
'<div id="target" aria-brailleroledescription=""></div>' | ||
); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
it('returns true when aria-brailleroledecription is whitespace-only', () => { | ||
const params = checkSetup( | ||
'<div id="target" aria-brailleroledescription=" \r\t\n "></div>' | ||
); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
|
||
describe('when aria-brailleroledescription has text', () => { | ||
it('returns false without aria-roledescription', () => { | ||
const params = checkSetup(` | ||
<div | ||
id="target" | ||
aria-brailleroledescription="foo" | ||
></div> | ||
`); | ||
assert.isFalse(checkEvaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { messageKey: 'noRoleDescription' }); | ||
}); | ||
|
||
it('returns false when aria-roledescription is empty', () => { | ||
const params = checkSetup(` | ||
<div | ||
id="target" | ||
aria-roledescription="" | ||
aria-brailleroledescription="foo" | ||
></div> | ||
`); | ||
assert.isFalse(checkEvaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { | ||
messageKey: 'emptyRoleDescription' | ||
}); | ||
}); | ||
|
||
it('returns false when aria-roledescription has only whitespace', () => { | ||
const params = checkSetup(` | ||
<div | ||
id="target" | ||
aria-roledescription=" \r\t\n " | ||
aria-brailleroledescription="foo" | ||
></div> | ||
`); | ||
assert.isFalse(checkEvaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { | ||
messageKey: 'emptyRoleDescription' | ||
}); | ||
}); | ||
|
||
it('returns true when aria-roledescription is not empty', () => { | ||
const params = checkSetup(` | ||
<div | ||
id="target" | ||
aria-roledescription="foo" | ||
aria-brailleroledescription="foo" | ||
></div> | ||
`); | ||
assert.isTrue(checkEvaluate.apply(checkContext, params)); | ||
}); | ||
}); | ||
}); |
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
27 changes: 27 additions & 0 deletions
27
test/integration/rules/aria-braille-equivalent/aria-braille-equivalent.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,27 @@ | ||
<button id="pass1" aria-braillelabel="hello">Hello</button> | ||
<button id="pass2" aria-braillelabel=""></button> | ||
<button id="fail1" aria-braillelabel="hello"></button> | ||
|
||
<aside | ||
id="pass3" | ||
aria-roledescription="table of contents" | ||
aria-brailleroledescription="" | ||
></aside> | ||
|
||
<aside | ||
id="pass4" | ||
aria-roledescription="table of contents" | ||
aria-brailleroledescription="table of contents" | ||
></aside> | ||
|
||
<aside | ||
id="pass5" | ||
aria-roledescription="" | ||
aria-brailleroledescription="" | ||
></aside> | ||
|
||
<aside | ||
id="fail2" | ||
aria-roledescription="" | ||
aria-brailleroledescription="table of contents" | ||
></aside> |
6 changes: 6 additions & 0 deletions
6
test/integration/rules/aria-braille-equivalent/aria-braille-equivalent.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,6 @@ | ||
{ | ||
"description": "aria-braille-equivalent tests", | ||
"rule": "aria-braille-equivalent", | ||
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"]], | ||
"violations": [["#fail1"], ["#fail2"]] | ||
} |
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,74 @@ | ||
describe('aria-braille-equivalent virtual-rule', () => { | ||
afterEach(() => { | ||
axe.reset(); | ||
}); | ||
|
||
it('passes when aria-braillelabel is not empty', () => { | ||
const results = axe.runVirtualRule('aria-braille-equivalent', { | ||
nodeName: 'img', | ||
attributes: { | ||
alt: 'Hello world', | ||
'aria-braillelabel': 'Hello world' | ||
} | ||
}); | ||
|
||
assert.lengthOf(results.passes, 1); | ||
assert.lengthOf(results.violations, 0); | ||
assert.lengthOf(results.incomplete, 0); | ||
}); | ||
|
||
it('fails when accessible text is empty but braille label is not', () => { | ||
const results = axe.runVirtualRule('aria-braille-equivalent', { | ||
nodeName: 'img', | ||
attributes: { | ||
alt: '', | ||
'aria-braillelabel': 'hello world' | ||
} | ||
}); | ||
|
||
assert.lengthOf(results.passes, 0); | ||
assert.lengthOf(results.violations, 1); | ||
assert.lengthOf(results.incomplete, 0); | ||
}); | ||
|
||
it('passes when roledescription and brailleroledescription are not empty', () => { | ||
const results = axe.runVirtualRule('aria-braille-equivalent', { | ||
nodeName: 'div', | ||
attributes: { | ||
'aria-roledescription': 'Hello world', | ||
'aria-brailleroledescription': 'Hello world' | ||
} | ||
}); | ||
|
||
assert.lengthOf(results.passes, 1); | ||
assert.lengthOf(results.violations, 0); | ||
assert.lengthOf(results.incomplete, 0); | ||
}); | ||
|
||
it('fails when roledescription is empty but brailleroledescription is not', () => { | ||
const results = axe.runVirtualRule('aria-braille-equivalent', { | ||
nodeName: 'div', | ||
attributes: { | ||
'aria-roledescription': '', | ||
'aria-brailleroledescription': 'Hello world' | ||
} | ||
}); | ||
|
||
assert.lengthOf(results.passes, 0); | ||
assert.lengthOf(results.violations, 1); | ||
assert.lengthOf(results.incomplete, 0); | ||
}); | ||
|
||
it('incompletes if the subtree fails to compute with aria-braillelabel', () => { | ||
const results = axe.runVirtualRule('aria-braille-equivalent', { | ||
nodeName: 'button', | ||
attributes: { | ||
'aria-braillelabel': 'Hello world' | ||
} | ||
}); | ||
|
||
assert.lengthOf(results.passes, 0); | ||
assert.lengthOf(results.violations, 0); | ||
assert.lengthOf(results.incomplete, 1); | ||
}); | ||
}); |