Skip to content

Commit b971caf

Browse files
authored
fix(get-role): handle presentation role inheritance for vnodes with no parent (#3801)
1 parent 6d01604 commit b971caf

File tree

5 files changed

+98
-8
lines changed

5 files changed

+98
-8
lines changed

lib/commons/aria/get-role.js

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ function getInheritedRole(vNode, explicitRoleOptions) {
5454
// if we can't look at the parent then we can't know if the node
5555
// inherits the presentational role or not
5656
if (!vNode.parent) {
57+
if (!vNode.actualNode) {
58+
return null;
59+
}
60+
5761
throw new ReferenceError(
5862
'Cannot determine role presentational inheritance of a required parent outside the current scope.'
5963
);

lib/rules/empty-table-header.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"id": "empty-table-header",
3-
"selector": "th, [role=\"rowheader\"], [role=\"columnheader\"]",
3+
"selector": "th:not([role]), [role=\"rowheader\"], [role=\"columnheader\"]",
44
"tags": ["cat.name-role-value", "best-practice"],
55
"metadata": {
66
"description": "Ensures table headers have discernible text",

test/commons/aria/get-role.js

+22
Original file line numberDiff line numberDiff line change
@@ -412,4 +412,26 @@ describe('aria.getRole', function () {
412412
assert.isNull(aria.getRole(node, { noPresentational: true }));
413413
});
414414
});
415+
416+
describe('SerialVirtualNode', function () {
417+
it('works with the SerialVirtualNode', function () {
418+
var vNode = new axe.SerialVirtualNode({
419+
nodeName: 'div',
420+
attributes: {
421+
role: 'button'
422+
}
423+
});
424+
assert.equal(aria.getRole(vNode), 'button');
425+
});
426+
427+
it('does not throw for missing parent in presentational role inheritance', function () {
428+
var vNode = new axe.SerialVirtualNode({
429+
nodeName: 'li'
430+
});
431+
432+
assert.doesNotThrow(function () {
433+
assert.equal(aria.getRole(vNode), 'listitem');
434+
});
435+
});
436+
});
415437
});

test/integration/rules/empty-table-header/empty-table-header.html

+6
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,11 @@
4545
</td>
4646
</tr>
4747
</table>
48+
49+
<table>
50+
<tr>
51+
<th id="ignore1" role="spinbutton">rowheader with a role</th>
52+
</tr>
53+
</table>
4854
</body>
4955
</html>

test/integration/virtual-rules/empty-table-header.js

+65-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,61 @@
11
describe('empty-table-header virtual-rule', function () {
2-
it('should incomplete when children are missing', function () {
2+
it('should fail when children contain no visible text', function () {
33
var thNode = new axe.SerialVirtualNode({
44
nodeName: 'th'
55
});
66
thNode.children = [];
77

88
var results = axe.runVirtualRule('empty-table-header', thNode);
99

10+
assert.lengthOf(results.passes, 0);
11+
assert.lengthOf(results.violations, 1);
12+
assert.lengthOf(results.incomplete, 0);
13+
});
14+
15+
it('should incomplete when children are missing', function () {
16+
var thNode = new axe.SerialVirtualNode({
17+
nodeName: 'th'
18+
});
19+
20+
var results = axe.runVirtualRule('empty-table-header', thNode);
21+
1022
assert.lengthOf(results.passes, 0);
1123
assert.lengthOf(results.violations, 0);
1224
assert.lengthOf(results.incomplete, 1);
1325
});
1426

27+
it('should fail for role=rowheader', function () {
28+
var vNode = new axe.SerialVirtualNode({
29+
nodeName: 'div',
30+
attributes: {
31+
role: 'rowheader'
32+
}
33+
});
34+
vNode.children = [];
35+
36+
var results = axe.runVirtualRule('empty-table-header', vNode);
37+
38+
assert.lengthOf(results.passes, 0);
39+
assert.lengthOf(results.violations, 1);
40+
assert.lengthOf(results.incomplete, 0);
41+
});
42+
43+
it('should fail for role=columnheader', function () {
44+
var vNode = new axe.SerialVirtualNode({
45+
nodeName: 'div',
46+
attributes: {
47+
role: 'columnheader'
48+
}
49+
});
50+
vNode.children = [];
51+
52+
var results = axe.runVirtualRule('empty-table-header', vNode);
53+
54+
assert.lengthOf(results.passes, 0);
55+
assert.lengthOf(results.violations, 1);
56+
assert.lengthOf(results.incomplete, 0);
57+
});
58+
1559
it('should pass with a table header', function () {
1660
var tableNode = new axe.SerialVirtualNode({
1761
nodeName: 'table'
@@ -137,19 +181,33 @@ describe('empty-table-header virtual-rule', function () {
137181
assert.lengthOf(results.incomplete, 0);
138182
});
139183

140-
it('should fail if table header has no child nodes', function () {
141-
var node = new axe.SerialVirtualNode({
184+
it('should be inapplicable when the th has role of cell', function () {
185+
var table = new axe.SerialVirtualNode({
186+
nodeName: 'table'
187+
});
188+
189+
var tr = new axe.SerialVirtualNode({
190+
nodeName: 'tr'
191+
});
192+
193+
var th = new axe.SerialVirtualNode({
142194
nodeName: 'th',
143195
attributes: {
144-
role: 'rowheader'
196+
role: 'cell'
145197
}
146198
});
147-
node.children = [];
148199

149-
var results = axe.runVirtualRule('empty-table-header', node);
200+
tr.children = [th];
201+
tr.parent = table;
202+
th.parent = tr;
203+
th.children = [];
204+
table.children = [tr];
205+
206+
var results = axe.runVirtualRule('empty-table-header', th);
150207

151208
assert.lengthOf(results.passes, 0);
152-
assert.lengthOf(results.violations, 1);
209+
assert.lengthOf(results.violations, 0);
153210
assert.lengthOf(results.incomplete, 0);
211+
assert.lengthOf(results.inapplicable, 1);
154212
});
155213
});

0 commit comments

Comments
 (0)