Skip to content

fix(material-experimental/mdc-chips): align test harnesses with the non-MDC version #21051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/material-experimental/mdc-chips/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ng_test_library(
"//src/cdk/testing",
"//src/cdk/testing/testbed",
"//src/material-experimental/mdc-chips",
"@npm//@angular/forms",
],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {FormControl, ReactiveFormsModule, Validators} from '@angular/forms';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatChipsModule} from '../index';
import {MatChipGridHarness} from './chip-grid-harness';

let fixture: ComponentFixture<ChipGridHarnessTest>;
let loader: HarnessLoader;

describe('MatChipGridHarness', () => {
let fixture: ComponentFixture<ChipGridHarnessTest>;
let loader: HarnessLoader;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MatChipsModule],
imports: [MatChipsModule, ReactiveFormsModule],
declarations: [ChipGridHarnessTest],
}).compileComponents();

Expand All @@ -26,27 +28,59 @@ describe('MatChipGridHarness', () => {
});

it('should get correct number of rows', async () => {
const harnesses = await loader.getAllHarnesses(MatChipGridHarness);
const rows = await harnesses[0].getRows();
const harness = await loader.getHarness(MatChipGridHarness);
const rows = await harness.getRows();
expect(rows.length).toBe(3);
});

it('should get the chip input harness', async () => {
const harnesses = await loader.getAllHarnesses(MatChipGridHarness);
const input = await harnesses[0].getTextInput();
const harness = await loader.getHarness(MatChipGridHarness);
const input = await harness.getInput();
expect(input).not.toBe(null);
});

it('should get whether the grid is disabled', async () => {
const harness = await loader.getHarness(MatChipGridHarness);
expect(await harness.isDisabled()).toBe(false);

fixture.componentInstance.control.disable();
expect(await harness.isDisabled()).toBe(true);
});

it('should get whether the grid is required', async () => {
const harness = await loader.getHarness(MatChipGridHarness);
expect(await harness.isRequired()).toBe(false);

fixture.componentInstance.required = true;
expect(await harness.isRequired()).toBe(true);
});

it('should get whether the grid is invalid', async () => {
const harness = await loader.getHarness(MatChipGridHarness);
expect(await harness.isInvalid()).toBe(false);

// Mark the control as touched since the default error
// state matcher only activates after a control is touched.
fixture.componentInstance.control.markAsTouched();
fixture.componentInstance.control.setValue(undefined);

expect(await harness.isInvalid()).toBe(true);
});

});

@Component({
template: `
<mat-chip-grid #grid>
<mat-chip-row> Chip A </mat-chip-row>
<mat-chip-row> Chip B </mat-chip-row>
<mat-chip-row> Chip C </mat-chip-row>
<input [matChipInputFor]="grid" />
<mat-chip-grid [formControl]="control" [required]="required" #grid>
<mat-chip-row>Chip A</mat-chip-row>
<mat-chip-row>Chip B</mat-chip-row>
<mat-chip-row>Chip C</mat-chip-row>
<input [matChipInputFor]="grid"/>
</mat-chip-grid>
`
})
class ChipGridHarnessTest {}
class ChipGridHarnessTest {
control = new FormControl('value', [Validators.required]);
required = false;
}

30 changes: 23 additions & 7 deletions src/material-experimental/mdc-chips/testing/chip-grid-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
*/

import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
import {ChipGridHarnessFilters} from './chip-harness-filters';
import {
ChipGridHarnessFilters,
ChipInputHarnessFilters,
ChipRowHarnessFilters,
} from './chip-harness-filters';
import {MatChipInputHarness} from './chip-input-harness';
import {MatChipRowHarness} from './chip-row-harness';

Expand All @@ -22,16 +26,28 @@ export class MatChipGridHarness extends ComponentHarness {
return new HarnessPredicate(MatChipGridHarness, options);
}

private _rows = this.locatorForAll(MatChipRowHarness);
private _input = this.locatorFor(MatChipInputHarness);
/** Gets whether the chip grid is disabled. */
async isDisabled(): Promise<boolean> {
return await (await this.host()).getAttribute('aria-disabled') === 'true';
}

