diff --git a/lib/checks/forms/autocomplete-valid.js b/lib/checks/forms/autocomplete-valid.js index 3d6c5da16d..4b7617d7e7 100644 --- a/lib/checks/forms/autocomplete-valid.js +++ b/lib/checks/forms/autocomplete-valid.js @@ -1,102 +1,2 @@ -let { - standaloneTerms = [], - qualifiedTerms = [], - qualifiers = [], - locations = [], - looseTyped = false -} = - options || {}; - -qualifiers = qualifiers.concat(['home', 'work', 'mobile', 'fax', 'pager']); -locations = locations.concat(['billing', 'shipping']); -standaloneTerms = standaloneTerms.concat([ - 'name', - 'honorific-prefix', - 'given-name', - 'additional-name', - 'family-name', - 'honorific-suffix', - 'nickname', - 'username', - 'new-password', - 'current-password', - 'organization-title', - 'organization', - 'street-address', - 'address-line1', - 'address-line2', - 'address-line3', - 'address-level4', - 'address-level3', - 'address-level2', - 'address-level1', - 'country', - 'country-name', - 'postal-code', - 'cc-name', - 'cc-given-name', - 'cc-additional-name', - 'cc-family-name', - 'cc-number', - 'cc-exp', - 'cc-exp-month', - 'cc-exp-year', - 'cc-csc', - 'cc-type', - 'transaction-currency', - 'transaction-amount', - 'language', - 'bday', - 'bday-day', - 'bday-month', - 'bday-year', - 'sex', - 'url', - 'photo' -]); - -qualifiedTerms = qualifiedTerms.concat([ - 'tel', - 'tel-country-code', - 'tel-national', - 'tel-area-code', - 'tel-local', - 'tel-local-prefix', - 'tel-local-suffix', - 'tel-extension', - 'email', - 'impp' -]); - -const autocomplete = node.getAttribute('autocomplete'); -const autocompleteTerms = autocomplete.split(/\s+/g).map(term => { - return term.toLowerCase(); -}); - -if (!looseTyped) { - if ( - autocompleteTerms[0].length > 8 && - autocompleteTerms[0].substr(0, 8) === 'section-' - ) { - autocompleteTerms.shift(); - } - - if (locations.includes(autocompleteTerms[0])) { - autocompleteTerms.shift(); - } - - if (qualifiers.includes(autocompleteTerms[0])) { - autocompleteTerms.shift(); - // only quantifiers allowed at this point - standaloneTerms = []; - } - - if (autocompleteTerms.length !== 1) { - return false; - } -} - -const purposeTerm = autocompleteTerms[autocompleteTerms.length - 1]; -return ( - standaloneTerms.includes(purposeTerm) || qualifiedTerms.includes(purposeTerm) -); +const autocomplete = node.getAttribute('autocomplete') || ''; +return axe.commons.text.isValidAutocomplete(autocomplete, options); diff --git a/lib/commons/text/is-valid-autocomplete.js b/lib/commons/text/is-valid-autocomplete.js new file mode 100644 index 0000000000..9811e095be --- /dev/null +++ b/lib/commons/text/is-valid-autocomplete.js @@ -0,0 +1,118 @@ +/* global text */ +const autocomplete = { + stateTerms: ['on', 'off'], + standaloneTerms: [ + 'name', + 'honorific-prefix', + 'given-name', + 'additional-name', + 'family-name', + 'honorific-suffix', + 'nickname', + 'username', + 'new-password', + 'current-password', + 'organization-title', + 'organization', + 'street-address', + 'address-line1', + 'address-line2', + 'address-line3', + 'address-level4', + 'address-level3', + 'address-level2', + 'address-level1', + 'country', + 'country-name', + 'postal-code', + 'cc-name', + 'cc-given-name', + 'cc-additional-name', + 'cc-family-name', + 'cc-number', + 'cc-exp', + 'cc-exp-month', + 'cc-exp-year', + 'cc-csc', + 'cc-type', + 'transaction-currency', + 'transaction-amount', + 'language', + 'bday', + 'bday-day', + 'bday-month', + 'bday-year', + 'sex', + 'url', + 'photo' + ], + qualifiers: ['home', 'work', 'mobile', 'fax', 'pager'], + qualifiedTerms: [ + 'tel', + 'tel-country-code', + 'tel-national', + 'tel-area-code', + 'tel-local', + 'tel-local-prefix', + 'tel-local-suffix', + 'tel-extension', + 'email', + 'impp' + ], + locations: ['billing', 'shipping'] +}; +text.autocomplete = autocomplete; + +text.isValidAutocomplete = function isValidAutocomplete( + autocomplete, + { + looseTyped = false, + stateTerms = [], + locations = [], + qualifiers = [], + standaloneTerms = [], + qualifiedTerms = [] + } = {} +) { + /*eslint max-statements: ["error", 21] */ + autocomplete = autocomplete.toLowerCase(); + stateTerms = stateTerms.concat(text.autocomplete.stateTerms); + if (stateTerms.includes(autocomplete)) { + return true; + } + + qualifiers = qualifiers.concat(text.autocomplete.qualifiers); + locations = locations.concat(text.autocomplete.locations); + standaloneTerms = standaloneTerms.concat(text.autocomplete.standaloneTerms); + qualifiedTerms = qualifiedTerms.concat(text.autocomplete.qualifiedTerms); + + const autocompleteTerms = autocomplete.split(/\s+/g); + if (!looseTyped) { + if ( + autocompleteTerms[0].length > 8 && + autocompleteTerms[0].substr(0, 8) === 'section-' + ) { + autocompleteTerms.shift(); + } + + if (locations.includes(autocompleteTerms[0])) { + autocompleteTerms.shift(); + } + + if (qualifiers.includes(autocompleteTerms[0])) { + autocompleteTerms.shift(); + // only quantifiers allowed at this point + standaloneTerms = []; + } + + if (autocompleteTerms.length !== 1) { + return false; + } + } + + const purposeTerm = autocompleteTerms[autocompleteTerms.length - 1]; + return ( + standaloneTerms.includes(purposeTerm) || + qualifiedTerms.includes(purposeTerm) + ); +}; diff --git a/test/checks/forms/autocomplete-valid.js b/test/checks/forms/autocomplete-valid.js index 1dbf0fa2fb..5f809f5e26 100644 --- a/test/checks/forms/autocomplete-valid.js +++ b/test/checks/forms/autocomplete-valid.js @@ -11,94 +11,57 @@ describe('autocomplete-valid', function() { qualifiedTerms: ['qualified-term'] }; + var _isValidAutocomplete; beforeEach(function() { axe._tree = undefined; + _isValidAutocomplete = axe.commons.text.isValidAutocomplete; }); afterEach(function() { + axe.commons.text.isValidAutocomplete = _isValidAutocomplete; fixture.innerHTML = ''; checkContext.reset(); }); - function autocompleteCheckParams(arg, opt) { - return checkSetup( - '', - opt || options - ); - } - - it('returns true the only term is a valid autocomplete term', function() { - var params = autocompleteCheckParams('standalone-term'); - assert.isTrue(evaluate.apply(checkContext, params)); - }); - - it('returns false the only term is an invalid autocomplete term', function() { - var params = autocompleteCheckParams('bad-term'); - assert.isFalse(evaluate.apply(checkContext, params)); - }); - - it('returns true if section-* is used as the first term', function() { - var params = autocompleteCheckParams('section-foo standalone-term'); - assert.isTrue(evaluate.apply(checkContext, params)); - }); - - it('returns true if `shipping` or `billing` is used as the first term', function() { - var params1 = autocompleteCheckParams('shipping standalone-term'); - assert.isTrue(evaluate.apply(checkContext, params1)); - - var params2 = autocompleteCheckParams('billing standalone-term'); - assert.isTrue(evaluate.apply(checkContext, params2)); + it('passes autocomplete attribute to text.isValidAutocomplete', function() { + var params = checkSetup(''); + var called = false; + axe.commons.text.isValidAutocomplete = function(arg1) { + assert.equal(arg1, 'foo'); + called = true; + }; + evaluate.apply(checkContext, params); + assert.isTrue(called); }); - it('returns true if section-* is used before `shipping` or `billing`', function() { - var params = autocompleteCheckParams( - 'section-foo shipping standalone-term' + it('passes options to text.isValidAutocomplete', function() { + var options = { foo: 'bar' }; + var params = checkSetup( + '', + options ); - assert.isTrue(evaluate.apply(checkContext, params)); + var called = false; + axe.commons.text.isValidAutocomplete = function(_, arg2) { + assert.equal(arg2, options); + called = true; + }; + evaluate.apply(checkContext, params); + assert.isTrue(called); }); - it('returns false if `shipping` or `billing` is used before section-*', function() { - var params = autocompleteCheckParams( - 'shipping section-foo standalone-term' + it('returns the outcome of text.isValidAutocomplete', function() { + var params1 = checkSetup( + '', + options ); - assert.isFalse(evaluate.apply(checkContext, params)); - }); + assert.isFalse(_isValidAutocomplete('badvalue')); + assert.isFalse(evaluate.apply(checkContext, params1)); - it('returns true if "home", "work", "mobile", "fax" or "pager" is used before aqualifier', function() { - ['home', 'work', 'mobile', 'fax', 'pager'].forEach(function(qualifier) { - var params = autocompleteCheckParams(qualifier + ' qualified-term'); - assert.isTrue( - evaluate.apply(checkContext, params), - 'failed for ' + qualifier - ); - }); - }); - - it('returns false if "home", "work", "mobile", "fax" or "pager" is used before an inappropriate term', function() { - ['home', 'work', 'mobile', 'fax', 'pager'].forEach(function(qualifier) { - var params = autocompleteCheckParams(qualifier + ' standalone-term'); - assert.isFalse( - evaluate.apply(checkContext, params), - 'failed for ' + qualifier - ); - }); - }); - - describe('options.strictMode:false', function() { - it('returns true if the last term is a valid autocomplete term', function() { - var params = autocompleteCheckParams('do not care! valid-term', { - looseTyped: true, - standaloneTerms: ['valid-term'] - }); - assert.isTrue(evaluate.apply(checkContext, params)); - }); - - it('returns false if the last term is an invalid autocomplete term', function() { - var params = autocompleteCheckParams('shipping invalid', { - looseTyped: true, - standaloneTerms: ['valid-term'] - }); - assert.isFalse(evaluate.apply(checkContext, params)); - }); + var params2 = checkSetup( + '', + options + ); + assert.isTrue(_isValidAutocomplete('email')); + assert.isTrue(evaluate.apply(checkContext, params2)); }); }); diff --git a/test/commons/text/is-valid-autocomplete.js b/test/commons/text/is-valid-autocomplete.js new file mode 100644 index 0000000000..ca5d81d7bd --- /dev/null +++ b/test/commons/text/is-valid-autocomplete.js @@ -0,0 +1,88 @@ +describe('text.isValidAutocomplete', function() { + 'use strict'; + + var isValidAutocomplete = axe.commons.text.isValidAutocomplete; + var options = { + standaloneTerms: ['standalone-term'], + qualifiedTerms: ['qualified-term'] + }; + + it('returns true if autocomplete is `on` or `off', function() { + ['on', 'off'].forEach(function(state) { + assert.isTrue(isValidAutocomplete(state, options)); + }); + }); + + it('returns false if `on` or `off` is used with another term', function() { + ['on', 'off'].forEach(function(state) { + assert.isFalse(isValidAutocomplete('section-foo ' + state, options)); + }); + }); + + it('returns true the only term is a valid autocomplete term', function() { + assert.isTrue(isValidAutocomplete('standalone-term', options)); + }); + + it('returns false the only term is an invalid autocomplete term', function() { + assert.isFalse(isValidAutocomplete('bad-term', options)); + }); + + it('returns true if section-* is used as the first term', function() { + assert.isTrue(isValidAutocomplete('section-foo standalone-term', options)); + }); + + it('returns true if `shipping` or `billing` is used as the first term', function() { + assert.isTrue(isValidAutocomplete('shipping standalone-term', options)); + assert.isTrue(isValidAutocomplete('billing standalone-term', options)); + }); + + it('returns true if section-* is used before `shipping` or `billing`', function() { + assert.isTrue( + isValidAutocomplete('section-foo shipping standalone-term', options) + ); + }); + + it('returns false if `shipping` or `billing` is used before section-*', function() { + assert.isFalse( + isValidAutocomplete('shipping section-foo standalone-term', options) + ); + }); + + it('returns true if "home", "work", "mobile", "fax" or "pager" is used before aqualifier', function() { + ['home', 'work', 'mobile', 'fax', 'pager'].forEach(function(qualifier) { + assert.isTrue( + isValidAutocomplete(qualifier + ' qualified-term', options), + 'failed for ' + qualifier + ); + }); + }); + + it('returns false if "home", "work", "mobile", "fax" or "pager" is used before an inappropriate term', function() { + ['home', 'work', 'mobile', 'fax', 'pager'].forEach(function(qualifier) { + assert.isFalse( + isValidAutocomplete(qualifier + ' standalone-term', options), + 'failed for ' + qualifier + ); + }); + }); + + describe('options.strictMode:false', function() { + it('returns true if the last term is a valid autocomplete term', function() { + assert.isTrue( + isValidAutocomplete('do not care! valid-term', { + looseTyped: true, + standaloneTerms: ['valid-term'] + }) + ); + }); + + it('returns false if the last term is an invalid autocomplete term', function() { + assert.isFalse( + isValidAutocomplete('shipping invalid', { + looseTyped: true, + standaloneTerms: ['valid-term'] + }) + ); + }); + }); +});