Angular CDK Component Harnessを実装する
前回は Angular CDK Component Harness を試しに使ってみました。今回は自作のコンポーネントに対して Component Harness を実装してみます。
環境
Angular CLI: 9.0.1 Node: 12.14.1 OS: win32 x64 Angular: 9.0.0 ... animations, cdk, common, compiler, compiler-cli, core, forms ... language-service, material, platform-browser ... platform-browser-dynamic, router Ivy Workspace: Yes Package Version ----------------------------------------------------------- @angular-devkit/architect 0.900.1 @angular-devkit/build-angular 0.900.1 @angular-devkit/build-optimizer 0.900.1 @angular-devkit/build-webpack 0.900.1 @angular-devkit/core 9.0.1 @angular-devkit/schematics 9.0.1 @angular/cli 9.0.1 @ngtools/webpack 9.0.1 @schematics/angular 9.0.1 @schematics/update 0.900.1 rxjs 6.5.4 typescript 3.7.5 webpack 4.41.2
Component Harness を実装する
対象のコンポーネント作成
まずは実装対象のコンポーネントを作成します。 input とその値を削除するボタンがあるシンプルなコンポーネントです。
<!-- input.component.html --> <input #input type="text" [value]="value" (input)="onValueChange(input.value)" [disabled]="disabled"> <button (click)="onValueChange('')" [disabled]="!canClear">Clear</button>
// input.component.ts import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-input', templateUrl: './input.component.html', styleUrls: ['./input.component.scss'], }) export class InputComponent { @Output() valueChange = new EventEmitter<string>(); @Input() value: string = ''; @Input() disabled: boolean = false; get canClear(): boolean { return !this.disabled && !!this.value; } onValueChange(value: string) { if (this.value !== value) { this.value = value; this.valueChange.emit(value); } } }
Component Harness のベースを実装する
Component Harness を実装するにはまず ComponentHarness
を継承したクラスを作成します。そして静的プロパティ hostSelector
に Harness が対象とするコンポーネントのセレクターを記述します。
import { ComponentHarness } from '@angular/cdk/testing'; export class InputComponentHarness extends ComponentHarness { /** * Component Harnessが対象とする要素のセレクター */ static hostSelector = 'app-input'; }
次にこのコンポーネントが内包する要素へアクセスするためのクエリーを記述します。
private _input = this.locatorFor('input'); private _clearButton = this.locatorFor('button');
クエリーの引数は CSS セレクターや ComponentHarness のクラスが設定できます。 locatorFor
locatorForOptional
locatorForAll
があるので使い分けましょう。
Component Harness の操作を実装する
次に実際に要素を操作するメソッドを実装します。このコンポーネントに対してユーザーが実行する操作とテスト時に取得したいこのコンポーネントの DOM の値にアクセスするものを書いていきましょう。
クエリーから取得される TestElement
には clear
blur
focus
hover
といった状態を変更させるものや text
getAttribute
getProperty
getCssValue
hasClass
の状態を取得するものが生えています。まんま Selenium の Element ライクに操作ができるのでとても楽に実装できます。
まずは操作するメソッドを実装します。今回は文字を入力する、クリアボタンを押す、の2つです。
/** * 文字を入力します。 */ async input(...keys: Array<string | TestKey>): Promise<void> { const input = await this._input(); return input.sendKeys(...keys); } /** * クリアボタンを押します。 */ async clickClear(): Promise<void> { const button = await this._clearButton(); return button.click(); }
次に状態を取得するメソッドです。入力された文字と input と button の非アクティブ状態を取得します。
/** * inputに入力されている文字を取得します。 */ async getValue(): Promise<string> { const input = await this._input(); return input.getProperty('value'); } /** * inputの非アクティブ状態を取得します。 */ async isInputDisabled(): Promise<boolean> { const input = await this._input(); return input.getProperty('disabled'); } /** * クリアボタンの非アクティブ状態を取得します。 */ async isClearButtonDisabled(): Promise<boolean> { const button = await this._clearButton(); return button.getProperty('disabled'); }
終わりに
凄く簡単にですが Component Harness を実装してみました。操作感は Selenium に近く、先にも書いたように Selenium による操作に慣れている人はとても実装しやすいと思います。