Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to test Angular Material Select Component using CDK Test Harness

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!

like image 823
Arindam Dawn Avatar asked Apr 10 '20 15:04

Arindam Dawn


People also ask

What is a component harness in angular?

A component harness is a testing API around an Angular directive or component. Component harnesses can be shared between unit tests, integration tests, and end-to-end tests. They result in less brittle tests as implementation details are hidden from test suites.

What is the CDK in angular?

It represents an abstraction of the core functionalities found in the Angular Material library, without any styling specific to Material Design. Think of the CDK as a blank state of well-tested functionality upon which you can develop your own bespoke components. Common Behaviors Tools for implementing common application features.

How to run test cases in angular CLI?

Complete Example 7. Run Test Cases To run the test cases, find the steps. 1. Install Angular CLI using link . 2. Download source code using download link given below on this page. 3. Use downloaded src in your Angular CLI application. 4. Run ng test using command prompt. Test result can be seen on command prompt as well as on browser.

How to test property binding between two select elements in component?

In our component we have two select elements and their property binding are performed by FormControl. Our test cases will be as following. 1. When we change the value of component property, the selected value should be changed of the associated select element. 2. We will test the changing of selected value from dropdown. 3.


2 Answers

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);
  });
like image 134
Arindam Dawn Avatar answered Oct 10 '22 19:10

Arindam Dawn


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
like image 34
quardem Avatar answered Oct 10 '22 18:10

quardem