Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(get-role): handle presentation role inheritance for vnodes with no parent #3801

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/checks/label/label-content-name-mismatch-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ function curateString(str) {

function labelContentNameMismatchEvaluate(node, options, virtualNode) {
const pixelThreshold = options?.pixelThreshold;
const occurrenceThreshold = options?.occurrenceThreshold ?? options?.occuranceThreshold;
const occurrenceThreshold =
options?.occurrenceThreshold ?? options?.occuranceThreshold;

const accText = accessibleText(node).toLowerCase();
if (isHumanInterpretable(accText) < 1) {
Expand Down
4 changes: 4 additions & 0 deletions lib/commons/aria/get-role.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ function getInheritedRole(vNode, explicitRoleOptions) {
// if we can't look at the parent then we can't know if the node
// inherits the presentational role or not
if (!vNode.parent) {
if (!vNode.actualNode) {
return null;
}

throw new ReferenceError(
'Cannot determine role presentational inheritance of a required parent outside the current scope.'
);
Expand Down
3 changes: 2 additions & 1 deletion lib/commons/text/accessible-text-virtual.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ function shouldIgnoreHidden(virtualNode, context) {
*/
function shouldIgnoreIconLigature(virtualNode, context) {
const { ignoreIconLigature, pixelThreshold } = context;
const occurrenceThreshold = context.occurrenceThreshold ?? context.occuranceThreshold;
const occurrenceThreshold =
context.occurrenceThreshold ?? context.occuranceThreshold;
if (virtualNode.props.nodeType !== 3 || !ignoreIconLigature) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/empty-table-header.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "empty-table-header",
"selector": "th, [role=\"rowheader\"], [role=\"columnheader\"]",
"selector": "th:not([role]), [role=\"rowheader\"], [role=\"columnheader\"]",
"tags": ["cat.name-role-value", "best-practice"],
"metadata": {
"description": "Ensures table headers have discernible text",
Expand Down
22 changes: 22 additions & 0 deletions test/commons/aria/get-role.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,4 +412,26 @@ describe('aria.getRole', function () {
assert.isNull(aria.getRole(node, { noPresentational: true }));
});
});

describe('SerialVirtualNode', function () {
it('works with the SerialVirtualNode', function () {
var vNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'button'
}
});
assert.equal(aria.getRole(vNode), 'button');
});

it('does not throw for missing parent in presentational role inheritance', function () {
var vNode = new axe.SerialVirtualNode({
nodeName: 'li'
});

assert.doesNotThrow(function () {
assert.equal(aria.getRole(vNode), 'listitem');
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ <h1 id="pass-h1-role-doc-subtitle" role="doc-subtitle"></h1>
role="combobox"
type="button"
aria-expanded="true"
id="pass-button-role-combobox">ok</button>
id="pass-button-role-combobox"
>
ok
</button>
<input
value="some label"
role="checkbox"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,11 @@
</td>
</tr>
</table>

<table>
<tr>
<th id="ignore1" role="spinbutton">rowheader with a role</th>
</tr>
</table>
</body>
</html>
72 changes: 65 additions & 7 deletions test/integration/virtual-rules/empty-table-header.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,61 @@
describe('empty-table-header virtual-rule', function () {
it('should incomplete when children are missing', function () {
it('should fail when children contain no visible text', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a couple more tests:

With empty content:

  1. it passes when the table has role=none
  2. it passes when the th has role=cell

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it passes when the table has role=none

Steve said that the following test needs to be a new ticket as it is not a related issue. He was going to chat with you today about it I think.

This test currently fails:

it('should pass with a table of role none', function () {
	var table = new axe.SerialVirtualNode({
		nodeName: 'table',
		attributes: {
			role: 'none'
		}
	});

	var tr = new axe.SerialVirtualNode({
		nodeName: 'tr',
	});

	var th = new axe.SerialVirtualNode({
		nodeName: 'th',
	});

	tr.children = [th];
	tr.parent = table;
	th.parent = tr;
	th.children = [];
	table.children = [tr];

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

	assert.lengthOf(results.passes, 1);
	assert.lengthOf(results.violations, 0);
	assert.lengthOf(results.incomplete, 0);
});

it passes when the th has role=cell

Test has been added and passes.

var thNode = new axe.SerialVirtualNode({
nodeName: 'th'
});
thNode.children = [];

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

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should incomplete when children are missing', function () {
var thNode = new axe.SerialVirtualNode({
nodeName: 'th'
});

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

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 1);
});

it('should fail for role=rowheader', function () {
var vNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'rowheader'
}
});
vNode.children = [];

var results = axe.runVirtualRule('empty-table-header', vNode);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for role=columnheader', function () {
var vNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'columnheader'
}
});
vNode.children = [];

var results = axe.runVirtualRule('empty-table-header', vNode);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should pass with a table header', function () {
var tableNode = new axe.SerialVirtualNode({
nodeName: 'table'
Expand Down Expand Up @@ -137,19 +181,33 @@ describe('empty-table-header virtual-rule', function () {
assert.lengthOf(results.incomplete, 0);
});

it('should fail if table header has no child nodes', function () {
var node = new axe.SerialVirtualNode({
it('should be inapplicable when the th has role of cell', function () {
var table = new axe.SerialVirtualNode({
nodeName: 'table'
});

var tr = new axe.SerialVirtualNode({
nodeName: 'tr'
});

var th = new axe.SerialVirtualNode({
nodeName: 'th',
attributes: {
role: 'rowheader'
role: 'cell'
}
});
node.children = [];

var results = axe.runVirtualRule('empty-table-header', node);
tr.children = [th];
tr.parent = table;
th.parent = tr;
th.children = [];
table.children = [tr];

var results = axe.runVirtualRule('empty-table-header', th);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
assert.lengthOf(results.inapplicable, 1);
});
});