Skip to content

Commit

Permalink
fix(material/form-field): handle datepicker inputs in harness (#22403)
Browse files Browse the repository at this point in the history
Resolves a TODO about supporting datepicker inputs in the form field harness.

Also fixes an error that is thrown when the date range input is combined with an MDC-based form field.

(cherry picked from commit f9cc564)
  • Loading branch information
crisbeto authored and annieyw committed May 3, 2021
1 parent 62b2142 commit ec266be
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('FormFieldHarnessExample', () => {
expect(await formField.getTextHints()).toEqual(['Hint']);

fixture.componentInstance.requiredControl.setValue('');
await (await formField.getControl())?.blur();
await ((await formField.getControl() as MatInputHarness))?.blur();
expect(await formField.getTextErrors()).toEqual(['Error']);
expect(await formField.getTextHints()).toEqual([]);
});
Expand Down
4 changes: 4 additions & 0 deletions src/material-experimental/mdc-form-field/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ts_library(
"//src/cdk/testing",
"//src/material-experimental/mdc-input/testing",
"//src/material-experimental/mdc-select/testing",
"//src/material/datepicker/testing",
"//src/material/form-field/testing",
"//src/material/form-field/testing/control",
],
Expand All @@ -32,11 +33,14 @@ ng_test_library(
deps = [
":testing",
"//src/material-experimental/mdc-autocomplete",
"//src/material-experimental/mdc-core",
"//src/material-experimental/mdc-form-field",
"//src/material-experimental/mdc-input",
"//src/material-experimental/mdc-input/testing",
"//src/material-experimental/mdc-select",
"//src/material-experimental/mdc-select/testing",
"//src/material/datepicker",
"//src/material/datepicker/testing",
"//src/material/form-field/testing:harness_tests_lib",
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@ import {MatInputHarness} from '@angular/material-experimental/mdc-input/testing'
import {MatSelectModule} from '@angular/material-experimental/mdc-select';
import {MatSelectHarness} from '@angular/material-experimental/mdc-select/testing';
import {runHarnessTests} from '@angular/material/form-field/testing/shared.spec';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {MatNativeDateModule} from '@angular/material-experimental/mdc-core';
import {
MatDatepickerInputHarness,
MatDateRangeInputHarness,
} from '@angular/material/datepicker/testing';
import {MatFormFieldHarness} from './form-field-harness';

describe('MDC-based MatFormFieldHarness', () => {
runHarnessTests(
[MatFormFieldModule, MatAutocompleteModule, MatInputModule, MatSelectModule], {
[
MatFormFieldModule,
MatAutocompleteModule,
MatInputModule,
MatSelectModule,
MatNativeDateModule,
MatDatepickerModule
], {
formFieldHarness: MatFormFieldHarness as any,
inputHarness: MatInputHarness,
selectHarness: MatSelectHarness,
datepickerInputHarness: MatDatepickerInputHarness,
dateRangeInputHarness: MatDateRangeInputHarness,
isMdcImplementation: true,
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import {
} from '@angular/material/form-field/testing';
import {MatInputHarness} from '@angular/material-experimental/mdc-input/testing';
import {MatSelectHarness} from '@angular/material-experimental/mdc-select/testing';
import {
MatDatepickerInputHarness,
MatDateRangeInputHarness,
} from '@angular/material/datepicker/testing';

// TODO(devversion): support datepicker harness once developed (COMP-203).
// Also support chip list harness.
// TODO(devversion): support support chip list harness
/** Possible harnesses of controls which can be bound to a form-field. */
export type FormFieldControlHarness = MatInputHarness|MatSelectHarness;
export type FormFieldControlHarness =
MatInputHarness|MatSelectHarness|MatDatepickerInputHarness|MatDateRangeInputHarness;

/** Harness for interacting with a MDC-based form-field's in tests. */
export class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldControlHarness> {
Expand All @@ -44,6 +48,8 @@ export class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldContr
protected _hints = this.locatorForAll('.mat-mdc-form-field-hint');
protected _inputControl = this.locatorForOptional(MatInputHarness);
protected _selectControl = this.locatorForOptional(MatSelectHarness);
protected _datepickerInputControl = this.locatorForOptional(MatDatepickerInputHarness);
protected _dateRangeInputControl = this.locatorForOptional(MatDateRangeInputHarness);
private _mdcTextField = this.locatorFor('.mat-mdc-text-field-wrapper');

/** Gets the appearance of the form-field. */
Expand Down
1 change: 1 addition & 0 deletions src/material/datepicker/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ts_library(
deps = [
"//src/cdk/coercion",
"//src/cdk/testing",
"//src/material/form-field/testing/control",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import {
ComponentHarnessConstructor,
HarnessPredicate,
ComponentHarness,
} from '@angular/cdk/testing';
import {MatFormFieldControlHarness} from '@angular/material/form-field/testing/control';
import {DatepickerInputHarnessFilters} from './datepicker-harness-filters';

/** Sets up the filter predicates for a datepicker input harness. */
Expand All @@ -28,7 +28,7 @@ export function getInputPredicate<T extends MatDatepickerInputHarnessBase>(
}

/** Base class for datepicker input harnesses. */
export abstract class MatDatepickerInputHarnessBase extends ComponentHarness {
export abstract class MatDatepickerInputHarnessBase extends MatFormFieldControlHarness {
/** Whether the input is disabled. */
async isDisabled(): Promise<boolean> {
return (await this.host()).getProperty('disabled')!;
Expand Down
4 changes: 4 additions & 0 deletions src/material/form-field/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ts_library(
module_name = "@angular/material/form-field/testing",
deps = [
"//src/cdk/testing",
"//src/material/datepicker/testing",
"//src/material/form-field/testing/control",
"//src/material/input/testing",
"//src/material/select/testing",
Expand Down Expand Up @@ -45,6 +46,9 @@ ng_test_library(
":harness_tests_lib",
":testing",
"//src/material/autocomplete",
"//src/material/core",
"//src/material/datepicker",
"//src/material/datepicker/testing",
"//src/material/form-field",
"//src/material/input",
"//src/material/input/testing",
Expand Down
17 changes: 16 additions & 1 deletion src/material/form-field/testing/form-field-harness.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import {MatNativeDateModule} from '@angular/material/core';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {
MatDatepickerInputHarness,
MatDateRangeInputHarness,
} from '@angular/material/datepicker/testing';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatInputModule} from '@angular/material/input';
import {MatInputHarness} from '@angular/material/input/testing';
Expand All @@ -9,10 +15,19 @@ import {MatFormFieldHarness} from './form-field-harness';
import {runHarnessTests} from './shared.spec';

describe('Non-MDC-based MatFormFieldHarness', () => {
runHarnessTests([MatFormFieldModule, MatAutocompleteModule, MatInputModule, MatSelectModule], {
runHarnessTests([
MatFormFieldModule,
MatAutocompleteModule,
MatInputModule,
MatSelectModule,
MatNativeDateModule,
MatDatepickerModule,
], {
formFieldHarness: MatFormFieldHarness,
inputHarness: MatInputHarness,
selectHarness: MatSelectHarness,
datepickerInputHarness: MatDatepickerInputHarness,
dateRangeInputHarness: MatDateRangeInputHarness,
isMdcImplementation: false,
});
});
25 changes: 20 additions & 5 deletions src/material/form-field/testing/form-field-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ import {
parallel,
TestElement
} from '@angular/cdk/testing';
import {
MatDatepickerInputHarness,
MatDateRangeInputHarness,
} from '@angular/material/datepicker/testing';
import {MatFormFieldControlHarness} from '@angular/material/form-field/testing/control';
import {MatInputHarness} from '@angular/material/input/testing';
import {MatSelectHarness} from '@angular/material/select/testing';
import {FormFieldHarnessFilters} from './form-field-harness-filters';

// TODO(devversion): support datepicker harness once developed (COMP-203).
// Also support chip list harness.
// TODO(devversion): support support chip list harness
/** Possible harnesses of controls which can be bound to a form-field. */
export type FormFieldControlHarness = MatInputHarness|MatSelectHarness;
export type FormFieldControlHarness =
MatInputHarness|MatSelectHarness|MatDatepickerInputHarness|MatDateRangeInputHarness;

export abstract class _MatFormFieldHarnessBase<ControlHarness extends MatFormFieldControlHarness>
extends ComponentHarness {
Expand All @@ -34,6 +38,8 @@ export abstract class _MatFormFieldHarnessBase<ControlHarness extends MatFormFie
protected abstract _hints: AsyncFactoryFn<TestElement[]>;
protected abstract _inputControl: AsyncFactoryFn<ControlHarness|null>;
protected abstract _selectControl: AsyncFactoryFn<ControlHarness|null>;
protected abstract _datepickerInputControl: AsyncFactoryFn<ControlHarness|null>;
protected abstract _dateRangeInputControl: AsyncFactoryFn<ControlHarness|null>;

/** Gets the appearance of the form-field. */
abstract getAppearance(): Promise<string>;
Expand Down Expand Up @@ -91,8 +97,15 @@ export abstract class _MatFormFieldHarnessBase<ControlHarness extends MatFormFie
if (type) {
return this.locatorForOptional(type)();
}
const [select, input] = await parallel(() => [this._selectControl(), this._inputControl()]);
return select || input;
const [select, input, datepickerInput, dateRangeInput] = await parallel(() => [
this._selectControl(),
this._inputControl(),
this._datepickerInputControl(),
this._dateRangeInputControl()
]);

// Match the datepicker inputs first since they can also have a `MatInput`.
return datepickerInput || dateRangeInput || select || input;
}

/** Gets the theme color of the form-field. */
Expand Down Expand Up @@ -234,6 +247,8 @@ export class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldContr
protected _hints = this.locatorForAll('mat-hint, .mat-hint');
protected _inputControl = this.locatorForOptional(MatInputHarness);
protected _selectControl = this.locatorForOptional(MatSelectHarness);
protected _datepickerInputControl = this.locatorForOptional(MatDatepickerInputHarness);
protected _dateRangeInputControl = this.locatorForOptional(MatDateRangeInputHarness);

/** Gets the appearance of the form-field. */
async getAppearance(): Promise<'legacy'|'standard'|'fill'|'outline'> {
Expand Down
34 changes: 30 additions & 4 deletions src/material/form-field/testing/shared.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ import {MatFormFieldHarness} from './form-field-harness';

/** Shared tests to run on both the original and MDC-based form-field's. */
export function runHarnessTests(
modules: Type<any>[], {formFieldHarness, inputHarness, selectHarness, isMdcImplementation}: {
modules: Type<any>[], {
formFieldHarness,
inputHarness,
selectHarness,
datepickerInputHarness,
dateRangeInputHarness,
isMdcImplementation,
}: {
formFieldHarness: typeof MatFormFieldHarness,
inputHarness: Type<any>,
selectHarness: Type<any>,
datepickerInputHarness: Type<any>,
dateRangeInputHarness: Type<any>
isMdcImplementation: boolean
}) {
let fixture: ComponentFixture<FormFieldHarnessTest>;
Expand All @@ -34,7 +43,7 @@ export function runHarnessTests(

it('should be able to load harnesses', async () => {
const formFields = await loader.getAllHarnesses(formFieldHarness);
expect(formFields.length).toBe(5);
expect(formFields.length).toBe(7);
});

it('should be able to load form-field that matches specific selector', async () => {
Expand All @@ -60,6 +69,8 @@ export function runHarnessTests(
expect(await formFields[2].getControl() instanceof selectHarness).toBe(true);
expect(await formFields[3].getControl() instanceof inputHarness).toBe(true);
expect(await formFields[4].getControl() instanceof inputHarness).toBe(true);
expect(await formFields[5].getControl() instanceof datepickerInputHarness).toBe(true);
expect(await formFields[6].getControl() instanceof dateRangeInputHarness).toBe(true);
});

it('should be able to get custom control of form-field', async () => {
Expand Down Expand Up @@ -189,13 +200,13 @@ export function runHarnessTests(
it('should be able to get the prefix text of a form-field', async () => {
const formFields = await loader.getAllHarnesses(formFieldHarness);
const prefixTexts = await parallel(() => formFields.map(f => f.getPrefixText()));
expect(prefixTexts).toEqual(['prefix_textprefix_text_2', '', '', '', '']);
expect(prefixTexts).toEqual(['prefix_textprefix_text_2', '', '', '', '', '', '']);
});

it('should be able to get the suffix text of a form-field', async () => {
const formFields = await loader.getAllHarnesses(formFieldHarness);
const suffixTexts = await parallel(() => formFields.map(f => f.getSuffixText()));
expect(suffixTexts).toEqual(['suffix_text', '', '', '', '']);
expect(suffixTexts).toEqual(['suffix_text', '', '', '', '', '', '']);
});

it('should be able to check if form field has been touched', async () => {
Expand Down Expand Up @@ -281,6 +292,21 @@ export function runHarnessTests(
<mat-label>Label</mat-label>
<input matInput>
</mat-form-field>
<mat-form-field>
<mat-label>Date</mat-label>
<input matInput [matDatepicker]="datepicker">
<mat-datepicker #datepicker></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Date range</mat-label>
<mat-date-range-input [rangePicker]="rangePicker">
<input matStartDate placeholder="Start date"/>
<input matEndDate placeholder="End date"/>
</mat-date-range-input>
<mat-date-range-picker #rangePicker></mat-date-range-picker>
</mat-form-field>
`
})
class FormFieldHarnessTest {
Expand Down
6 changes: 5 additions & 1 deletion tools/public_api_guard/material/form-field/testing.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export declare abstract class _MatFormFieldHarnessBase<ControlHarness extends MatFormFieldControlHarness> extends ComponentHarness {
protected abstract _dateRangeInputControl: AsyncFactoryFn<ControlHarness | null>;
protected abstract _datepickerInputControl: AsyncFactoryFn<ControlHarness | null>;
protected abstract _errors: AsyncFactoryFn<TestElement[]>;
protected abstract _hints: AsyncFactoryFn<TestElement[]>;
protected abstract _inputControl: AsyncFactoryFn<ControlHarness | null>;
Expand Down Expand Up @@ -29,14 +31,16 @@ export declare abstract class _MatFormFieldHarnessBase<ControlHarness extends Ma
abstract isLabelFloating(): Promise<boolean>;
}

export declare type FormFieldControlHarness = MatInputHarness | MatSelectHarness;
export declare type FormFieldControlHarness = MatInputHarness | MatSelectHarness | MatDatepickerInputHarness | MatDateRangeInputHarness;

export interface FormFieldHarnessFilters extends BaseHarnessFilters {
floatingLabelText?: string | RegExp;
hasErrors?: boolean;
}

export declare class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldControlHarness> {
protected _dateRangeInputControl: AsyncFactoryFn<MatDateRangeInputHarness | null>;
protected _datepickerInputControl: AsyncFactoryFn<MatDatepickerInputHarness | null>;
protected _errors: AsyncFactoryFn<TestElement[]>;
protected _hints: AsyncFactoryFn<TestElement[]>;
protected _inputControl: AsyncFactoryFn<MatInputHarness | null>;
Expand Down

0 comments on commit ec266be

Please sign in to comment.