diff --git a/README.md b/README.md index 494cb91..57d251c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Projeto criado na Mentoria Angular Pro -![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ortegavan/ecommerce/ci.yml) ![GitHub last commit](https://img.shields.io/github/last-commit/ortegavan/ecommerce) ![GitHub Tag](https://img.shields.io/github/v/tag/ortegavan/ecommerce) ![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/ortegavan_ecommerce?server=https%3A%2F%2Fsonarcloud.io) ![Static Badge](https://img.shields.io/badge/code_style-prettier-ff69b4) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ortegavan/ecommerce/ci.yml) ![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/ortegavan_ecommerce?server=https%3A%2F%2Fsonarcloud.io) ![GitHub last commit](https://img.shields.io/github/last-commit/ortegavan/ecommerce) ![GitHub Tag](https://img.shields.io/github/v/tag/ortegavan/ecommerce) ![Static Badge](https://img.shields.io/badge/code_style-prettier-ff69b4) Este documento contém os exercícios feitos em aula + minhas notas pessoais sobre a Mentoria Angular Pro de Paolo Almeida e Andrew Rosário. @@ -473,3 +473,27 @@ Os componentes filhos conseguem acessar o componente pai via injeção de depend Para cenários mais genéricos, é possível criar uma classe abstrata que os orquestradores implementam para serem injetadas nos componentes filhos. Para saber mais, acesse [este link](https://netbasal.com/create-a-multi-step-form-in-angular-44cdc5b75cdc). Formulários complexos também podem ser divididos com [ControlContainer](https://andrewrosario.medium.com/dividindo-formulários-complexos-no-angular-com-controlcontainer-1b107d59c8be) e com [ControlValueAccessor](https://andrewrosario.medium.com/form-controls-customizados-no-angular-com-controlvalueaccessor-367e773e3fec). + +## ✨ Aula 22 + +Finalizamos o formulário iniciado na aula anterior e implementamos os testes. Foi necessário fornecer rotas com `provideRouter` e importar o `NoopAnimationsModule` para os testes passarem. Para testarmos a interação com o HTML do form, substituímos a manipulação do `FormControl` como mostrado a seguir: + +```typescript +it('should display email error message', () => { + // component.control.setValue('teste'); + const input: HTMLInputElement = + fixture.nativeElement.querySelector('input'); + input.value = 'teste'; + input.dispatchEvent(new Event('input')); + + component.control.markAllAsTouched(); + fixture.detectChanges(); + const error = fixture.nativeElement.querySelector( + '[data-testid="error-email"]' + ); + + expect(error).toBeTruthy(); +}); +``` + +Onde `data-testid` é um atributo customizado que usamos para identificar elementos no HTML. diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.css b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.css index e69de29..ef6fadf 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.css +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.css @@ -0,0 +1,8 @@ +.form-field { + width: 100%; +} + +.container { + display: flex; + justify-content: end; +} diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.html b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.html index 1d6eb33..80a558d 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.html +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.html @@ -1 +1,30 @@ -

auth-form-email works!

+ + Informe seu email + + @if (control.hasError('email') && !control.hasError('required')) { + Por favor, informe um e-mail válido + } @if (control.hasError('required')) { + O e-mail é obrigatório + } + + +
+ +
diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.spec.ts b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.spec.ts index ff50253..25cfbb9 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.spec.ts +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.spec.ts @@ -1,6 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AuthFormEmailComponent } from './auth-form-email.component'; import { AuthFormComponent } from '../auth-form.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { provideRouter } from '@angular/router'; describe('AuthFormEmailComponent', () => { let component: AuthFormEmailComponent; @@ -8,8 +10,8 @@ describe('AuthFormEmailComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [AuthFormEmailComponent], - providers: [AuthFormComponent], + imports: [AuthFormEmailComponent, NoopAnimationsModule], + providers: [AuthFormComponent, provideRouter([])], }).compileComponents(); fixture = TestBed.createComponent(AuthFormEmailComponent); @@ -20,4 +22,40 @@ describe('AuthFormEmailComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should enable button when control is valid', () => { + const button = fixture.nativeElement.querySelector('button'); + expect(button.disabled).toBe(true); + + component.control.setValue('teste@teste.com'); + fixture.detectChanges(); + + expect(button.disabled).toBe(false); + }); + + it('should display required error message', () => { + component.control.markAllAsTouched(); + fixture.detectChanges(); + const error = fixture.nativeElement.querySelector( + '[data-testid="error-required"]' + ); + + expect(error).toBeTruthy(); + }); + + it('should display email error message', () => { + // component.control.setValue('teste'); + const input: HTMLInputElement = + fixture.nativeElement.querySelector('input'); + input.value = 'teste'; + input.dispatchEvent(new Event('input')); + + component.control.markAllAsTouched(); + fixture.detectChanges(); + const error = fixture.nativeElement.querySelector( + '[data-testid="error-email"]' + ); + + expect(error).toBeTruthy(); + }); }); diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.ts b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.ts index 811139c..5aab826 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.ts +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-email/auth-form-email.component.ts @@ -1,11 +1,21 @@ import { Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AuthFormComponent } from '../auth-form.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { RouterModule } from '@angular/router'; @Component({ selector: 'ecommerce-auth-form-email', standalone: true, - imports: [CommonModule], + imports: [ + CommonModule, + MatButtonModule, + MatInputModule, + ReactiveFormsModule, + RouterModule, + ], templateUrl: './auth-form-email.component.html', styleUrl: './auth-form-email.component.css', }) diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.css b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.css index e69de29..ef6fadf 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.css +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.css @@ -0,0 +1,8 @@ +.form-field { + width: 100%; +} + +.container { + display: flex; + justify-content: end; +} diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.html b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.html index 602e83a..21e46d9 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.html +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.html @@ -1 +1,14 @@ -

auth-form-password works!

+ + Informe sua senha + + + +
+ +
diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.spec.ts b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.spec.ts index b1dde5b..b61b96a 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.spec.ts +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.spec.ts @@ -1,6 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AuthFormPasswordComponent } from './auth-form-password.component'; import { AuthFormComponent } from '../auth-form.component'; +import { provideRouter } from '@angular/router'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('AuthFormPasswordComponent', () => { let component: AuthFormPasswordComponent; @@ -8,8 +10,8 @@ describe('AuthFormPasswordComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [AuthFormPasswordComponent], - providers: [AuthFormComponent], + imports: [AuthFormPasswordComponent, NoopAnimationsModule], + providers: [AuthFormComponent, provideRouter([])], }).compileComponents(); fixture = TestBed.createComponent(AuthFormPasswordComponent); diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.ts b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.ts index 92f3b17..5936ae0 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.ts +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form-password/auth-form-password.component.ts @@ -1,11 +1,19 @@ import { Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AuthFormComponent } from '../auth-form.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; @Component({ selector: 'ecommerce-auth-form-password', standalone: true, - imports: [CommonModule], + imports: [ + CommonModule, + MatButtonModule, + MatInputModule, + ReactiveFormsModule, + ], templateUrl: './auth-form-password.component.html', styleUrl: './auth-form-password.component.css', }) diff --git a/modules/feature/auth/form/src/lib/auth-form/auth-form.component.html b/modules/feature/auth/form/src/lib/auth-form/auth-form.component.html index 4f4a90b..896ceda 100644 --- a/modules/feature/auth/form/src/lib/auth-form/auth-form.component.html +++ b/modules/feature/auth/form/src/lib/auth-form/auth-form.component.html @@ -1,5 +1,6 @@
+

Entrar