1 import { SeperatorOptions, digitChars, specialChars } from './constants';
2 import type { MemorablePasswordOptions } from './memorable';
3 import { generateMemorablePassword } from './memorable';
4 import WORD_LIST from './wordlist.json';
6 const WORDS = Object.values(WORD_LIST);
8 const wordsInList = (words: string[], options: MemorablePasswordOptions) =>
9 words.every((word) => {
10 let originalWord = word.toLowerCase();
11 if (options.extraNumbers) originalWord = originalWord.slice(0, -1);
12 return WORDS.includes(originalWord);
15 const wordsMatchCapitalization = (words: string[], options: MemorablePasswordOptions) =>
16 words.every((word) => (options.capitalize ? /[A-Z]/.test(word[0]) : true));
18 const wordsMatchExtraNumbers = (words: string[], options: MemorablePasswordOptions) =>
19 words.every((word) => (options.extraNumbers ? /[0-9]/.test(word[word.length - 1]) : true));
21 describe('memorable password generator', () => {
22 test('should throw if wordCount is 0', () => {
24 generateMemorablePassword({
26 seperator: SeperatorOptions.HYPHEN,
33 test('generate from wordlist with basic seperators', () => {
34 const options: MemorablePasswordOptions[] = [
35 { wordCount: 1, seperator: SeperatorOptions.HYPHEN, capitalize: false, extraNumbers: false },
36 { wordCount: 4, seperator: SeperatorOptions.HYPHEN, capitalize: false, extraNumbers: false },
37 { wordCount: 4, seperator: SeperatorOptions.COMMA, capitalize: true, extraNumbers: false },
38 { wordCount: 4, seperator: SeperatorOptions.PERIOD, capitalize: false, extraNumbers: false },
39 { wordCount: 4, seperator: SeperatorOptions.UNDERSCORE, capitalize: false, extraNumbers: false },
40 { wordCount: 4, seperator: SeperatorOptions.SPACE, capitalize: false, extraNumbers: false },
41 { wordCount: 5, seperator: SeperatorOptions.HYPHEN, capitalize: true, extraNumbers: false },
42 { wordCount: 10, seperator: SeperatorOptions.SPACE, capitalize: false, extraNumbers: false },
43 { wordCount: 15, seperator: SeperatorOptions.UNDERSCORE, capitalize: true, extraNumbers: false },
46 options.forEach((options) => {
47 const result = generateMemorablePassword(options);
48 const words = result.split(options.seperator);
49 expect(result).toBeDefined();
50 expect(words.length).toEqual(options.wordCount);
51 expect(wordsInList(words, options)).toBe(true);
52 expect(wordsMatchCapitalization(words, options)).toBe(true);
53 expect(wordsMatchExtraNumbers(words, options)).toBe(true);
57 test('generate from wordlist with random number seperator', () => {
58 const options: MemorablePasswordOptions[] = [
59 { wordCount: 4, seperator: SeperatorOptions.NUMBER, capitalize: false, extraNumbers: false },
60 { wordCount: 5, seperator: SeperatorOptions.NUMBER, capitalize: true, extraNumbers: false },
61 { wordCount: 10, seperator: SeperatorOptions.NUMBER, capitalize: true, extraNumbers: false },
62 { wordCount: 15, seperator: SeperatorOptions.NUMBER, capitalize: false, extraNumbers: false },
65 options.forEach((options) => {
66 const result = generateMemorablePassword(options);
67 const words = result.split(new RegExp(digitChars.split('').join('|')));
68 expect(result).toBeDefined();
69 expect(words.length).toEqual(options.wordCount);
70 expect(wordsInList(words, options)).toBe(true);
71 expect(wordsMatchCapitalization(words, options)).toBe(true);
72 expect(wordsMatchExtraNumbers(words, options)).toBe(true);
76 test('generate from wordlist with random number or symbol seperator', () => {
77 const options: MemorablePasswordOptions[] = [
78 { wordCount: 4, seperator: SeperatorOptions.NUMBER_OR_SYMBOL, capitalize: false, extraNumbers: false },
79 { wordCount: 5, seperator: SeperatorOptions.NUMBER_OR_SYMBOL, capitalize: true, extraNumbers: false },
80 { wordCount: 10, seperator: SeperatorOptions.NUMBER_OR_SYMBOL, capitalize: true, extraNumbers: false },
81 { wordCount: 15, seperator: SeperatorOptions.NUMBER_OR_SYMBOL, capitalize: false, extraNumbers: false },
84 options.forEach((options) => {
85 const result = generateMemorablePassword(options);
86 const words = result.split(
91 .map((char) => (specialChars.includes(char) ? `\\${char}` : char))
95 expect(result).toBeDefined();
96 expect(words.length).toEqual(options.wordCount);
97 expect(wordsInList(words, options)).toBe(true);
98 expect(wordsMatchCapitalization(words, options)).toBe(true);
99 expect(wordsMatchExtraNumbers(words, options)).toBe(true);
103 test('should support interpolating extra numbers', () => {
104 const options: MemorablePasswordOptions[] = [
105 { wordCount: 1, seperator: SeperatorOptions.HYPHEN, capitalize: false, extraNumbers: true },
106 { wordCount: 4, seperator: SeperatorOptions.HYPHEN, capitalize: false, extraNumbers: true },
107 { wordCount: 4, seperator: SeperatorOptions.COMMA, capitalize: true, extraNumbers: true },
108 { wordCount: 4, seperator: SeperatorOptions.PERIOD, capitalize: false, extraNumbers: true },
109 { wordCount: 4, seperator: SeperatorOptions.UNDERSCORE, capitalize: false, extraNumbers: true },
110 { wordCount: 4, seperator: SeperatorOptions.SPACE, capitalize: false, extraNumbers: true },
111 { wordCount: 5, seperator: SeperatorOptions.HYPHEN, capitalize: true, extraNumbers: true },
112 { wordCount: 10, seperator: SeperatorOptions.SPACE, capitalize: false, extraNumbers: true },
113 { wordCount: 15, seperator: SeperatorOptions.UNDERSCORE, capitalize: true, extraNumbers: true },
116 options.forEach((options) => {
117 const result = generateMemorablePassword(options);
118 const words = result.split(options.seperator);
119 expect(result).toBeDefined();
120 expect(words.length).toEqual(options.wordCount);
121 expect(wordsInList(words, options)).toBe(true);
122 expect(wordsMatchCapitalization(words, options)).toBe(true);
123 expect(wordsMatchExtraNumbers(words, options)).toBe(true);