diff --git a/src/cdk/testing/protractor/protractor-element.ts b/src/cdk/testing/protractor/protractor-element.ts index 75bea77b5044..145bb00077a9 100644 --- a/src/cdk/testing/protractor/protractor-element.ts +++ b/src/cdk/testing/protractor/protractor-element.ts @@ -147,6 +147,10 @@ export class ProtractorElement implements TestElement { return browser.executeScript(`return arguments[0][arguments[1]]`, this.element, name); } + async setInputValue(value: string): Promise { + return browser.executeScript(`arguments[0].value = arguments[1]`, this.element, value); + } + async matchesSelector(selector: string): Promise { return browser.executeScript(` return (Element.prototype.matches || diff --git a/src/cdk/testing/test-element.ts b/src/cdk/testing/test-element.ts index eef0b3ca34e9..3c02cb6b9e77 100644 --- a/src/cdk/testing/test-element.ts +++ b/src/cdk/testing/test-element.ts @@ -113,6 +113,12 @@ export interface TestElement { /** Gets the value of a property of an element. */ getProperty(name: string): Promise; + /** + * Sets the value of a property of an input. + * @breaking-change 11.0.0 To become a required method. + */ + setInputValue?(value: string): Promise; + /** Checks whether this element matches the given selector. */ matchesSelector(selector: string): Promise; diff --git a/src/cdk/testing/testbed/unit-test-element.ts b/src/cdk/testing/testbed/unit-test-element.ts index eea12805b349..75f6c5c121f3 100644 --- a/src/cdk/testing/testbed/unit-test-element.ts +++ b/src/cdk/testing/testbed/unit-test-element.ts @@ -153,6 +153,11 @@ export class UnitTestElement implements TestElement { return (this.element as any)[name]; } + async setInputValue(value: string): Promise { + await this._stabilize(); + (this.element as any).value = value; + } + async matchesSelector(selector: string): Promise { await this._stabilize(); const elementPrototype = Element.prototype as any; diff --git a/src/cdk/testing/tests/protractor.e2e.spec.ts b/src/cdk/testing/tests/protractor.e2e.spec.ts index bb8da1d692cd..db9f90628dd9 100644 --- a/src/cdk/testing/tests/protractor.e2e.spec.ts +++ b/src/cdk/testing/tests/protractor.e2e.spec.ts @@ -283,6 +283,14 @@ describe('ProtractorHarnessEnvironment', () => { expect(await input.getProperty('value')).toBe('Hello'); }); + it('should be able to set the value of an input', async () => { + const input = await harness.input(); + + // @breaking-change 11.0.0 Remove non-null assertion once `setInputValue` is required. + await input.setInputValue!('hello'); + expect(await input.getProperty('value')).toBe('hello'); + }); + it('should check if selector matches', async () => { const button = await harness.button(); expect(await button.matchesSelector('button:not(.fake-class)')).toBe(true); diff --git a/src/cdk/testing/tests/testbed.spec.ts b/src/cdk/testing/tests/testbed.spec.ts index 59854ff67cbe..17d15578d73e 100644 --- a/src/cdk/testing/tests/testbed.spec.ts +++ b/src/cdk/testing/tests/testbed.spec.ts @@ -368,6 +368,14 @@ describe('TestbedHarnessEnvironment', () => { expect(await input.getProperty('value')).toBe('Hello'); }); + it('should be able to set the value of an input', async () => { + const input = await harness.input(); + + // @breaking-change 11.0.0 Remove non-null assertion once `setInputValue` is required. + await input.setInputValue!('hello'); + expect(await input.getProperty('value')).toBe('hello'); + }); + it('should check if selector matches', async () => { const button = await harness.button(); expect(await button.matchesSelector('button:not(.fake-class)')).toBe(true); diff --git a/src/material/input/testing/BUILD.bazel b/src/material/input/testing/BUILD.bazel index fb1f65e08d05..47dc421d1eba 100644 --- a/src/material/input/testing/BUILD.bazel +++ b/src/material/input/testing/BUILD.bazel @@ -26,6 +26,7 @@ ng_test_library( srcs = ["shared.spec.ts"], deps = [ ":testing", + "//src/cdk/platform", "//src/cdk/testing", "//src/cdk/testing/testbed", "//src/material/input", diff --git a/src/material/input/testing/input-harness.ts b/src/material/input/testing/input-harness.ts index c74ea2476bae..090937d11056 100644 --- a/src/material/input/testing/input-harness.ts +++ b/src/material/input/testing/input-harness.ts @@ -111,5 +111,13 @@ export class MatInputHarness extends MatFormFieldControlHarness { if (newValue) { await inputEl.sendKeys(newValue); } + + // Some input types won't respond to key presses (e.g. `color`) so to be sure that the + // value is set, we also set the property after the keyboard sequence. Note that we don't + // want to do it before, because it can cause the value to be entered twice. + // @breaking-change 11.0.0 Remove non-null assertion once `setInputValue` is required. + if (inputEl.setInputValue) { + await inputEl.setInputValue(newValue); + } } } diff --git a/src/material/input/testing/shared.spec.ts b/src/material/input/testing/shared.spec.ts index 0a405f88520d..e82663970219 100644 --- a/src/material/input/testing/shared.spec.ts +++ b/src/material/input/testing/shared.spec.ts @@ -4,6 +4,7 @@ import {Component} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ReactiveFormsModule} from '@angular/forms'; import {MatInputModule} from '@angular/material/input'; +import {getSupportedInputTypes} from '@angular/cdk/platform'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MatInputHarness} from './input-harness'; @@ -28,7 +29,7 @@ export function runHarnessTests( it('should load all input harnesses', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); }); it('should load input with specific id', async () => { @@ -49,7 +50,7 @@ export function runHarnessTests( it('should be able to get id of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getId()).toMatch(/mat-input-\d+/); expect(await inputs[1].getId()).toMatch(/mat-input-\d+/); expect(await inputs[2].getId()).toBe('myTextarea'); @@ -59,7 +60,7 @@ export function runHarnessTests( it('should be able to get name of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getName()).toBe('favorite-food'); expect(await inputs[1].getName()).toBe(''); expect(await inputs[2].getName()).toBe(''); @@ -69,7 +70,7 @@ export function runHarnessTests( it('should be able to get value of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getValue()).toBe('Sushi'); expect(await inputs[1].getValue()).toBe(''); expect(await inputs[2].getValue()).toBe(''); @@ -79,7 +80,7 @@ export function runHarnessTests( it('should be able to set value of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getValue()).toBe('Sushi'); expect(await inputs[1].getValue()).toBe(''); expect(await inputs[3].getValue()).toBe(''); @@ -98,7 +99,7 @@ export function runHarnessTests( it('should be able to get disabled state', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].isDisabled()).toBe(false); expect(await inputs[1].isDisabled()).toBe(false); @@ -113,7 +114,7 @@ export function runHarnessTests( it('should be able to get readonly state', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].isReadonly()).toBe(false); expect(await inputs[1].isReadonly()).toBe(false); @@ -128,7 +129,7 @@ export function runHarnessTests( it('should be able to get required state', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].isRequired()).toBe(false); expect(await inputs[1].isRequired()).toBe(false); @@ -143,7 +144,7 @@ export function runHarnessTests( it('should be able to get placeholder of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getPlaceholder()).toBe('Favorite food'); expect(await inputs[1].getPlaceholder()).toBe(''); expect(await inputs[2].getPlaceholder()).toBe('Leave a comment'); @@ -153,7 +154,7 @@ export function runHarnessTests( it('should be able to get type of input', async () => { const inputs = await loader.getAllHarnesses(inputHarness); - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(6); expect(await inputs[0].getType()).toBe('text'); expect(await inputs[1].getType()).toBe('number'); expect(await inputs[2].getType()).toBe('textarea'); @@ -180,6 +181,18 @@ export function runHarnessTests( await input.blur(); expect(getActiveElementTagName()).not.toBe('input'); }); + + it('should be able to set the value of a control that cannot be typed in', async () => { + // We can't test this against browsers that don't support color inputs. + if (!getSupportedInputTypes().has('color')) { + return; + } + + const input = await loader.getHarness(inputHarness.with({selector: '#colorControl'})); + expect(await input.getValue()).toBe('#000000'); // Color inputs default to black. + await input.setValue('#00ff00'); + expect((await input.getValue()).toLowerCase()).toBe('#00ff00'); + }); } function getActiveElementTagName() { @@ -220,6 +233,10 @@ function getActiveElementTagName() { + + + + ` }) class InputHarnessTest { diff --git a/tools/public_api_guard/cdk/testing.d.ts b/tools/public_api_guard/cdk/testing.d.ts index c353c3fce6b1..69582e323a27 100644 --- a/tools/public_api_guard/cdk/testing.d.ts +++ b/tools/public_api_guard/cdk/testing.d.ts @@ -120,6 +120,7 @@ export interface TestElement { matchesSelector(selector: string): Promise; sendKeys(...keys: (string | TestKey)[]): Promise; sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise; + setInputValue?(value: string): Promise; text(): Promise; } diff --git a/tools/public_api_guard/cdk/testing/protractor.d.ts b/tools/public_api_guard/cdk/testing/protractor.d.ts index b142d71a1530..f17b018bf85a 100644 --- a/tools/public_api_guard/cdk/testing/protractor.d.ts +++ b/tools/public_api_guard/cdk/testing/protractor.d.ts @@ -15,6 +15,7 @@ export declare class ProtractorElement implements TestElement { matchesSelector(selector: string): Promise; sendKeys(...keys: (string | TestKey)[]): Promise; sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise; + setInputValue(value: string): Promise; text(): Promise; } diff --git a/tools/public_api_guard/cdk/testing/testbed.d.ts b/tools/public_api_guard/cdk/testing/testbed.d.ts index d807e1ff6669..ee74b4456ffb 100644 --- a/tools/public_api_guard/cdk/testing/testbed.d.ts +++ b/tools/public_api_guard/cdk/testing/testbed.d.ts @@ -32,5 +32,6 @@ export declare class UnitTestElement implements TestElement { matchesSelector(selector: string): Promise; sendKeys(...keys: (string | TestKey)[]): Promise; sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise; + setInputValue(value: string): Promise; text(): Promise; }