From 20c88f3a397f908d5cf402fe46eb8d66943806a0 Mon Sep 17 00:00:00 2001 From: Joe Morgan Date: Wed, 31 Jan 2018 12:58:29 -0600 Subject: [PATCH 1/3] tests --- .babelrc | 13 +++ .eslintrc | 181 +++++++++++++++++++++++++++++++++++ .gitignore | 1 + package.json | 65 +++++++------ src/passwordStrength.spec.js | 20 ++++ 5 files changed, 253 insertions(+), 27 deletions(-) create mode 100644 .babelrc create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 src/passwordStrength.spec.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..1e78999 --- /dev/null +++ b/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": [ + "env", + "react", + "stage-0" + ], + "plugins": [ + "transform-class-properties", + "transform-decorators", + "transform-react-constant-elements", + "transform-react-inline-elements" + ] +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..e2a8306 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,181 @@ +{ + "env": { + "es6": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module", + "ecmaFeatures": { + "binaryLiterals": true, + "blockBindings": true, + "defaultParams": true, + "forOf": true, + "generators": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": true, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "octalLiterals": true, + "regexUFlag": true, + "regexYFlag": true, + "templateStrings": true, + "unicodeCodePointEscapes": true, + "jsx": false + } + }, + "rules": { + "comma-dangle": 2, + "no-cond-assign": 2, + "no-console": 1, + "no-constant-condition": 2, + "no-control-regex": 0, + "no-debugger": 1, + "no-dupe-keys": 2, + "no-empty": 2, + "no-empty-class": 0, + "no-ex-assign": 0, + "no-extra-boolean-cast": 2, + "no-extra-parens": 2, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 1, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-sparse-arrays": 2, + "no-unreachable": 2, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + "block-scoped-var": 2, + "complexity": 0, + "consistent-return": 1, + "curly": 2, + "default-case": 2, + "dot-notation": 2, + "eqeqeq": 1, + "guard-for-in": 1, + "no-alert": 1, + "no-caller": 2, + "no-div-regex": 0, + "no-else-return": 2, + "no-empty-label": 0, + "no-eq-null": 1, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 1, + "no-loop-func": 2, + "no-multi-spaces": 1, + "no-multi-str": 1, + "no-native-reassign": 2, + "no-new": 1, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 0, + "no-octal-escape": 0, + "no-process-env": 0, + "no-proto": 0, + "no-redeclare": 0, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 0, + "no-unused-expressions": 0, + "no-void": 0, + "no-warning-comments": 1, + "no-with": 0, + "radix": 1, + "vars-on-top": 1, + "wrap-iife": 1, + "yoda": 1, + "strict": 0, + "no-catch-shadow": 0, + "no-shadow": 0, + "no-shadow-restricted-names": 0, + "no-undef": 0, + "no-undef-init": 0, + "no-undefined": 0, + "no-unused-vars": 2, + "no-use-before-define": 0, + "brace-style": 2, + "camelcase": 2, + "comma-spacing": 0, + "comma-style": [2, "last"], + "consistent-this": 1, + "eol-last": 0, + "func-names": 0, + "func-style": 0, + "key-spacing": 2, + "max-nested-callbacks": 0, + "new-cap": 0, + "new-parens": 0, + "no-array-constructor": 0, + "no-inline-comments": 1, + "no-lonely-if": 1, + "no-mixed-spaces-and-tabs": 1, + "no-multiple-empty-lines": 0, + "no-nested-ternary": 1, + "no-new-object": 2, + "semi-spacing": 2, + "no-spaced-func": 2, + "no-ternary": 0, + "no-trailing-spaces": 1, + "no-underscore-dangle": 2, + "no-wrap-func": 0, + "one-var": [1, "never"], + "operator-assignment": 0, + "padded-blocks": 0, + "quote-props": 0, + "quotes": [2, "single"], + "semi": 2, + "sort-vars": 0, + "space-before-function-paren": [2, "never"], + "keyword-spacing": 2, + "space-before-blocks": 2, + "object-curly-spacing": 2, + "array-bracket-spacing": 2, + "computed-property-spacing": 2, + "space-in-parens": 2, + "space-infix-ops": 2, + "space-unary-ops": 2, + "spaced-comment": 1, + "wrap-regex": 2, + "no-var": 2, + "generator-star-spacing": ["error", {"before": true, "after": false}], + "arrow-body-style": 1, + "arrow-parens": 0, + "arrow-spacing": [2, {"before": true, "after": true}], + "constructor-super": 0, + "no-class-assign": 0, + "no-confusing-arrow": 0, + "no-const-assign": 2, + "no-dupe-class-members": 0, + "no-duplicate-imports": 2, + "no-new-symbol": 0, + "no-useless-computed-key": 2, + "no-useless-constructor": 2, + "no-useless-rename": [2, {"ignoreDestructuring": false, "ignoreImport": false, "ignoreExport": false}], + "object-shorthand": 0, + "prefer-arrow-callback": 0, + "prefer-const": 2, + "prefer-destructuring": 1, + "prefer-numeric-literals": 0, + "prefer-rest-params": 1, + "prefer-spread": 1, + "prefer-template": 2, + "require-yield": 0, + "rest-spread-spacing": [2, "never"], + "sort-imports": 0, + "symbol-description": 0, + "template-curly-spacing": [2, "never"], + "yield-star-spacing": [2, {"before": true, "after": true}], + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/package.json b/package.json index 48ddcd3..cd9eb84 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,41 @@ { - "name": "passwordStrength", - "version": "1.0.0", - "description": "A plugin for testing and displaying the strength of a password.", - "keywords": [ - "password", - "strength", - "form", - "DEGJS" - ], - "author": { - "name": "Aaron Ladage", - "email": "aladage@degdigital.com", - "url": "http://aaronladage.com" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/DEGJS/passwordStrength.git" - }, - "bugs": "https://github.com/DEGJS/passwordStrength/issues", - "jspm": { - "registry": "jspm", - "format": "es6", - "main": "passwordStrength.js", - "directories": { - "lib": "src" - } + "name": "passwordStrength", + "version": "1.0.0", + "description": "A plugin for testing and displaying the strength of a password.", + "keywords": [ + "password", + "strength", + "form", + "DEGJS" + ], + "author": { + "name": "Aaron Ladage", + "email": "aladage@degdigital.com", + "url": "http://aaronladage.com" + }, + "scripts": { + "test": "jest .", + "lint": "eslint ./src" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/DEGJS/passwordStrength.git" + }, + "bugs": "https://github.com/DEGJS/passwordStrength/issues", + "devDependencies": { + "babel-env": "^2.4.1", + "babel-jest": "^22.0.4", + "eslint": "^4.14.0", + "expect": "^22.0.3", + "jest": "^22.0.4" + }, + "jspm": { + "registry": "jspm", + "format": "es6", + "main": "passwordStrength.js", + "directories": { + "lib": "src" } + } } diff --git a/src/passwordStrength.spec.js b/src/passwordStrength.spec.js new file mode 100644 index 0000000..0326e7f --- /dev/null +++ b/src/passwordStrength.spec.js @@ -0,0 +1,20 @@ +import expect from 'expect'; +import passwordStrength from './passwordStrength'; + +describe('Password Strength', () => { + it('should pass', () => { + document.body.innerHTML = `
` + const password = passwordStrength(); + return expect(password.test('2%9ddiaA')).resolves.toEqual('passing'); + }); + it('should fail', async () => { + expect.assertions(1); + document.body.innerHTML = `
` + const password = passwordStrength(); + try { + await password.test('foo'); + } catch (e) { + expect(e).toEqual('failing') + } + }); +}); From 7a49442d5923e9d957cd2f5097aa48068bd05cf0 Mon Sep 17 00:00:00 2001 From: Joe Morgan Date: Wed, 31 Jan 2018 13:07:46 -0600 Subject: [PATCH 2/3] linting --- src/passwordStrength.js | 369 +++++++++++++++++------------------ src/passwordStrength.spec.js | 8 +- 2 files changed, 185 insertions(+), 192 deletions(-) diff --git a/src/passwordStrength.js b/src/passwordStrength.js index b5728ac..f6b4062 100644 --- a/src/passwordStrength.js +++ b/src/passwordStrength.js @@ -1,191 +1,184 @@ -/* */ -const passwordStrength = function(options = {}) { - - const errors = { - prefix: 'passwordStrength: ', - noRulesSet: 'No rules set.' - }, - defaults = { - inputEl: null, - wrapperSelector: '.js-pwstrength', - headingClass: 'password-strength__heading', - headingText: 'Password must:', - listClass: 'password-strength__list', - listItemDisplayClass: 'password-strength__list-item', - listItemJsClass: 'js-pwstrength-list-item', - untestedClass: 'is-untested', - passingClass: 'is-passing', - failingClass: 'is-failing', - inputEvent: 'keyup', - untestedMsg: 'untested', - passingMsg: 'passing', - failingMsg: 'failing', - logErrors: false, - onUntestedCallback: null, - onFailingCallback: null, - onPassingCallback: null, - rules: [ - { - label: 'Be at least 8 characters', - test: function(val) { - return val.length >= 8; - } - }, - { - label: 'Contain at least 1 uppercase character', - test: function(val) { - return val.replace(/[^A-Z]/g, "").length > 0; - } - }, - { - label: 'Contain at least 1 numerical character', - test: function(val) { - return val.match(/\d+/g) !== null; - } - }, - { - label: 'Contain at least 1 special character', - test: function(val) { - return /[~`!@#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(val); - } - } - ] - }; - - let wrapperEl, - listItemEls, - settings; - - function init() { - settings = Object.assign({}, defaults, options); - wrapperEl = document.querySelector(settings.wrapperSelector); - if ((wrapperEl) && (wrapperEl.innerHTML === '')) { - if (rulesAreSet()) { - render(); - if (settings.inputEl !== null) { - bindEvents(); - } - } else { - logError(errors.noRulesSet); - } - } - }; - - function bindEvents() { - settings.inputEl.addEventListener(settings.inputEvent, onInputEvent); - }; - - function onInputEvent(e) { - test(e.target.value) - .then(function(response) { - fireCallback(settings.onPassingCallback, response); - }) - .catch(function(error) { - fireCallback(settings.onFailingCallback, error); - }); - }; - - function render() { - wrapperEl.classList.add(settings.untestedClass); - let content = ` -

${settings.headingText}

- - `; - wrapperEl.insertAdjacentHTML('afterbegin', content); - listItemEls = Array.from(wrapperEl.querySelectorAll('.' + settings.listItemJsClass)); - }; - - function renderListItems() { - let output = ''; - settings.rules.forEach(function(rule) { - output += ` -
  • ${rule.label}
  • - ` - }); - return output; - }; - - function test(val = null, validateWrapper = true) { - return new Promise(function(resolve, reject) { - let hasFailure = false; - wrapperEl.classList.remove(settings.untestedClass); - if ((!val) || (val.length === 0)) { - wrapperEl.classList.remove(settings.failingClass, settings.passingClass); - if (validateWrapper === true) { - wrapperEl.classList.add(settings.untestedClass); - } - listItemEls.forEach(function(el) { - el.classList.add(settings.untestedClass); - el.classList.remove(settings.failingClass, settings.passingClass); - }); - fireCallback(settings.onUntestedCallback, settings.untestedMsg); - hasFailure === false; - } else { - let allRulesPass = true; - settings.rules.forEach(function(rule, index) { - listItemEls[index].classList.remove(settings.untestedClass); - let ruleDoesPass = rule.test(val) === true; - if (ruleDoesPass) { - listItemEls[index].classList.remove(settings.failingClass); - listItemEls[index].classList.add(settings.passingClass); - } else { - allRulesPass = false; - listItemEls[index].classList.add(settings.failingClass); - listItemEls[index].classList.remove(settings.passingClass); - } - return ruleDoesPass; - }); - if (allRulesPass) { - wrapperEl.classList.remove(settings.failingClass); - if (validateWrapper === true) { - wrapperEl.classList.add(settings.passingClass); - } - fireCallback(settings.onPassingCallback, settings.passingMsg); - resolve(settings.passingMsg); - } else { - wrapperEl.classList.remove(settings.passingClass, settings.failingClass); - if (validateWrapper === true) { - wrapperEl.classList.add(settings.failingClass); - } - fireCallback(settings.onFailingCallback, settings.failingMsg) - reject(settings.failingMsg); - } - } - }); - }; - - function rulesAreSet() { - return settings.rules && Array.isArray(settings.rules) && settings.rules.length > 0; - }; - - function logError(msg, method = 'log') { - if (settings.logErrors === true) { - console[method](errors.prefix + msg); - } - }; - - function fireCallback(callback, msg) { - if (callback !== null) { - callback(msg); - } - }; - - function destroy() { - if (settings.inputEl !== null) { - settings.inputEl.removeEventListener(settings.inputEvent, onInputEvent); - } - wrapperEl.innerHTML = ''; - }; - - init(); - - return { - test: test, - destroy: destroy - }; +const errors = { + prefix: 'passwordStrength: ', + noRulesSet: 'No rules set.' +}; +const defaults = { + inputEl: null, + wrapperSelector: '.js-pwstrength', + headingClass: 'password-strength__heading', + headingText: 'Password must:', + listClass: 'password-strength__list', + listItemDisplayClass: 'password-strength__list-item', + listItemJsClass: 'js-pwstrength-list-item', + untestedClass: 'is-untested', + passingClass: 'is-passing', + failingClass: 'is-failing', + inputEvent: 'keyup', + untestedMsg: 'untested', + passingMsg: 'passing', + failingMsg: 'failing', + logErrors: false, + onUntestedCallback: null, + onFailingCallback: null, + onPassingCallback: null, + rules: [ + { + label: 'Be at least 8 characters', + test: function(val) { + return val.length >= 8; + } + }, + { + label: 'Contain at least 1 uppercase character', + test: function(val) { + return val.replace(/[^A-Z]/g, '').length > 0; + } + }, + { + label: 'Contain at least 1 numerical character', + test: function(val) { + return val.match(/\d+/g) !== null; + } + }, + { + label: 'Contain at least 1 special character', + test: function(val) { + return (/[~`!@#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g).test(val); + } + } + ] }; -export default passwordStrength; \ No newline at end of file +let wrapperEl; +let listItemEls; +let settings; + +function bindEvents() { + settings.inputEl.addEventListener(settings.inputEvent, onInputEvent); +} + +function onInputEvent(e) { + test(e.target.value) + .then(function(response) { + fireCallback(settings.onPassingCallback, response); + }) + .catch(function(error) { + fireCallback(settings.onFailingCallback, error); + }); +} + +function render() { + wrapperEl.classList.add(settings.untestedClass); + const content = ` +

    ${settings.headingText}

    + + `; + wrapperEl.insertAdjacentHTML('afterbegin', content); + listItemEls = Array.from(wrapperEl.querySelectorAll(`.${settings.listItemJsClass}`)); +} + +function renderListItems() { + let output = ''; + settings.rules.forEach(function(rule) { + output += ` +
  • ${rule.label}
  • + `; + }); + return output; +} + +function test(val = null, validateWrapper = true) { + return new Promise(function(resolve, reject) { + const hasFailure = false; + wrapperEl.classList.remove(settings.untestedClass); + if (!val || val.length === 0) { + wrapperEl.classList.remove(settings.failingClass, settings.passingClass); + if (validateWrapper === true) { + wrapperEl.classList.add(settings.untestedClass); + } + listItemEls.forEach(function(el) { + el.classList.add(settings.untestedClass); + el.classList.remove(settings.failingClass, settings.passingClass); + }); + fireCallback(settings.onUntestedCallback, settings.untestedMsg); + hasFailure === false; + } else { + let allRulesPass = true; + settings.rules.forEach(function(rule, index) { + listItemEls[index].classList.remove(settings.untestedClass); + const ruleDoesPass = rule.test(val) === true; + if (ruleDoesPass) { + listItemEls[index].classList.remove(settings.failingClass); + listItemEls[index].classList.add(settings.passingClass); + } else { + allRulesPass = false; + listItemEls[index].classList.add(settings.failingClass); + listItemEls[index].classList.remove(settings.passingClass); + } + return ruleDoesPass; + }); + if (allRulesPass) { + wrapperEl.classList.remove(settings.failingClass); + if (validateWrapper === true) { + wrapperEl.classList.add(settings.passingClass); + } + fireCallback(settings.onPassingCallback, settings.passingMsg); + resolve(settings.passingMsg); + } else { + wrapperEl.classList.remove(settings.passingClass, settings.failingClass); + if (validateWrapper === true) { + wrapperEl.classList.add(settings.failingClass); + } + fireCallback(settings.onFailingCallback, settings.failingMsg); + reject(settings.failingMsg); + } + } + }); +} + +function rulesAreSet() { + return settings.rules && Array.isArray(settings.rules) && settings.rules.length > 0; +} + +function logError(msg, method = 'log') { + if (settings.logErrors === true) { + console[method](errors.prefix + msg); + } +} + +function fireCallback(callback, msg) { + if (callback !== null) { + callback(msg); + } +} + +function destroy() { + if (settings.inputEl !== null) { + settings.inputEl.removeEventListener(settings.inputEvent, onInputEvent); + } + wrapperEl.innerHTML = ''; +} + +function passwordStrength(options = {}) { + settings = Object.assign({}, defaults, options); + wrapperEl = document.querySelector(settings.wrapperSelector); + if (wrapperEl && wrapperEl.innerHTML === '') { + if (rulesAreSet()) { + render(); + if (settings.inputEl !== null) { + bindEvents(); + } + } else { + logError(errors.noRulesSet); + } + } + return { + test: test, + destroy: destroy + }; +} + +export default passwordStrength; diff --git a/src/passwordStrength.spec.js b/src/passwordStrength.spec.js index 0326e7f..e1ebd8c 100644 --- a/src/passwordStrength.spec.js +++ b/src/passwordStrength.spec.js @@ -3,18 +3,18 @@ import passwordStrength from './passwordStrength'; describe('Password Strength', () => { it('should pass', () => { - document.body.innerHTML = `
    ` + document.body.innerHTML = '
    '; const password = passwordStrength(); return expect(password.test('2%9ddiaA')).resolves.toEqual('passing'); }); - it('should fail', async () => { + it('should fail', async() => { expect.assertions(1); - document.body.innerHTML = `
    ` + document.body.innerHTML = '
    '; const password = passwordStrength(); try { await password.test('foo'); } catch (e) { - expect(e).toEqual('failing') + expect(e).toEqual('failing'); } }); }); From f2f1cd895fbd3842d3e59a3d630350a561a37d09 Mon Sep 17 00:00:00 2001 From: Joe Morgan Date: Wed, 31 Jan 2018 13:09:09 -0600 Subject: [PATCH 3/3] remove unnecessary babel presets --- .babelrc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.babelrc b/.babelrc index 1e78999..9abcfb0 100644 --- a/.babelrc +++ b/.babelrc @@ -1,13 +1,5 @@ { "presets": [ "env", - "react", - "stage-0" - ], - "plugins": [ - "transform-class-properties", - "transform-decorators", - "transform-react-constant-elements", - "transform-react-inline-elements" ] }