diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 5c534a0bbe..289dff15c5 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -77,6 +77,7 @@ | scrollable-region-focusable | Elements that have scrollable content should be accessible by keyboard | Moderate | wcag2a, wcag211 | true | true | false | | server-side-image-map | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f | true | false | true | | skip-link | Ensure all skip links have a focusable target | Moderate | cat.keyboard, best-practice | true | true | true | +| svg-img-alt | Ensures svg elements with an img, graphics-document or graphics-symbol role have an accessible text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | true | false | | tabindex | Ensures tabindex attribute values are not greater than 0 | Serious | cat.keyboard, best-practice | true | true | false | | table-duplicate-name | Ensure that tables do not have the same summary and caption | Minor | cat.tables, best-practice | true | true | false | | table-fake-caption | Ensure that tables with a caption use the <caption> element. | Serious | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | true | true | false | diff --git a/lib/checks/shared/svg-non-empty-title.js b/lib/checks/shared/svg-non-empty-title.js new file mode 100644 index 0000000000..3b099d686b --- /dev/null +++ b/lib/checks/shared/svg-non-empty-title.js @@ -0,0 +1,4 @@ +const titleNode = virtualNode.children.find(({ props }) => { + return props.nodeName === 'title'; +}); +return !!titleNode && titleNode.actualNode.textContent.trim() !== ''; diff --git a/lib/checks/shared/svg-non-empty-title.json b/lib/checks/shared/svg-non-empty-title.json new file mode 100644 index 0000000000..9f929992cc --- /dev/null +++ b/lib/checks/shared/svg-non-empty-title.json @@ -0,0 +1,11 @@ +{ + "id": "svg-non-empty-title", + "evaluate": "svg-non-empty-title.js", + "metadata": { + "impact": "serious", + "messages": { + "pass": "element has a child that is a title", + "fail": "element has no child that is a title" + } + } +} diff --git a/lib/rules/html-namespace-matches.js b/lib/rules/html-namespace-matches.js new file mode 100644 index 0000000000..1dafa700da --- /dev/null +++ b/lib/rules/html-namespace-matches.js @@ -0,0 +1 @@ +return node.namespaceURI === 'http://www.w3.org/1999/xhtml'; diff --git a/lib/rules/role-img-alt.json b/lib/rules/role-img-alt.json index 49ec8eaabe..53700d8532 100644 --- a/lib/rules/role-img-alt.json +++ b/lib/rules/role-img-alt.json @@ -1,6 +1,7 @@ { "id": "role-img-alt", - "selector": "[role='img']:not(svg):not(img):not(area):not(input):not(object)", + "selector": "[role='img']:not(img):not(area):not(input):not(object)", + "matches": "html-namespace-matches.js", "tags": [ "cat.text-alternatives", "wcag2a", diff --git a/lib/rules/svg-img-alt.json b/lib/rules/svg-img-alt.json new file mode 100644 index 0000000000..45d76a69b4 --- /dev/null +++ b/lib/rules/svg-img-alt.json @@ -0,0 +1,24 @@ +{ + "id": "svg-img-alt", + "selector": "[role=\"img\"], [role=\"graphics-symbol\"], svg[role=\"graphics-document\"]", + "matches": "svg-namespace-matches.js", + "tags": [ + "cat.text-alternatives", + "wcag2a", + "wcag111", + "section508", + "section508.22.a" + ], + "metadata": { + "description": "Ensures svg elements with an img, graphics-document or graphics-symbol role have an accessible text", + "help": "svg elements with an img role have an alternative text" + }, + "all": [], + "any": [ + "svg-non-empty-title", + "aria-label", + "aria-labelledby", + "non-empty-title" + ], + "none": [] +} diff --git a/lib/rules/svg-namespace-matches.js b/lib/rules/svg-namespace-matches.js new file mode 100644 index 0000000000..6be1468d52 --- /dev/null +++ b/lib/rules/svg-namespace-matches.js @@ -0,0 +1 @@ +return node.namespaceURI === 'http://www.w3.org/2000/svg'; diff --git a/test/checks/shared/svg-non-empty-title.js b/test/checks/shared/svg-non-empty-title.js new file mode 100644 index 0000000000..24a773d308 --- /dev/null +++ b/test/checks/shared/svg-non-empty-title.js @@ -0,0 +1,56 @@ +describe('svg-non-empty-title tests', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var checkSetup = axe.testUtils.checkSetup; + var check = checks['svg-non-empty-title']; + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('returns true if the element has a `title` child', function() { + var checkArgs = checkSetup( + '' + ); + assert.isTrue(check.evaluate.apply(null, checkArgs)); + }); + + it('returns true if the `title` child has text nested in another element', function() { + var checkArgs = checkSetup( + '' + ); + assert.isTrue(check.evaluate.apply(null, checkArgs)); + }); + + it('returns false if the element has no `title` child', function() { + var checkArgs = checkSetup(''); + assert.isFalse(check.evaluate.apply(null, checkArgs)); + }); + + it('returns false if the `title` child is empty', function() { + var checkArgs = checkSetup(''); + assert.isFalse(check.evaluate.apply(null, checkArgs)); + }); + + it('returns false if the `title` is a grandchild', function() { + var checkArgs = checkSetup( + '' + ); + assert.isFalse(check.evaluate.apply(null, checkArgs)); + }); + + it('returns false if the `title` child has only whitespace', function() { + var checkArgs = checkSetup( + '' + ); + assert.isFalse(check.evaluate.apply(null, checkArgs)); + }); + + it('returns false if there are multiple titles, and the first is empty', function() { + var checkArgs = checkSetup( + '' + ); + assert.isFalse(check.evaluate.apply(null, checkArgs)); + }); +}); diff --git a/test/integration/rules/role-img-alt/role-img-alt.html b/test/integration/rules/role-img-alt/role-img-alt.html index 8edbb4801b..e430fdbde6 100644 --- a/test/integration/rules/role-img-alt/role-img-alt.html +++ b/test/integration/rules/role-img-alt/role-img-alt.html @@ -11,3 +11,14 @@
+ + diff --git a/test/integration/rules/svg-img-alt/svg-img-alt.html b/test/integration/rules/svg-img-alt/svg-img-alt.html new file mode 100644 index 0000000000..9826bb9070 --- /dev/null +++ b/test/integration/rules/svg-img-alt/svg-img-alt.html @@ -0,0 +1,236 @@ +