Skip to content

Commit

Permalink
Merge pull request #56 from Kleostro/feat/tu-01-03/error-handling
Browse files Browse the repository at this point in the history
feat(tu-01-03): error handling
  • Loading branch information
katyastan authored Aug 17, 2024
2 parents 5271d62 + 53fc1db commit 399c295
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 13 deletions.
26 changes: 24 additions & 2 deletions src/app/auth/pages/register/register.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@ <h2 class="register-title">Sign Up</h2>
class="register-input p-inputtext"
/>
@if (registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched) {
<small class="p-error">Please input a valid email address!</small>
@if (registrationForm.get('email')?.errors?.['required']) {
<small class="p-error">Please input your email!</small>
}
@if (registrationForm.get('email')?.errors?.['email']) {
<small class="p-error">Please input a valid email address!</small>
}
}
@if (registrationForm.hasError('invalidUniqueKey')) {
<small class="p-error">Account with this email already exists</small>
}
@if (registrationForm.hasError('invalidEmail')) {
<small class="p-error">Email is wrong. Please input a valid email address!</small>
}
</div>
</div>
Expand All @@ -31,7 +42,15 @@ <h2 class="register-title">Sign Up</h2>
class="register-input p-inputtext"
/>
@if (registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched) {
<small class="p-error">Password must be at least 8 characters long</small>
@if (registrationForm.get('password')?.errors?.['required']) {
<small class="p-error">Please input your password!</small>
}
@if (registrationForm.get('password')?.errors?.['minlength']) {
<small class="p-error">Password must be at least 8 characters long</small>
}
}
@if (registrationForm.hasError('invalidPassword')) {
<small class="p-error">Password is wrong. Password must be at least 8 characters long</small>
}
</div>
</div>
Expand All @@ -56,6 +75,9 @@ <h2 class="register-title">Sign Up</h2>
@if (registrationForm.hasError('passwordMismatch') && registrationForm.get('confirm')?.touched) {
<small class="p-error">Passwords do not match</small>
}
@if (registrationForm.hasError('invalidFields')) {
<small class="p-error">Fields are empty. Please input email and password.</small>
}
<div class="register-button-container">
<p-button type="submit" label="Register" [disabled]="!registrationForm.valid" class="p-mt-2"></p-button>
<p-link pButton routerLink="/sign-in" label="Sign In" class="p-button-text p-mt-2"></p-link>
Expand Down
65 changes: 64 additions & 1 deletion src/app/auth/pages/register/register.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,85 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

import { MessageService } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { MessageModule } from 'primeng/message';
import { MessagesModule } from 'primeng/messages';
import { PasswordModule } from 'primeng/password';
import { of } from 'rxjs';

import { SignUpService } from '../../../api/signUpService/sign-up.service';
import { RegisterComponent } from './register.component';

describe('RegisterComponent', () => {
let component: RegisterComponent;
let fixture: ComponentFixture<RegisterComponent>;
let signUpService: SignUpService;

beforeEach(async () => {
const signUpServiceMock = {
signUp: jest.fn(),
};
const messageServiceMock = {
add: jest.fn(),
};

await TestBed.configureTestingModule({
imports: [RegisterComponent],
imports: [
RegisterComponent,
ReactiveFormsModule,
InputTextModule,
ButtonModule,
MessagesModule,
MessageModule,
PasswordModule,
],
providers: [
{ provide: SignUpService, useValue: signUpServiceMock },
{ provide: MessageService, useValue: messageServiceMock },
{ provide: FormBuilder, useValue: new FormBuilder() },
{ provide: ActivatedRoute, useValue: { snapshot: { queryParams: {} } } },
],
}).compileComponents();

fixture = TestBed.createComponent(RegisterComponent);
component = fixture.componentInstance;
signUpService = TestBed.inject(SignUpService);
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should initialize the registration form', () => {
expect(component.registrationForm).toBeDefined();
expect(component.registrationForm.controls.email).toBeDefined();
expect(component.registrationForm.controls.password).toBeDefined();
expect(component.registrationForm.controls.confirm).toBeDefined();
});

it('should validate password match', () => {
const passwordControl = component.registrationForm.controls.password;
const confirmControl = component.registrationForm.controls.confirm;
passwordControl.setValue('password123');
confirmControl.setValue('password1234');
fixture.detectChanges();
// eslint-disable-next-line dot-notation
expect(component.registrationForm.errors?.['passwordMismatch']).toBeTruthy();
confirmControl.setValue('password123');
fixture.detectChanges();
// eslint-disable-next-line dot-notation
expect(component.registrationForm.errors?.['passwordMismatch']).toBeUndefined();
});

it('should reset the form on successful registration', () => {
jest.spyOn(signUpService, 'signUp').mockReturnValue(of({}));
component.submitForm();
fixture.detectChanges();
expect(component.registrationForm.pristine).toBeTruthy();
expect(component.registrationForm.untouched).toBeFalsy();
});
});
48 changes: 38 additions & 10 deletions src/app/auth/pages/register/register.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';

