6

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();
  });
});
orlyohreally
  • 410
  • 6
  • 19

3 Answers3

7

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

RonanCodes
  • 1,016
  • 9
  • 14
0

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>
HeltonSS
  • 116
  • 4
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 06 '21 at 16:09
0

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);
  });
Springer21
  • 66
  • 2