diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 6a910d80cd..91f06e842c 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -53,6 +53,7 @@ | [object-alt](https://dequeuniversity.com/rules/axe/4.0/object-alt?application=RuleDescription) | Ensures <object> elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review | | [role-img-alt](https://dequeuniversity.com/rules/axe/4.0/role-img-alt?application=RuleDescription) | Ensures [role='img'] elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review | | [scrollable-region-focusable](https://dequeuniversity.com/rules/axe/4.0/scrollable-region-focusable?application=RuleDescription) | Elements that have scrollable content should be accessible by keyboard | Moderate | wcag2a, wcag211 | failure | +| [select-name](https://dequeuniversity.com/rules/axe/4.0/select-name?application=RuleDescription) | Ensures select element has an accessible name | Minor, Critical | cat.forms, wcag2a, wcag412, wcag131, section508, section508.22.n | failure, needs review | | [server-side-image-map](https://dequeuniversity.com/rules/axe/4.0/server-side-image-map?application=RuleDescription) | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f | needs review | | [svg-img-alt](https://dequeuniversity.com/rules/axe/4.0/svg-img-alt?application=RuleDescription) | 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 | failure, needs review | | [td-headers-attr](https://dequeuniversity.com/rules/axe/4.0/td-headers-attr?application=RuleDescription) | Ensure that each cell in a table using the headers refers to another cell in that table | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g | failure, needs review | diff --git a/lib/checks/shared/non-empty-placeholder.json b/lib/checks/shared/non-empty-placeholder.json new file mode 100644 index 0000000000..809a3df626 --- /dev/null +++ b/lib/checks/shared/non-empty-placeholder.json @@ -0,0 +1,14 @@ +{ + "id": "non-empty-placeholder", + "evaluate": "attr-non-space-content-evaluate", + "options": { + "attribute": "placeholder" + }, + "metadata": { + "impact": "serious", + "messages": { + "pass": "Element has a placeholder attribute", + "fail": "Element has no placeholder attribute or the placeholder attribute is empty" + } + } +} diff --git a/lib/commons/text/native-text-methods.js b/lib/commons/text/native-text-methods.js index 23c1f43614..dcc11a70df 100644 --- a/lib/commons/text/native-text-methods.js +++ b/lib/commons/text/native-text-methods.js @@ -93,11 +93,18 @@ const nativeTextMethods = { */ singleSpace: function singleSpace() { return ' '; - } + }, + + /** + * Return the placeholder text + * @param {VirtualNode} element + * @return {String} placeholder text + */ + placeholderText: attrText.bind(null, 'placeholder') }; -function attrText(attr, { actualNode }) { - return actualNode.getAttribute(attr) || ''; +function attrText(attr, vNode) { + return vNode.attr(attr) || ''; } /** diff --git a/lib/rules/label.json b/lib/rules/label.json index 553f506659..22999f272b 100644 --- a/lib/rules/label.json +++ b/lib/rules/label.json @@ -1,6 +1,6 @@ { "id": "label", - "selector": "input, select, textarea", + "selector": "input, textarea", "matches": "label-matches", "tags": [ "cat.forms", @@ -21,6 +21,7 @@ "implicit-label", "explicit-label", "non-empty-title", + "non-empty-placeholder", "role-none", "role-presentation" ], diff --git a/lib/rules/select-name.json b/lib/rules/select-name.json new file mode 100644 index 0000000000..e65fc81eb0 --- /dev/null +++ b/lib/rules/select-name.json @@ -0,0 +1,27 @@ +{ + "id": "select-name", + "selector": "select", + "tags": [ + "cat.forms", + "wcag2a", + "wcag412", + "wcag131", + "section508", + "section508.22.n" + ], + "metadata": { + "description": "Ensures select element has an accessible name", + "help": "Select element must have and accessible name" + }, + "all": [], + "any": [ + "aria-label", + "aria-labelledby", + "implicit-label", + "explicit-label", + "non-empty-title", + "role-none", + "role-presentation" + ], + "none": ["help-same-as-label", "hidden-explicit-label"] +} diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index 57d16f60d3..3b79a1cb1c 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -502,9 +502,9 @@ const htmlElms = { implicitAttrs: { 'aria-valuenow': '' }, - // 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url" and textarea Element + // 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url" // 5.7 Other Form Elements - namingMethods: ['labelText'] + namingMethods: ['labelText', 'placeholderText'] } } }, @@ -840,7 +840,8 @@ const htmlElms = { 'aria-valuenow': '', 'aria-multiline': 'true' }, - namingMethods: ['labelText'] + // 5.1 textarea + namingMethods: ['labelText', 'placeholderText'] }, tfoot: { allowedRoles: true diff --git a/test/checks/shared/non-empty-placeholder.js b/test/checks/shared/non-empty-placeholder.js new file mode 100644 index 0000000000..78ed141659 --- /dev/null +++ b/test/checks/shared/non-empty-placeholder.js @@ -0,0 +1,37 @@ +describe('non-empty-placeholder', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var checkSetup = axe.testUtils.checkSetup; + var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-placeholder'); + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('should return true if a placeholder is present', function() { + var params = checkSetup(''); + + assert.isTrue(checkEvaluate.apply(null, params)); + }); + + it('should return false if a placeholder is not present', function() { + var params = checkSetup(''); + + assert.isFalse(checkEvaluate.apply(null, params)); + }); + + it('should return false if a placeholder is present, but empty', function() { + var params = checkSetup(''); + + assert.isFalse(checkEvaluate.apply(null, params)); + }); + + it('should collapse whitespace', function() { + var params = checkSetup( + '' + ); + + assert.isFalse(checkEvaluate.apply(null, params)); + }); +}); diff --git a/test/commons/text/accessible-text.js b/test/commons/text/accessible-text.js index 7a0e370066..fb76a851d3 100644 --- a/test/commons/text/accessible-text.js +++ b/test/commons/text/accessible-text.js @@ -1026,8 +1026,7 @@ describe('text.accessibleTextVirtual', function() { }); }); - // not implemented yet, doesn't work accross ATs - it.skip('should find a placeholder attribute', function() { + it('should find a placeholder attribute', function() { types.forEach(function(type) { var t = type ? ' type="' + type + '"' : ''; fixture.innerHTML = ''; @@ -1118,8 +1117,7 @@ describe('text.accessibleTextVirtual', function() { ); }); - // not implemented yet, doesn't work accross ATs - it.skip('should find a placeholder attribute', function() { + it('should find a placeholder attribute', function() { fixture.innerHTML = ''; axe.testUtils.flatTreeSetup(fixture); diff --git a/test/commons/text/native-text-methods.js b/test/commons/text/native-text-methods.js index b16d82a3be..add0d9f1b0 100644 --- a/test/commons/text/native-text-methods.js +++ b/test/commons/text/native-text-methods.js @@ -184,4 +184,19 @@ describe('text.nativeTextMethods', function() { assert.equal(singleSpace(), ' '); }); }); + + describe('placeholderText', function() { + var placeholderText = nativeTextMethods.placeholderText; + it('returns the placeholder attribute of actualNode', function() { + fixtureSetup(''); + var input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0]; + assert.equal(placeholderText(input), 'foo'); + }); + + it('returns `` when there is no placeholder', function() { + fixtureSetup(''); + var input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0]; + assert.equal(placeholderText(input), ''); + }); + }); }); diff --git a/test/integration/rules/label/label.html b/test/integration/rules/label/label.html index 801d1a866d..2b5df20f3b 100644 --- a/test/integration/rules/label/label.html +++ b/test/integration/rules/label/label.html @@ -5,42 +5,26 @@ - - -
Label
- - - - + - + > -