import { MessageService } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { MessageModule } from 'primeng/message';
import { MessagesModule } from 'primeng/messages';
import { PasswordModule } from 'primeng/password';
import { firstValueFrom } from 'rxjs';

import { OverriddenHttpErrorResponse } from '@/app/api/models/errorResponse';
import { User } from '@/app/api/models/user';

import { SignUpService } from '../../../api/signUpService/sign-up.service';
import { passwordMatchValidator } from '../../../shared/validators/validators';

export interface RegisterForm {
email: FormControl<string>;
password: FormControl<string>;
confirm: FormControl<string>;
}

@Component({
selector: 'app-register',
standalone: true,
Expand All @@ -22,6 +34,7 @@ import { passwordMatchValidator } from '../../../shared/validators/validators';
MessagesModule,
MessageModule,
PasswordModule,
RouterLink,
],
templateUrl: './register.component.html',
styleUrl: './register.component.scss',
Expand All @@ -30,33 +43,48 @@ import { passwordMatchValidator } from '../../../shared/validators/validators';
})
export class RegisterComponent {
private messageService = inject(MessageService);
private signUpService = inject(SignUpService);
private router = inject(Router);
private fb = inject(FormBuilder);

public registrationForm = this.fb.group(
{
email: ['', [Validators.email.bind(this), Validators.required.bind(this)]],
password: ['', [Validators.required.bind(this), Validators.minLength(8).bind(this)]],
confirm: ['', [Validators.required.bind(this)]],
email: this.fb.control<string>('', [Validators.required.bind(this), Validators.email.bind(this)]),
password: this.fb.control<string>('', [Validators.required.bind(this), Validators.minLength(8).bind(this)]),
confirm: this.fb.control<string>('', [Validators.required.bind(this)]),
},
{
validators: passwordMatchValidator.bind(this),
validators: passwordMatchValidator,
},
);

public submitForm(): void {
if (this.registrationForm.valid) {
// Perform registration logic here (e.g., send data to backend)
// console.log('submit', this.registrationForm.value);
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Registration successful!' });
this.registrationForm.reset();
firstValueFrom(this.signUpService.signUp(this.userData))
.then(() => {
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Registration successful!' });
this.router.navigate(['/sign-in']);
this.registrationForm.reset();
})
.catch((err: OverriddenHttpErrorResponse) => {
this.registrationForm.setErrors({ [err.error.reason]: true });
});
} else {
Object.values(this.registrationForm.controls).forEach((control) => {
if (control.invalid) {
control.markAsDirty();
control.markAsTouched();
control.updateValueAndValidity({
onlySelf: true,
});
}
});
}
}

private get userData(): User {
return {
email: this.registrationForm.controls.email.value || '',
password: this.registrationForm.controls.password.value || '',
};
}
}

0 comments on commit 399c295

Please sign in to comment.