/** Gets whether the chip grid is required. */
async isRequired(): Promise<boolean> {
return await (await this.host()).hasClass('mat-mdc-chip-list-required');
}

/** Gets whether the chip grid is invalid. */
async isInvalid(): Promise<boolean> {
return await (await this.host()).getAttribute('aria-invalid') === 'true';
}

/** Gets promise of the harnesses for the chip rows. */
async getRows(): Promise<MatChipRowHarness[]> {
return await this._rows();
getRows(filter: ChipRowHarnessFilters = {}): Promise<MatChipRowHarness[]> {
return this.locatorForAll(MatChipRowHarness.with(filter))();
}

/** Gets promise of the chip text input harness. */
async getTextInput(): Promise<MatChipInputHarness|null> {
return await this._input();
getInput(filter: ChipInputHarnessFilters = {}): Promise<MatChipInputHarness|null> {
return this.locatorFor(MatChipInputHarness.with(filter))();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@

import {BaseHarnessFilters} from '@angular/cdk/testing';

// TODO(mmalerba): Add additional options that make sense for each harness type.

export interface ChipGridHarnessFilters extends BaseHarnessFilters {}

export interface ChipHarnessFilters extends BaseHarnessFilters {}

export interface ChipInputHarnessFilters extends BaseHarnessFilters {}
export interface ChipHarnessFilters extends BaseHarnessFilters {
/** Only find instances whose text matches the given value. */
text?: string | RegExp;
}

export interface ChipInputHarnessFilters extends BaseHarnessFilters {
/** Filters based on the value of the input. */
value?: string | RegExp;
/** Filters based on the placeholder text of the input. */
placeholder?: string | RegExp;
}

export interface ChipListboxHarnessFilters extends BaseHarnessFilters {}

export interface ChipOptionHarnessFilters extends ChipHarnessFilters {}
export interface ChipOptionHarnessFilters extends ChipHarnessFilters {
/** Only find chip instances whose selected state matches the given value. */
selected?: boolean;
}

export interface ChipGridHarnessFilters extends BaseHarnessFilters {}

export interface ChipRowHarnessFilters extends ChipHarnessFilters {}

Expand Down
26 changes: 21 additions & 5 deletions src/material-experimental/mdc-chips/testing/chip-harness.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {HarnessLoader} from '@angular/cdk/testing';
import {HarnessLoader, parallel} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatChipsModule} from '../index';
import {MatChipHarness} from './chip-harness';

let fixture: ComponentFixture<ChipHarnessTest>;
let loader: HarnessLoader;

describe('MatChipHarness', () => {
let fixture: ComponentFixture<ChipHarnessTest>;
let loader: HarnessLoader;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MatChipsModule],
Expand All @@ -34,22 +35,37 @@ describe('MatChipHarness', () => {
expect(await harnesses[4].getText()).toBe('Chip Row');
});

it('should be able to remove a mat-chip-row', async () => {
it('should be able to remove a chip', async () => {
const removeChipSpy = spyOn(fixture.componentInstance, 'removeChip');

const harnesses = await loader.getAllHarnesses(MatChipHarness);
await harnesses[4].remove();

expect(removeChipSpy).toHaveBeenCalledTimes(1);
});

it('should get the disabled state of a chip', async () => {
const harnesses = await loader.getAllHarnesses(MatChipHarness);
const disabledStates = await parallel(() => harnesses.map(harness => harness.isDisabled()));
expect(disabledStates).toEqual([false, false, false, true, false]);
});

it('should get the remove button of a chip', async () => {
const harness = await loader.getHarness(MatChipHarness.with({selector: '.has-remove-button'}));
expect(await harness.getRemoveButton()).toBeTruthy();
});

});

@Component({
template: `
<mat-basic-chip>Basic Chip</mat-basic-chip>
<mat-chip>Chip <span matChipTrailingIcon>trailing_icon</span></mat-chip>
<mat-chip><mat-chip-avatar>B</mat-chip-avatar>Chip with avatar</mat-chip>
<mat-chip disabled>Disabled Chip <span matChipRemove>remove_icon</span></mat-chip>
<mat-chip
class="has-remove-button"
disabled>Disabled Chip <span matChipRemove>remove_icon</span>
</mat-chip>
<mat-chip-row (removed)="removeChip()">Chip Row</mat-chip-row>
`
})
Expand Down
26 changes: 22 additions & 4 deletions src/material-experimental/mdc-chips/testing/chip-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/

import {ComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing';
import {ChipHarnessFilters} from './chip-harness-filters';
import {ChipHarnessFilters, ChipRemoveHarnessFilters} from './chip-harness-filters';
import {MatChipRemoveHarness} from './chip-remove-harness';

/** Harness for interacting with a mat-chip in tests. */
export class MatChipHarness extends ComponentHarness {
Expand All @@ -20,8 +21,10 @@ export class MatChipHarness extends ComponentHarness {
// methods. See https://github.com/microsoft/TypeScript/issues/5863
static with<T extends typeof MatChipHarness>(this: T, options: ChipHarnessFilters = {}):
HarnessPredicate<InstanceType<T>> {
return new HarnessPredicate(MatChipHarness, options) as
unknown as HarnessPredicate<InstanceType<T>>;
return new HarnessPredicate(MatChipHarness, options)
.addOption('text', options.text, (harness, label) => {
return HarnessPredicate.stringMatches(harness.getText(), label);
}) as unknown as HarnessPredicate<InstanceType<T>>;
}

/** Gets a promise for the text content the option. */
Expand All @@ -31,10 +34,25 @@ export class MatChipHarness extends ComponentHarness {
});
}

/** Whether the chip is disabled. */
async isDisabled(): Promise<boolean> {
return (await this.host()).hasClass('mat-mdc-chip-disabled');
}

/** Delete a chip from the set. */
async remove(): Promise<void> {
const hostEl = await this.host();
await hostEl.sendKeys!(TestKey.DELETE);
await hostEl.sendKeys(TestKey.DELETE);

// @breaking-change 12.0.0 Remove non-null assertion from `dispatchEvent`.
await hostEl.dispatchEvent!('transitionend', {propertyName: 'width'});
}

/**
* Gets the remove button inside of a chip.
* @param filter Optionally filters which chips are included.
*/
async getRemoveButton(filter: ChipRemoveHarnessFilters = {}): Promise<MatChipRemoveHarness> {
return this.locatorFor(MatChipRemoveHarness.with(filter))();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatChipsModule} from '../index';
import {MatChipInputHarness} from './chip-input-harness';

let fixture: ComponentFixture<ChipInputHarnessTest>;
let loader: HarnessLoader;

describe('MatChipInputHarness', () => {
let fixture: ComponentFixture<ChipInputHarnessTest>;
let loader: HarnessLoader;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MatChipsModule],
Expand All @@ -30,18 +31,53 @@ describe('MatChipInputHarness', () => {
expect(await harnesses[0].isDisabled()).toBe(false);
expect(await harnesses[1].isDisabled()).toBe(true);
});

it('should get whether the input is required', async () => {
const harness = await loader.getHarness(MatChipInputHarness);
expect(await harness.isRequired()).toBe(false);

fixture.componentInstance.required = true;
expect(await harness.isRequired()).toBe(true);
});

it('should get whether the input placeholder', async () => {
const harness = await loader.getHarness(MatChipInputHarness);
expect(await harness.getPlaceholder()).toBe('Placeholder');
});

it('should get and set the input value', async () => {
const harness = await loader.getHarness(MatChipInputHarness);
expect(await harness.getValue()).toBe('');

await harness.setValue('value');
expect(await harness.getValue()).toBe('value');
});

it('should control the input focus state', async () => {
const harness = await loader.getHarness(MatChipInputHarness);
expect(await harness.isFocused()).toBe(false);

await harness.focus();
expect(await harness.isFocused()).toBe(true);

await harness.blur();
expect(await harness.isFocused()).toBe(false);
});

});

@Component({
template: `
<mat-chip-grid #grid1>
<input [matChipInputFor]="grid1" />
<input [matChipInputFor]="grid1" [required]="required" placeholder="Placeholder" />
</mat-chip-grid>

<mat-chip-grid #grid2>
<input [matChipInputFor]="grid2" disabled />
</mat-chip-grid>
`
})
class ChipInputHarnessTest {}
class ChipInputHarnessTest {
required = false;
}

Loading