Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / pass / lib / password / random.spec.ts
blob8b76a5d2c5af69eaf65aaf6a5cd1dbf92b24450d
1 import { specialChars } from './constants';
2 import type { RandomPasswordOptions } from './random';
3 import { generateRandomPassword } from './random';
5 const passwordMatchesOptions = (password: string, options: RandomPasswordOptions) => {
6     const validLength = password.length === options.length;
7     const validUppercase = !options.useUppercase || /[A-Z]/.test(password);
8     const validDigits = !options.useDigits || /[0-9]/.test(password);
9     const validSpecialChars =
10         !options.useSpecialChars ||
11         new RegExp(
12             specialChars
13                 .split('')
14                 .map((char) => (specialChars.includes(char) ? `\\${char}` : char))
15                 .join('|')
16         ).test(password);
18     return validLength && validUppercase && validDigits && validSpecialChars;
21 describe('random password generator', () => {
22     test('should throw if password length is smaller than 4', () => {
23         expect(() =>
24             generateRandomPassword({
25                 length: 3,
26                 useDigits: true,
27                 useSpecialChars: true,
28                 useUppercase: true,
29             })
30         ).toThrow();
32         expect(() =>
33             generateRandomPassword({
34                 length: 0,
35                 useDigits: true,
36                 useSpecialChars: true,
37                 useUppercase: true,
38             })
39         ).toThrow();
40     });
42     test('supports lowercase mode', () => {
43         const options: RandomPasswordOptions[] = [
44             { length: 4, useDigits: false, useSpecialChars: false, useUppercase: false },
45             { length: 5, useDigits: false, useSpecialChars: false, useUppercase: false },
46             { length: 10, useDigits: false, useSpecialChars: false, useUppercase: false },
47             { length: 15, useDigits: false, useSpecialChars: false, useUppercase: false },
48             { length: 20, useDigits: false, useSpecialChars: false, useUppercase: false },
49         ];
51         options.forEach((option) => expect(passwordMatchesOptions(generateRandomPassword(option), option)).toBe(true));
52     });
54     test('supports uppercase mode', () => {
55         const options: RandomPasswordOptions[] = [
56             { length: 4, useDigits: false, useSpecialChars: false, useUppercase: true },
57             { length: 5, useDigits: false, useSpecialChars: false, useUppercase: true },
58             { length: 10, useDigits: false, useSpecialChars: false, useUppercase: true },
59             { length: 15, useDigits: false, useSpecialChars: false, useUppercase: true },
60             { length: 20, useDigits: false, useSpecialChars: false, useUppercase: true },
61         ];
63         options.forEach((option) => expect(passwordMatchesOptions(generateRandomPassword(option), option)).toBe(true));
64     });
66     test('supports digits mode', () => {
67         const options: RandomPasswordOptions[] = [
68             { length: 4, useDigits: true, useSpecialChars: false, useUppercase: false },
69             { length: 5, useDigits: true, useSpecialChars: false, useUppercase: false },
70             { length: 10, useDigits: true, useSpecialChars: false, useUppercase: false },
71             { length: 15, useDigits: true, useSpecialChars: false, useUppercase: false },
72             { length: 20, useDigits: true, useSpecialChars: false, useUppercase: false },
73         ];
75         options.forEach((option) => expect(passwordMatchesOptions(generateRandomPassword(option), option)).toBe(true));
76     });
78     test('supports special mode', () => {
79         const options: RandomPasswordOptions[] = [
80             { length: 4, useDigits: false, useSpecialChars: true, useUppercase: false },
81             { length: 5, useDigits: false, useSpecialChars: true, useUppercase: false },
82             { length: 10, useDigits: false, useSpecialChars: true, useUppercase: false },
83             { length: 15, useDigits: false, useSpecialChars: true, useUppercase: false },
84             { length: 20, useDigits: false, useSpecialChars: true, useUppercase: false },
85         ];
87         options.forEach((option) => expect(passwordMatchesOptions(generateRandomPassword(option), option)).toBe(true));
88     });
90     test('supports combinations', () => {
91         const options: RandomPasswordOptions[] = [
92             { length: 4, useDigits: true, useSpecialChars: true, useUppercase: true },
93             { length: 5, useDigits: false, useSpecialChars: true, useUppercase: true },
94             { length: 10, useDigits: true, useSpecialChars: false, useUppercase: true },
95             { length: 15, useDigits: true, useSpecialChars: true, useUppercase: false },
96             { length: 20, useDigits: true, useSpecialChars: true, useUppercase: true },
97         ];
99         options.forEach((option) => expect(passwordMatchesOptions(generateRandomPassword(option), option)).toBe(true));
100     });
102     test('password validation recursion sanity check', () => {
103         const options: RandomPasswordOptions[] = Array.from({ length: 1000 }, () => ({
104             length: 4,
105             useDigits: true,
106             useSpecialChars: true,
107             useUppercase: true,
108         }));
110         options.forEach((option) => {
111             let counter = 0;
112             generateRandomPassword(option, () => counter++);
113             expect(counter).toBeLessThan(100);
114         });
115     });