diff --git a/lib/rules/table-or-grid-role-matches.js b/lib/rules/table-or-grid-role-matches.js new file mode 100644 index 0000000000..1443b0acdf --- /dev/null +++ b/lib/rules/table-or-grid-role-matches.js @@ -0,0 +1,6 @@ +import { getRole } from '../commons/aria'; + +export default function tableOrGridRoleMatches(_, vNode) { + const role = getRole(vNode); + return ['treegrid', 'grid', 'table'].includes(role); +} diff --git a/lib/rules/td-headers-attr.json b/lib/rules/td-headers-attr.json index b3104d0f7c..141c4a0933 100644 --- a/lib/rules/td-headers-attr.json +++ b/lib/rules/td-headers-attr.json @@ -1,6 +1,7 @@ { "id": "td-headers-attr", "selector": "table", + "matches": "table-or-grid-role-matches", "tags": ["cat.tables", "wcag2a", "wcag131", "section508", "section508.22.g"], "actIds": ["a25f45"], "metadata": { diff --git a/test/checks/tables/td-headers-attr.js b/test/checks/tables/td-headers-attr.js index f9ba57dd84..8df3cc03e3 100644 --- a/test/checks/tables/td-headers-attr.js +++ b/test/checks/tables/td-headers-attr.js @@ -1,15 +1,15 @@ -describe('td-headers-attr', function() { +describe('td-headers-attr', function () { 'use strict'; var fixture = document.getElementById('fixture'); var checkContext = axe.testUtils.MockCheckContext(); - afterEach(function() { + afterEach(function () { fixture.innerHTML = ''; checkContext.reset(); }); - it('returns true no headers attribute is present', function() { + it('returns true no headers attribute is present', function () { fixture.innerHTML = '' + ' ' + @@ -22,7 +22,7 @@ describe('td-headers-attr', function() { ); }); - it('returns true if a valid header is present', function() { + it('returns true if a valid header is present', function () { fixture.innerHTML = '
hi hello
' + ' ' + @@ -35,7 +35,7 @@ describe('td-headers-attr', function() { ); }); - it('returns true if multiple valid headers are present', function() { + it('returns true if multiple valid headers are present', function () { fixture.innerHTML = '
hello
' + ' ' + @@ -48,7 +48,7 @@ describe('td-headers-attr', function() { ); }); - it('returns true with an empty header', function() { + it('returns true with an empty header', function () { fixture.innerHTML = '
hello hello
' + ' ' + @@ -61,7 +61,7 @@ describe('td-headers-attr', function() { ); }); - it('returns undefined if headers is empty', function() { + it('returns undefined if headers is empty', function () { fixture.innerHTML = '
' + ' ' + @@ -74,7 +74,7 @@ describe('td-headers-attr', function() { ); }); - it('returns false if the header is a table cell', function() { + it('returns false if the header is a table cell', function () { var node; fixture.innerHTML = @@ -109,7 +109,7 @@ describe('td-headers-attr', function() { ); }); - it('returns false if the header refers to the same cell', function() { + it('returns false if the header refers to the same cell', function () { fixture.innerHTML = '
' + ' ' + diff --git a/test/integration/rules/td-headers-attr/td-headers-attr.html b/test/integration/rules/td-headers-attr/td-headers-attr.html new file mode 100644 index 0000000000..c0afe4060a --- /dev/null +++ b/test/integration/rules/td-headers-attr/td-headers-attr.html @@ -0,0 +1,40 @@ +
hello
+ + +
HelloWorld
+ + + + +
HelloWorld
+ + + + + +
HelloHelloWorld
+ + + + +
HelloWorld
+ + + +
World
+ + + +
World
+ + + +
World
+ + + + + + + +
World
diff --git a/test/integration/rules/td-headers-attr/td-headers-attr.json b/test/integration/rules/td-headers-attr/td-headers-attr.json new file mode 100644 index 0000000000..cda7bf99cb --- /dev/null +++ b/test/integration/rules/td-headers-attr/td-headers-attr.json @@ -0,0 +1,6 @@ +{ + "description": "td-headers-attr test", + "rule": "td-headers-attr", + "violations": [["#fail1"], ["#fail2"], ["#fail3"]], + "passes": [["#pass1"], ["#pass2"], ["#pass3"]] +} diff --git a/test/rule-matches/table-or-grid-role-matches.js b/test/rule-matches/table-or-grid-role-matches.js new file mode 100644 index 0000000000..9d196ec3f1 --- /dev/null +++ b/test/rule-matches/table-or-grid-role-matches.js @@ -0,0 +1,51 @@ +describe('table-or-grid-role-matches', () => { + const { queryFixture } = axe.testUtils; + const rule = axe.utils.getRule('td-headers-attr'); + + it(`returns true for tables without role`, () => { + const vNode = queryFixture(` + +
foo bar
`); + assert.isTrue(rule.matches(vNode.actualNode, vNode)); + }); + + ['table', 'grid', 'treegrid'].forEach(role => { + it(`returns true for tables with role=${role}`, () => { + const vNode = queryFixture(` + +
foo bar
`); + assert.isTrue(rule.matches(vNode.actualNode, vNode)); + }); + }); + + ['region', 'presentation', 'none'].forEach(role => { + it(`returns false for tables with role=${role}`, () => { + const vNode = queryFixture(` + +
foo bar
`); + assert.isFalse(rule.matches(vNode.actualNode, vNode)); + }); + }); + + it(`returns true for tables with an invalid role`, () => { + const vNode = queryFixture(` + +
foo bar
`); + assert.isTrue(rule.matches(vNode.actualNode, vNode)); + }); + + it(`returns true for focusable tables with role=none`, () => { + const vNode = queryFixture(` + +
foo bar
`); + assert.isTrue(rule.matches(vNode.actualNode, vNode)); + }); + + it(`returns true for tables with role=none but with a global ARIA attribute`, () => { + const vNode = + queryFixture(` + +
foo bar
`); + assert.isTrue(rule.matches(vNode.actualNode, vNode)); + }); +});