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

refactor(table-duplicate-name): use virtual node #3604

Merged
merged 9 commits into from
Aug 18, 2022
Merged
2 changes: 1 addition & 1 deletion doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Rules that do not necessarily conform to WCAG success criterion but are industry
| [scope-attr-valid](https://dequeuniversity.com/rules/axe/4.4/scope-attr-valid?application=RuleDescription) | Ensures the scope attribute is used correctly on tables | Moderate, Critical | cat.tables, best-practice | failure | |
| [skip-link](https://dequeuniversity.com/rules/axe/4.4/skip-link?application=RuleDescription) | Ensure all skip links have a focusable target | Moderate | cat.keyboard, best-practice | failure, needs review | |
| [tabindex](https://dequeuniversity.com/rules/axe/4.4/tabindex?application=RuleDescription) | Ensures tabindex attribute values are not greater than 0 | Serious | cat.keyboard, best-practice | failure | |
| [table-duplicate-name](https://dequeuniversity.com/rules/axe/4.4/table-duplicate-name?application=RuleDescription) | Ensure the <caption> element does not contain the same text as the summary attribute | Minor | cat.tables, best-practice | failure | |
| [table-duplicate-name](https://dequeuniversity.com/rules/axe/4.4/table-duplicate-name?application=RuleDescription) | Ensure the <caption> element does not contain the same text as the summary attribute | Minor | cat.tables, best-practice | failure, needs review | |

## WCAG 2.0 and 2.1 level AAA rules

Expand Down
31 changes: 21 additions & 10 deletions lib/checks/tables/same-caption-summary-evaluate.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { accessibleText } from '../../commons/text';

function sameCaptionSummaryEvaluate(node) {
// passing node.caption to accessibleText instead of using
// the logic in accessibleTextVirtual on virtualNode
return (
!!(node.summary && node.caption) &&
node.summary.toLowerCase() === accessibleText(node.caption).toLowerCase()
);
}
import { sanitize, subtreeText } from '../../commons/text';

export default sameCaptionSummaryEvaluate;

function sameCaptionSummaryEvaluate(node, options, virtualNode) {
if (virtualNode.children === undefined) {
return undefined;
}

var summary = virtualNode.attr('summary');
var captionNode = virtualNode.children.find(isCaptionNode);
var caption = captionNode ? sanitize(subtreeText(captionNode)) : false;

if (!caption || !summary) {
return false;
}

return sanitize(summary).toLowerCase() === sanitize(caption).toLowerCase();
}

function isCaptionNode(virtualNode) {
return virtualNode.props.nodeName === 'caption';
}
3 changes: 2 additions & 1 deletion lib/checks/tables/same-caption-summary.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"impact": "minor",
"messages": {
"pass": "Content of summary attribute and <caption> are not duplicated",
"fail": "Content of summary attribute and <caption> element are identical"
"fail": "Content of summary attribute and <caption> element are identical",
"incomplete": "Unable to determine if <table> element has a caption"
}
}
}
3 changes: 2 additions & 1 deletion locales/_template.json
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,8 @@
},
"same-caption-summary": {
"pass": "Content of summary attribute and <caption> are not duplicated",
"fail": "Content of summary attribute and <caption> element are identical"
"fail": "Content of summary attribute and <caption> element are identical",
"incomplete": "Unable to determine if <table> element has a caption"
},
"scope-value": {
"pass": "Scope attribute is used correctly",
Expand Down
18 changes: 8 additions & 10 deletions test/checks/tables/same-caption-summary.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
describe('same-caption-summary', function() {
describe('same-caption-summary', function () {
'use strict';

var fixture = document.getElementById('fixture');
var checkSetup = axe.testUtils.checkSetup;
var shadowCheckSetup = axe.testUtils.shadowCheckSetup;
var shadowSupport = axe.testUtils.shadowSupport;

var checkContext = axe.testUtils.MockCheckContext();

afterEach(function() {
fixture.innerHTML = '';
afterEach(function () {
checkContext.reset();
axe._tree = undefined;
});

it('should return false there is no caption', function() {
it('should return false there is no caption', function () {
var params = checkSetup(
'<table summary="hi" id="target"><tr><td></td></tr></table>'
);
Expand All @@ -26,7 +24,7 @@ describe('same-caption-summary', function() {
);
});

it('should return false there is no summary', function() {
it('should return false there is no summary', function () {
var params = checkSetup(
'<table id="target"><caption>Hi</caption><tr><td></td></tr></table>'
);
Expand All @@ -38,7 +36,7 @@ describe('same-caption-summary', function() {
);
});

it('should return false if summary and caption are different', function() {
it('should return false if summary and caption are different', function () {
var params = checkSetup(
'<table summary="bye" id="target"><caption>Hi</caption><tr><td></td></tr></table>'
);
Expand All @@ -50,7 +48,7 @@ describe('same-caption-summary', function() {
);
});

it('should return true if summary and caption are the same', function() {
it('should return true if summary and caption are the same', function () {
var params = checkSetup(
'<table summary="Hi" id="target"><caption>Hi</caption><tr><td></td></tr></table>'
);
Expand All @@ -62,7 +60,7 @@ describe('same-caption-summary', function() {
);
});

it('should return true if summary and caption are the same with mixed casing', function() {
it('should return true if summary and caption are the same with mixed casing', function () {
var params = checkSetup(
'<table summary="My Table" id="target">' +
'<caption> my table </caption>' +
Expand All @@ -84,7 +82,7 @@ describe('same-caption-summary', function() {

(shadowSupport.v1 ? it : xit)(
'should match slotted caption elements',
function() {
function () {
var params = shadowCheckSetup(
'<div>' +
'<span slot="caption">Caption</span>' +
Expand Down
138 changes: 138 additions & 0 deletions test/integration/virtual-rules/table-duplicate-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
describe('table-duplicate-name virtual-rule', function () {
it('should incomplete on table element with children undefined', function () {
var tableNode = new axe.SerialVirtualNode({
nodeName: 'table'
});
tableNode.children = undefined;

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

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

it('should pass on table element', function () {
var tableNode = new axe.SerialVirtualNode({
nodeName: 'table'
});

tableNode.children = [];

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

assert.lengthOf(results.passes, 1);
dbowling marked this conversation as resolved.
Show resolved Hide resolved
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should pass when table has empty summary', function () {
var tableNode = new axe.SerialVirtualNode({
nodeName: 'table',
attributes: {
summary: ''
}
});
tableNode.children = [];

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

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

it('should pass when table has empty caption and summary', function () {
var tableNode = new axe.SerialVirtualNode({
nodeName: 'table',
attributes: {
summary: ''
}
});

var captionNode = new axe.SerialVirtualNode({
nodeName: 'caption'
});
captionNode.parent = tableNode;

var textNode = new axe.SerialVirtualNode({
nodeName: '#text',
nodeType: 3,
nodeValue: ''
});
textNode.parent = captionNode;

captionNode.children = [textNode];
tableNode.children = [captionNode];

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

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

it('should fail when table summary and <caption> have the same text', function () {
var DUPLICATED_TEXT = 'foobar';

var tableNode = new axe.SerialVirtualNode({
nodeName: 'table',
attributes: {
summary: DUPLICATED_TEXT
}
});

var captionNode = new axe.SerialVirtualNode({
nodeName: 'caption'
});
captionNode.parent = tableNode;

var textNode = new axe.SerialVirtualNode({
nodeName: '#text',
nodeType: 3,
nodeValue: DUPLICATED_TEXT
});
textNode.parent = captionNode;

captionNode.children = [textNode];
tableNode.children = [captionNode];

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

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

it('should fail when table summary and <caption> have the same text, excluding whitespace', function () {
var DUPLICATED_TEXT = 'foobar';

var tableNode = new axe.SerialVirtualNode({
nodeName: 'table',
attributes: {
summary: ' ' + DUPLICATED_TEXT
}
});

var captionNode = new axe.SerialVirtualNode({
nodeName: 'caption'
});
captionNode.parent = tableNode;

var textNode = new axe.SerialVirtualNode({
nodeName: '#text',
nodeType: 3,
nodeValue: ' \t ' + DUPLICATED_TEXT
});
textNode.parent = captionNode;

captionNode.children = [textNode];
tableNode.children = [captionNode];

var results = axe.runVirtualRule('table-duplicate-name', tableNode);

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