-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from PayU/min-len
feat: min length matcher
- Loading branch information
Showing
8 changed files
with
139 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export { uppercaseMatcher } from './matchers/uppercaseMatcher'; | ||
export { minLengthMatcher } from './matchers/minLengthMatcher'; | ||
export { lowercaseMatcher } from './matchers/lowercaseMatcher'; | ||
export { numberMatcher } from './matchers/numberMatcher'; | ||
export { specialMatcher } from './matchers/specialMatcher'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Matcher, Match } from '@zxcvbn-ts/core/dist/types'; | ||
|
||
export const minLengthMatcher = (minLength: number): Matcher => ({ | ||
Matching: class MinLengthMatcher { | ||
match({ password }: { password: string }): Match[] { | ||
const matches: Match[] = []; | ||
if (password.length < minLength) { | ||
matches.push({ pattern: 'minLength', token: password, i: 0, j: password.length - 1 }); | ||
} | ||
return matches; | ||
} | ||
}, | ||
feedback(_match) { | ||
return { warning: `Password may not be shorter than ${minLength} characters.`, suggestions: [] }; | ||
}, | ||
scoring(_match) { | ||
return -100; | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,120 @@ | ||
import 'mocha'; | ||
import { expect } from 'chai'; | ||
import { uppercaseMatcher, lowercaseMatcher, numberMatcher, specialMatcher } from '../../src'; | ||
import { uppercaseMatcher, lowercaseMatcher, numberMatcher, specialMatcher, minLengthMatcher } from '../../src'; | ||
|
||
describe('character requirements matchers and feedback', () => { | ||
describe('uppercaseMatcher', () => { | ||
it('should return a match for missing uppercase letters', () => { | ||
const result = uppercaseMatcher.Matching.prototype.match({ password: 'password123!' }); | ||
expect(result).to.deep.include({ pattern: 'uppercase', token: 'password123!', i: 0, j: 11 }); | ||
}); | ||
|
||
it('should return a match for missing lowercase letters', () => { | ||
const result = lowercaseMatcher.Matching.prototype.match({ password: 'PASSWORD123!' }); | ||
expect(result).to.deep.include({ pattern: 'lowercase', token: 'PASSWORD123!', i: 0, j: 11 }); | ||
}); | ||
|
||
it('should return a match for missing numbers', () => { | ||
const result = numberMatcher.Matching.prototype.match({ password: 'Password!' }); | ||
expect(result).to.deep.include({ pattern: 'number', token: 'Password!', i: 0, j: 8 }); | ||
}); | ||
|
||
it('should return a match for missing special characters', () => { | ||
const result = specialMatcher.Matching.prototype.match({ password: 'Password123' }); | ||
expect(result).to.deep.include({ pattern: 'special', token: 'Password123', i: 0, j: 10 }); | ||
}); | ||
|
||
it('should return no matches for a password meeting all requirements', () => { | ||
const resultUppercase = uppercaseMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultLowercase = lowercaseMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultNumber = numberMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultSpecial = specialMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
|
||
expect(resultUppercase).to.be.empty; | ||
expect(resultLowercase).to.be.empty; | ||
expect(resultNumber).to.be.empty; | ||
expect(resultSpecial).to.be.empty; | ||
}); | ||
|
||
it('should provide correct feedback for missing uppercase letters', () => { | ||
const match = { pattern: 'uppercase', token: 'password123!', i: 0, j: 11, guesses: 1, guessesLog10: 0 }; | ||
const feedback = uppercaseMatcher.feedback(match); | ||
expect(feedback.warning).to.equal('Include at least one uppercase letter.'); | ||
}); | ||
|
||
it('should return a score of -100 for missing uppercase letters', () => { | ||
const match = { pattern: 'uppercase', token: 'password123!', i: 0, j: 11 }; | ||
const score = uppercaseMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
}); | ||
}); | ||
|
||
describe('lowercaseMatcher', () => { | ||
it('should return a match for missing lowercase letters', () => { | ||
const result = lowercaseMatcher.Matching.prototype.match({ password: 'PASSWORD123!' }); | ||
expect(result).to.deep.include({ pattern: 'lowercase', token: 'PASSWORD123!', i: 0, j: 11 }); | ||
}); | ||
|
||
it('should provide correct feedback for missing lowercase letters', () => { | ||
const match = { pattern: 'lowercase', token: 'PASSWORD123!', i: 0, j: 11, guesses: 1, guessesLog10: 0 }; | ||
const feedback = lowercaseMatcher.feedback(match); | ||
expect(feedback.warning).to.equal('Include at least one lowercase letter.'); | ||
}); | ||
|
||
it('should return a score of -100 for missing lowercase letters', () => { | ||
const match = { pattern: 'lowercase', token: 'PASSWORD123!', i: 0, j: 11 }; | ||
const score = lowercaseMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
}); | ||
}); | ||
|
||
describe('numberMatcher', () => { | ||
it('should return a match for missing numbers', () => { | ||
const result = numberMatcher.Matching.prototype.match({ password: 'Password!' }); | ||
expect(result).to.deep.include({ pattern: 'number', token: 'Password!', i: 0, j: 8 }); | ||
}); | ||
|
||
it('should provide correct feedback for missing numbers', () => { | ||
const match = { pattern: 'number', token: 'Password!', i: 0, j: 8, guesses: 1, guessesLog10: 0 }; | ||
const feedback = numberMatcher.feedback(match); | ||
expect(feedback.warning).to.equal('Include at least one number.'); | ||
}); | ||
|
||
it('should return a score of -100 for missing numbers', () => { | ||
const match = { pattern: 'number', token: 'Password!', i: 0, j: 8 }; | ||
const score = numberMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
}); | ||
}); | ||
|
||
describe('specialMatcher', () => { | ||
it('should return a match for missing special characters', () => { | ||
const result = specialMatcher.Matching.prototype.match({ password: 'Password123' }); | ||
expect(result).to.deep.include({ pattern: 'special', token: 'Password123', i: 0, j: 10 }); | ||
}); | ||
|
||
it('should provide correct feedback for missing special characters', () => { | ||
const match = { pattern: 'special', token: 'Password123', i: 0, j: 10, guesses: 1, guessesLog10: 0 }; | ||
const feedback = specialMatcher.feedback(match); | ||
expect(feedback.warning).to.equal('Include at least one special character.'); | ||
}); | ||
|
||
it('should return a score of -100 for missing uppercase letters', () => { | ||
const match = { pattern: 'uppercase', token: 'password123!', i: 0, j: 11 }; | ||
const score = uppercaseMatcher.scoring(match); | ||
it('should return a score of -100 for missing special characters', () => { | ||
const match = { pattern: 'special', token: 'Password123', i: 0, j: 10 }; | ||
const score = specialMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
}); | ||
}); | ||
|
||
it('should return a score of -100 for missing lowercase letters', () => { | ||
const match = { pattern: 'lowercase', token: 'PASSWORD123!', i: 0, j: 11 }; | ||
const score = lowercaseMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
describe('minLengthMatcher', () => { | ||
const minLength = 10; | ||
const matcher = minLengthMatcher(minLength); | ||
|
||
it('should return a match for passwords shorter than the minimum length', () => { | ||
const result = matcher.Matching.prototype.match({ password: 'short' }); | ||
expect(result).to.deep.include({ pattern: 'minLength', token: 'short', i: 0, j: 4 }); | ||
}); | ||
|
||
it('should return a score of -100 for missing numbers', () => { | ||
const match = { pattern: 'number', token: 'Password!', i: 0, j: 8 }; | ||
const score = numberMatcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
it('should return no matches for passwords meeting the minimum length', () => { | ||
const result = matcher.Matching.prototype.match({ password: 'longenoughpassword' }); | ||
expect(result).to.be.empty; | ||
}); | ||
|
||
it('should return a score of -100 for missing special characters', () => { | ||
const match = { pattern: 'special', token: 'Password123', i: 0, j: 10 }; | ||
const score = specialMatcher.scoring(match); | ||
it('should provide correct feedback for passwords shorter than the minimum length', () => { | ||
const match = { pattern: 'minLength', token: 'short', i: 0, j: 4, guesses: 1, guessesLog10: 0 }; | ||
const feedback = matcher.feedback(match); | ||
expect(feedback.warning).to.equal(`Password may not be shorter than ${minLength} characters.`); | ||
}); | ||
|
||
it('should return a score of -100 for passwords shorter than the minimum length', () => { | ||
const match = { pattern: 'minLength', token: 'short', i: 0, j: 4 }; | ||
const score = matcher.scoring(match); | ||
expect(score).to.equal(-100); | ||
}); | ||
}); | ||
|
||
describe('multiple matchers', () => { | ||
it('should return no matches for a password meeting all requirements', () => { | ||
const resultUppercase = uppercaseMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultLowercase = lowercaseMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultNumber = numberMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
const resultSpecial = specialMatcher.Matching.prototype.match({ password: 'Password123!' }); | ||
|
||
expect(resultUppercase).to.be.empty; | ||
expect(resultLowercase).to.be.empty; | ||
expect(resultNumber).to.be.empty; | ||
expect(resultSpecial).to.be.empty; | ||
}); | ||
}); |