Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions packages/optimus-ui/src/password/password.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ class TestPasswordRefTemplateComponent {

@Component({
standalone: false,
template: ` <input type="password" pPassword [(ngModel)]="value" [feedback]="feedback" [promptLabel]="promptLabel" [weakLabel]="weakLabel" [mediumLabel]="mediumLabel" [strongLabel]="strongLabel" /> `
template: `
<input type="password" pPassword [(ngModel)]="value" [feedback]="feedback" [promptLabel]="promptLabel" [weakLabel]="weakLabel" [mediumLabel]="mediumLabel" [strongLabel]="strongLabel" [mediumRegex]="mediumRegex" [strongRegex]="strongRegex" />
`
})
class TestPasswordDirectiveComponent {
value: string | null = null as any;
Expand All @@ -223,6 +225,8 @@ class TestPasswordDirectiveComponent {
weakLabel: string = 'Weak';
mediumLabel: string = 'Medium';
strongLabel: string = 'Strong';
mediumRegex: string = '^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})';
strongRegex: string = '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})';
}

@Component({
Expand Down Expand Up @@ -1290,22 +1294,26 @@ describe('PasswordDirective', () => {
expect(directive.mediumLabel).toBe('Medium');
expect(directive.strongLabel).toBe('Strong');
expect(directive.feedback).toBe(true);
expect(directive.mediumRegex).toContain('(?=.*[a-z])(?=.*[A-Z])');
expect(directive.strongRegex).toContain('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])');
});
});

describe('Password Strength Algorithm', () => {
it('should test strength correctly', () => {
it('should test strength correctly using the default medium/strong regex', () => {
expect(directive.testStrength('')).toBe(0);
expect(directive.testStrength('a')).toBeGreaterThan(0);
expect(directive.testStrength('aA')).toBeGreaterThan(directive.testStrength('a'));
expect(directive.testStrength('aA1')).toBeGreaterThan(directive.testStrength('aA'));
expect(directive.testStrength('aA1!')).toBeGreaterThan(directive.testStrength('aA1'));
expect(directive.testStrength('abc')).toBe(1);
expect(directive.testStrength('abcDEF')).toBe(2);
expect(directive.testStrength('abcDEF123')).toBe(3);
});

it('should normalize values correctly', () => {
expect(directive.normalize(1, 2)).toBe(0.5);
expect(directive.normalize(2, 2)).toBe(1);
expect(directive.normalize(3, 2)).toBeGreaterThan(1);
it('should respect custom mediumRegex and strongRegex inputs', () => {
directive.mediumRegex = '^(?=.{4,})';
directive.strongRegex = '^(?=.{8,})';

expect(directive.testStrength('abc')).toBe(1);
expect(directive.testStrength('abcd')).toBe(2);
expect(directive.testStrength('abcdefgh')).toBe(3);
});
});

Expand Down
46 changes: 19 additions & 27 deletions packages/optimus-ui/src/password/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ export class PasswordDirective extends BaseEditableHolder {
* @group Props
*/
@Input() strongLabel: string = 'Strong';
/**
* Regex value for medium regex.
* @group Props
*/
@Input() mediumRegex: string = '^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})';
/**
* Regex value for strong regex.
* @group Props
*/
@Input() strongRegex: string = '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})';
/**
* Whether to show the strength indicator or not.
* @group Props
Expand Down Expand Up @@ -272,15 +282,15 @@ export class PasswordDirective extends BaseEditableHolder {
label = this.promptLabel;
meterPos = '0px 0px';
} else {
var score = this.testStrength(value);
var level = this.testStrength(value);

if (score < 30) {
if (level === 1) {
label = this.weakLabel;
meterPos = '0px -10px';
} else if (score >= 30 && score < 80) {
} else if (level === 2) {
label = this.mediumLabel;
meterPos = '0px -20px';
} else if (score >= 80) {
} else if (level === 3) {
label = this.strongLabel;
meterPos = '0px -30px';
}
Expand Down Expand Up @@ -324,31 +334,13 @@ export class PasswordDirective extends BaseEditableHolder {
}

testStrength(str: string) {
let grade: number = 0;
let val: Nullable<RegExpMatchArray>;

val = str.match('[0-9]');
grade += this.normalize(val ? val.length : 1 / 4, 1) * 25;

val = str.match('[a-zA-Z]');
grade += this.normalize(val ? val.length : 1 / 2, 3) * 10;

val = str.match('[!@#$%^&*?_~.,;=]');
grade += this.normalize(val ? val.length : 1 / 6, 1) * 35;

val = str.match('[A-Z]');
grade += this.normalize(val ? val.length : 1 / 6, 1) * 30;

grade *= str.length / 8;

return grade > 100 ? 100 : grade;
}
let level = 0;

normalize(x: number, y: number) {
let diff = x - y;
if (new RegExp(this.strongRegex).test(str)) level = 3;
else if (new RegExp(this.mediumRegex).test(str)) level = 2;
else if (str.length) level = 1;

if (diff <= 0) return x / y;
else return 1 + 0.5 * (x / (x + y / 4));
return level;
}

bindScrollListener() {
Expand Down