Skip to content

Commit

Permalink
feat(Password): Remove symbol password requirement
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffreykwan committed Sep 13, 2023
1 parent 9b3ef8c commit 6cf5461
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 60 deletions.
9 changes: 1 addition & 8 deletions src/app/domain/password/password-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@ export class PasswordErrors {
messageCode: string = 'invalidPassword';
missingLetter: boolean;
missingNumber: boolean;
missingSymbol: boolean;
tooShort: boolean;

constructor(
missingLetter: boolean,
missingNumber: boolean,
missingSymbol: boolean,
tooShort: boolean
) {
constructor(missingLetter: boolean, missingNumber: boolean, tooShort: boolean) {
this.missingLetter = missingLetter;
this.missingNumber = missingNumber;
this.missingSymbol = missingSymbol;
this.tooShort = tooShort;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StudentService } from '../../../student/student.service';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { PasswordModule } from '../../../password/password.module';
import { PasswordRequirementComponent } from '../../../password/password-requirement/password-requirement.component';

export class MockStudentService {
changePassword(
Expand All @@ -26,8 +27,6 @@ export class MockStudentService {
}
}

const PASSWORD = 'abcd1234!';

describe('ForgotStudentPasswordChangeComponent', () => {
let component: ForgotStudentPasswordChangeComponent;
let fixture: ComponentFixture<ForgotStudentPasswordChangeComponent>;
Expand Down Expand Up @@ -65,8 +64,9 @@ describe('ForgotStudentPasswordChangeComponent', () => {
});

it('should enable the submit button when the password fields are filled in', () => {
component.changePasswordFormGroup.controls['newPassword'].setValue(PASSWORD);
component.changePasswordFormGroup.controls['confirmNewPassword'].setValue(PASSWORD);
const password = PasswordRequirementComponent.VALID_PASSWORD;
component.changePasswordFormGroup.controls['newPassword'].setValue(password);
component.changePasswordFormGroup.controls['confirmNewPassword'].setValue(password);
fixture.detectChanges();
const submitButton = getSubmitButton();
expect(submitButton.disabled).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PasswordModule } from '../../../password/password.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider';
import { PasswordRequirementComponent } from '../../../password/password-requirement/password-requirement.component';

export class MockTeacherService {
changePassword(
Expand Down Expand Up @@ -100,7 +101,7 @@ describe('ForgotTeacherPasswordChangeComponent', () => {
const navigateSpy = spyOn(router, 'navigate');
component.username = 'SpongebobSquarepants';
component.verificationCode = '123456';
const newPassword = 'Abcd1234';
const newPassword = PasswordRequirementComponent.VALID_PASSWORD;
component.changePasswordFormGroup.controls['newPassword'].setValue(newPassword);
component.changePasswordFormGroup.controls['confirmNewPassword'].setValue(newPassword);
component.submit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { MatDialogModule } from '@angular/material/dialog';
import { PasswordModule } from '../../../password/password.module';
import { MatIconModule } from '@angular/material/icon';
import { PasswordErrors } from '../../../domain/password/password-errors';
import { PasswordRequirementComponent } from '../../../password/password-requirement/password-requirement.component';

const CORRECT_OLD_PASSWORD = 'correctOldPassword123';
const INCORRECT_OLD_PASSWORD = 'incorrectOldPassword123';
const INVALID_PASSWORD_PATTERN = 'abcd1234';
const NEW_PASSWORD_1 = 'abcd111!';
const NEW_PASSWORD_2 = 'abcd222!';
const INVALID_PASSWORD = PasswordRequirementComponent.INVALID_PASSWORD_TOO_SHORT;
const NEW_PASSWORD_1 = PasswordRequirementComponent.VALID_PASSWORD;
const NEW_PASSWORD_2 = PasswordRequirementComponent.VALID_PASSWORD + '!';

export class MockUserService {
getUser(): BehaviorSubject<User> {
Expand Down Expand Up @@ -127,13 +128,13 @@ function oldPasswordIncorrect_disableSubmitButtonAndShowError() {
function saveChanges_newPasswordPatternInvalid_ShowError() {
describe('server returns response that says new password is not valid', () => {
it('shows the new password error messages and disables the submit button', async () => {
const passwordErrors = new PasswordErrors(false, false, true, false);
const passwordErrors = new PasswordErrors(false, false, true);
spyOn(TestBed.inject(UserService), 'changePassword').and.returnValue(
generateErrorObservable(passwordErrors)
);
setPasswords(CORRECT_OLD_PASSWORD, INVALID_PASSWORD_PATTERN, INVALID_PASSWORD_PATTERN);
setPasswords(CORRECT_OLD_PASSWORD, INVALID_PASSWORD, INVALID_PASSWORD);
component.saveChanges();
expectPasswordErrors(false, false, true, false);
expectPasswordErrors(false, false, true);
expectSubmitButtonDisabled();
});
});
Expand All @@ -142,7 +143,6 @@ function saveChanges_newPasswordPatternInvalid_ShowError() {
function expectPasswordErrors(
missingLetter: boolean,
missingNumber: boolean,
missingSymbol: boolean,
tooShort: boolean
): void {
expect(component.newPasswordFormGroup.get('newPassword').getError('missingLetter')).toBe(
Expand All @@ -151,9 +151,6 @@ function expectPasswordErrors(
expect(component.newPasswordFormGroup.get('newPassword').getError('missingNumber')).toBe(
missingNumber
);
expect(component.newPasswordFormGroup.get('newPassword').getError('missingSymbol')).toBe(
missingSymbol
);
expect(component.newPasswordFormGroup.get('newPassword').getError('tooShort')).toBe(tooShort);
}

Expand Down Expand Up @@ -192,7 +189,7 @@ function unlinkGoogleButtonClick_showDialog() {
function invalidPassword_showError() {
describe('new password does not satisfy the requirements', () => {
it('shows password errors and disables submit button', async () => {
setPasswords(CORRECT_OLD_PASSWORD, INVALID_PASSWORD_PATTERN, INVALID_PASSWORD_PATTERN);
setPasswords(CORRECT_OLD_PASSWORD, INVALID_PASSWORD, INVALID_PASSWORD);
expect(component.newPasswordFormGroup.get('newPassword').errors).not.toBeNull();
expectSubmitButtonDisabled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { of } from 'rxjs';
import { PasswordModule } from '../../../password/password.module';
import { UserService } from '../../../services/user.service';
import { UnlinkGoogleAccountPasswordComponent } from './unlink-google-account-password.component';
import { PasswordRequirementComponent } from '../../../password/password-requirement/password-requirement.component';

class MockUserService {
unlinkGoogleUser(newPassword: string) {
Expand Down Expand Up @@ -45,7 +46,7 @@ describe('UnlinkGoogleAccountPasswordComponent', () => {
function formSubmit_callUserServiceUnlinkGoogleUserFunction() {
it('should call UserService.UnlinkGoogleUserFunction when form is submitted', () => {
const unlinkFunctionSpy = spyOn(userService, 'unlinkGoogleUser').and.returnValue(of({}));
const newPassword = 'Abcd1234';
const newPassword = PasswordRequirementComponent.VALID_PASSWORD;
component.newPasswordFormGroup.setValue({
newPassword: newPassword,
confirmNewPassword: newPassword
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { NewPasswordAndConfirmHarness } from './new-password-and-confirm.harness';
import { PasswordModule } from '../password.module';
import { PasswordErrors } from '../../domain/password/password-errors';
import { PasswordRequirementComponent } from '../password-requirement/password-requirement.component';

let component: NewPasswordAndConfirmComponent;
let fixture: ComponentFixture<NewPasswordAndConfirmComponent>;
const INVALID_PASSWORD_MISSING_LETTER = '1234567!';
const INVALID_PASSWORD_MISSING_NUMBER = 'abcdefg!';
const INVALID_PASSWORD_MISSING_SYMBOL = 'abcd1234';
const INVALID_PASSWORD_TOO_SHORT = 'abcd12!';
let newPasswordAndConfirmHarness: NewPasswordAndConfirmHarness;
const VALID_PASSWORD = 'abcd123!';

describe('NewPasswordAndConfirmComponent', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -62,34 +58,40 @@ function passwordIsMissing(): void {

function passwordIsValid(): void {
describe('password is valid', () => {
it('does not show any error', async () => {
await newPasswordAndConfirmHarness.setNewPassword(VALID_PASSWORD);
expect(await newPasswordAndConfirmHarness.isNewPasswordRequiredErrorDisplayed()).toBeFalse();
describe('password contains letters and numbers', () => {
it('does not show any error', async () => {
await setPasswordAndExpectNoErrors(PasswordRequirementComponent.VALID_PASSWORD);
});
});
describe('password contains letters, numbers, and symbols', () => {
it('does not show any error', async () => {
await setPasswordAndExpectNoErrors(PasswordRequirementComponent.VALID_PASSWORD + '!$');
});
});
});
}

async function setPasswordAndExpectNoErrors(password: string): Promise<void> {
await newPasswordAndConfirmHarness.setNewPassword(password);
expect(await newPasswordAndConfirmHarness.isNewPasswordRequiredErrorDisplayed()).toBeFalse();
}

function passwordErrorCases(): void {
const errorCases = [
{
descriptionText: 'missing a letter',
password: INVALID_PASSWORD_MISSING_LETTER,
expectedErrors: new PasswordErrors(true, false, false, false)
password: PasswordRequirementComponent.INVALID_PASSWORD_MISSING_LETTER,
expectedErrors: new PasswordErrors(true, false, false)
},
{
descriptionText: 'missing a number',
password: INVALID_PASSWORD_MISSING_NUMBER,
expectedErrors: new PasswordErrors(false, true, false, false)
},
{
descriptionText: 'missing a symbol',
password: INVALID_PASSWORD_MISSING_SYMBOL,
expectedErrors: new PasswordErrors(false, false, true, false)
password: PasswordRequirementComponent.INVALID_PASSWORD_MISSING_NUMBER,
expectedErrors: new PasswordErrors(false, true, false)
},
{
descriptionText: 'too short',
password: INVALID_PASSWORD_TOO_SHORT,
expectedErrors: new PasswordErrors(false, false, false, true)
password: PasswordRequirementComponent.INVALID_PASSWORD_TOO_SHORT,
expectedErrors: new PasswordErrors(false, false, true)
}
];
errorCases.forEach(({ descriptionText, password, expectedErrors }) => {
Expand All @@ -107,7 +109,6 @@ function passwordErrorCases(): void {
async function checkPasswordRequirements(passwordErrors: PasswordErrors): Promise<void> {
expect(await newPasswordAndConfirmHarness.isMissingLetter()).toBe(passwordErrors.missingLetter);
expect(await newPasswordAndConfirmHarness.isMissingNumber()).toBe(passwordErrors.missingNumber);
expect(await newPasswordAndConfirmHarness.isMissingSymbol()).toBe(passwordErrors.missingSymbol);
expect(await newPasswordAndConfirmHarness.isTooShort()).toBe(passwordErrors.tooShort);
}

Expand All @@ -124,8 +125,9 @@ function confirmPasswordValidation() {

describe('passwords match', () => {
it('does not show any error', async () => {
await newPasswordAndConfirmHarness.setNewPassword(VALID_PASSWORD);
await newPasswordAndConfirmHarness.setConfirmNewPassword(VALID_PASSWORD);
const password = PasswordRequirementComponent.VALID_PASSWORD;
await newPasswordAndConfirmHarness.setNewPassword(password);
await newPasswordAndConfirmHarness.setConfirmNewPassword(password);
expect(
await newPasswordAndConfirmHarness.isConfirmNewPasswordDoesNotMatchErrorDisplayed()
).toBeFalse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ export class NewPasswordAndConfirmComponent implements OnInit {
protected passwordRequirements: any[] = [
{ errorFieldName: 'missingLetter', text: $localize`include a letter` },
{ errorFieldName: 'missingNumber', text: $localize`include a number` },
{
errorFieldName: 'missingSymbol',
text: $localize`include one of these symbols ! @ # $ % ^ & *`
},
{ errorFieldName: 'tooShort', text: $localize`be at least 8 characters long` }
];
protected passwordStrength: number = 0;
Expand All @@ -59,14 +55,12 @@ export class NewPasswordAndConfirmComponent implements OnInit {
const value = control.value ? control.value : '';
const hasLetter = /[a-zA-Z]+/.test(value);
const hasNumber = /[0-9]+/.test(value);
const hasSymbol = /[!@#$%^&*]+/.test(value);
const appropriateLength = value.length >= this.PASSWORD_MIN_LENGTH;
const passwordValid = hasLetter && hasNumber && hasSymbol && appropriateLength;
const passwordValid = hasLetter && hasNumber && appropriateLength;
return !passwordValid
? {
missingLetter: !hasLetter,
missingNumber: !hasNumber,
missingSymbol: !hasSymbol,
tooShort: !appropriateLength
}
: null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ export class NewPasswordAndConfirmHarness extends ComponentHarness {
return this.isMissingRequirement('include a number');
}

async isMissingSymbol(): Promise<boolean> {
return this.isMissingRequirement('include one of these symbols ! @ # $ % ^ & *');
}

async isTooShort(): Promise<boolean> {
return this.isMissingRequirement('be at least 8 characters long');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { FormControl } from '@angular/forms';
styleUrls: ['./password-requirement.component.scss']
})
export class PasswordRequirementComponent {
public static INVALID_PASSWORD_MISSING_LETTER = '12345678';
public static INVALID_PASSWORD_MISSING_NUMBER = 'abcdefgh';
public static INVALID_PASSWORD_TOO_SHORT = 'abcd123';
public static VALID_PASSWORD = 'abcd1234';

@Input() errorFieldName: string;
@Input() passwordFormControl: FormControl;
@Input() requirementText: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import { BrowserModule, By } from '@angular/platform-browser';
import { RecaptchaV3Module, ReCaptchaV3Service, RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha';
import { PasswordModule } from '../../password/password.module';
import { ConfigService } from '../../services/config.service';
import { PasswordRequirementComponent } from '../../password/password-requirement/password-requirement.component';

let router: Router;
let component: RegisterStudentFormComponent;
let configService: ConfigService;
let fixture: ComponentFixture<RegisterStudentFormComponent>;
const PASSWORD: string = 'abcd1234!';
const PASSWORD: string = PasswordRequirementComponent.VALID_PASSWORD;
let recaptchaV3Service: ReCaptchaV3Service;
let studentService: StudentService;
let snackBar: MatSnackBar;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { By } from '@angular/platform-browser';
import { RecaptchaV3Module, ReCaptchaV3Service, RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha';
import { PasswordModule } from '../../password/password.module';
import { ConfigService } from '../../services/config.service';
import { PasswordRequirementComponent } from '../../password/password-requirement/password-requirement.component';

class MockTeacherService {
registerTeacherAccount() {}
Expand All @@ -35,7 +36,7 @@ class MockConfigService {
let component: RegisterTeacherFormComponent;
let configService: ConfigService;
let fixture: ComponentFixture<RegisterTeacherFormComponent>;
const PASSWORD: string = 'abcd1234!';
const PASSWORD: string = PasswordRequirementComponent.VALID_PASSWORD;
let teacherService: TeacherService;
let recaptchaV3Service: ReCaptchaV3Service;
let router: Router;
Expand Down

0 comments on commit 6cf5461

Please sign in to comment.