'How to test that component template contains mat-error
I want to create test for component that checks that mat-error is displayed.
I've created a test but it's failing because DOM does not have mat-error at all during testing. Although it works fine when project is served.
Template snippet
<mat-error>
Error message
</mat-error>
Test set up
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
BrowserAnimationsModule
],
declarations: [MyComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fit('should display error', () => {
const matErrorEl: HTMLElement =
fixture.debugElement.query(By.directive(MatError)).nativeElement;
expect(matErrorEl).toBeTruthy();
});
});
Solution 1:[1]
tldr; You have to touch the FormControl before any errors will show.
You have to first touch the component.
Using reactive forms (where nameFormControl is of type FormControl):
component.nameFormControl.markAsTouched();
Your mat-error element will now show in the view.
For a real scenario you will have an ngIf on the mat-error element and will need to set that error as well.
Example template:
<mat-error *ngIf="nameFormControl.hasError('required')">
Error message
</mat-error>
Adding error:
component.nameFormControl.setErrors({ required: true} as ValidationErrors);
Similar issue for this question:
How to catch the <mat-error> error message text content using Protractor
Official docs on Angular Form Validation:
https://angular.io/guide/form-validation#why-check-dirty-and-touched
Solution 2:[2]
I followed an other way, I utilized a library named spectator for help me with scenarios using angular-material spectator
file test
import { createComponentFactory, Spectator } from '@ngneat/spectator';
describe('InputEmailComponent', () => {
let spectator: Spectator<InputEmailComponent>;
const createComponent = createComponentFactory({
component: InputEmailComponent,
});
beforeEach(() => (spectator = createComponent()));
it(`should get the component mat-error with a email empty`, () => {
// Pass a value null for a email validator required
spectator.component.email.setValue(null);
expect(spectator.query('mat-error')).toHaveText('Insira um e-mail válido');
});
});
file component
import { Component, Input, OnInit } from "@angular/core";
import {
ControlContainer,
FormControl,
FormGroupDirective,
Validators,
} from "@angular/forms";
@Component({
selector: "input-email",
templateUrl: "./input-email.component.html",
viewProviders: [
{
provide: ControlContainer,
useExisting: FormGroupDirective,
},
],
})
export class InputEmailComponent implements OnInit {
@Input() style: string = "";
email: FormControl;
constructor() {
this.email = new FormControl("", [Validators.required, Validators.email]);
}
ngOnInit(): void {}
getErrorMessage(): string | null {
if (this.email.hasError("required") || this.email.hasError("email")) {
return "Insira um e-mail válido";
}
return null;
}
}
file template html
<mat-form-field [style]="style" appearance="fill">
<mat-label>e-mail</mat-label>
<input
type="email"
formControlName="email"
matInput
placeholder="digite seu e-mail"
/>
<mat-error *ngIf="!email.valid">{{ getErrorMessage() }}</mat-error>
</mat-form-field>
Solution 3:[3]
I was having the same issue the other day working with the angular testing-library and Jest. The answer of @RonanCodes actually brought me on the right track. I had to add the detectChanges() statement after the markAsTouched() as well, though.
Admittedly, my setup is a bit different, since the date picker is a separate shared component, where you can inject a map of errors to handle depending on the validator functions that you added to the form control.
Anyways, in any case that adding markAsTouched does not resolve your issue, try adding detectChanges as well.
it('should show error', async () => {
const reqMsg = 'This field is required';
const errors = new Map([['required', reqMsg]]);
const control = new FormControl(null, [Validators.required]);
const rc = await renderComponent('ariaLabel', 'label', control, errors);
control.setErrors({ required: true });
control.markAsTouched();
rc.detectChanges();
expect(screen.getByTestId('date-picker-error')).toHaveTextContent(reqMsg);
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | RonanCodes |
| Solution 2 | |
| Solution 3 | Springer21 |
