Unable to test Angular Material Select Component using CDK Test Harness
Asked Answered
M

2

7

I am trying to write some tests for a component that uses Angular Material Components. I read about the CDK Test Harness https://material.angular.io/guide/using-component-harnesses and I want to fetch the count of options in the Mat Select component based on that.

<mat-card>
  <mat-card-content>
    <form [formGroup]="filterLikelihoodForm" (ngSubmit)="onSearchClick()">
      <div class="container">
        <div class="row align-items-center">
          <div class="col-sm">
            <mat-form-field class="full-width" id="periodField">
              <mat-label>Select Period</mat-label>
              <mat-select formControlName="period" id="period">
                <mat-option *ngFor="let period of periodList" [value]="period.id">
                  {{ period.monthName }}
                </mat-option>
              </mat-select>
              <mat-error *ngIf="f.period.errors">
                Please select a valid period
              </mat-error>
            </mat-form-field>
          </div>
          <div class="col-sm">
            <mat-form-field class="full-width">
              <input
                matInput
                type="number"
                formControlName="finishedDeals"
                placeholder="Enter no of finished deals"
              />
              <mat-error *ngIf="f.finishedDeals.errors">
                Please enter a valid number
              </mat-error>
            </mat-form-field>
          </div>
          <div class="col-sm">
            <button
              mat-flat-button
              color="primary"
              [disabled]="filterLikelihoodForm.invalid"
              class="full-width"
            >
              Search
            </button>
          </div>
        </div>
      </div>
    </form>
  </mat-card-content>
</mat-card>

Here is my spec file code:

import { HarnessLoader } from "@angular/cdk/testing";
import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
import { TestBed, async, ComponentFixture } from "@angular/core/testing";
import { FilterLikelihoodComponent } from "./filter-likelihood.component";
import { MatFormFieldHarness } from "@angular/material/form-field/testing";
import { MatFormFieldModule } from "@angular/material/form-field";
import { ReactiveFormsModule } from "@angular/forms";
import { MatSelectModule } from "@angular/material/select";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";
import { MatInputModule } from "@angular/material/input";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { periodListMock } from "./mock/filter-likelihood.mock";
import { MatSelectHarness } from "@angular/material/select/testing";
import { MatButtonHarness } from "@angular/material/button/testing";
import { Component } from "@angular/core";
import { MatInputHarness } from "@angular/material/input/testing";

describe("Filter Likelihood Component", () => {
  let testHostComponent: TestHostComponent;
  let testHostFixture: ComponentFixture<TestHostComponent>;

  let component: FilterLikelihoodComponent;
  let fixture: ComponentFixture<FilterLikelihoodComponent>;
  let loader: HarnessLoader;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [FilterLikelihoodComponent, TestHostComponent],
      imports: [
        ReactiveFormsModule,
        MatFormFieldModule,
        MatSelectModule,
        MatCardModule,
        MatButtonModule,
        MatInputModule,
        NoopAnimationsModule,
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    testHostFixture = TestBed.createComponent(TestHostComponent);
    testHostComponent = testHostFixture.componentInstance;
    testHostFixture.detectChanges();

    loader = TestbedHarnessEnvironment.loader(testHostFixture);
  });

  it("should have correct period label", async () => {
    const expectedLabel = "Select Period";
    const periodFieldHarness = await loader.getHarness<MatFormFieldHarness>(
      MatFormFieldHarness.with({ selector: "#periodField" })
    );
    const actual = await periodFieldHarness.getLabel();
    expect(actual).toBe(expectedLabel);
  });

  it("should have 3 recent periods in the dropdown", async () => {
    const expectedCount = 3;
    const selectHarness = await loader.getHarness<MatSelectHarness>(
      MatSelectHarness
    );
    const actual = ((await selectHarness.getOptions()).length);
    expect(actual).toBe(expectedCount);
  });

  it("should have a search button", async () => {
    const expectedLabel = "Search";
    const buttonHarness = await loader.getHarness<MatButtonHarness>(
      MatButtonHarness
    );
    const actual = await buttonHarness.getText();
    expect(actual).toBe(expectedLabel);
  });

  it("should have 10 finished deals by default", async () => {
    const expectedFinishedDeals = 10;
    const inputHarness = await loader.getHarness<MatInputHarness>(
      MatInputHarness
    );
    const actual = await inputHarness.getValue();
    expect(parseInt(actual)).toBe(expectedFinishedDeals);
  });

  it("should have a enabled button by default", async () => {
    const expectedDisabledState = false;
    const buttonHarness = await loader.getHarness<MatButtonHarness>(
      MatButtonHarness
    );
    const actual = await buttonHarness.isDisabled();
    expect(actual).toBe(expectedDisabledState);
  });

  @Component({
    selector: `host-component`,
    template: `<app-filter-likelihood
      [periodList]="periodList"
      [initialFinishedDeals]="defaultFinishedDeals"
    ></app-filter-likelihood>`,
  })
  class TestHostComponent {
    periodList = new Helper().mockPeriodList;
    defaultFinishedDeals = 10;
  }
});

class Helper {
  mockPeriodList = periodListMock;
}

The second spec with title "should have 3 recent periods in the dropdown" is failing as I am getting the options count as 0 always.

Can someone help me figure out my bug and let me know the correct way to write this spec using Mat test harness?

Thanks in advance!

Myalgia answered 10/4, 2020 at 15:12 Comment(0)
M
14

After some tinkering with the Select Harness API, I was able to solve this. I have updated the spec to get the host element and then triggered a click event.

  it("should have 3 recent periods in the dropdown", async () => {
    const expectedCount = 3;
    const selectHarness = await loader.getHarness<MatSelectHarness>(
      MatSelectHarness
    );

    //Click the select element host
    await (await selectHarness.host()).click();
    const actual = (await selectHarness.getOptions()).length;
    expect(actual).toBe(expectedCount);
  });
Myalgia answered 10/4, 2020 at 16:19 Comment(1)
Instead of await (await selectHarness.host()).click(); I did await selectHarness.open() and got the same (working) results. Seems like it has trouble loading the MatOptionHarness if the select isn't "opened"?Inscription
U
7

You are trying to get number of options before you open select element.

const selectHarness = await loader.getHarness<MatSelectHarness>(MatSelectHarness);
const count = (await selectHarness.getOptions()).length;  // count will be 0
await selectHarness.open();  // you can open matselect element via open method
const count = (await selectHarness.getOptions()).length;  // count will be as expected
Udall answered 5/5, 2020 at 19:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.