Skip to content

Commit 34bc6de

Browse files
committed
fix(input/testing): unable to set the value of inputs that don't respond to typing
The `setValue` method of the input harness is set up so that it types out the value in the input. This won't work for input that don't respond to typing (e.g. `color`). These changes add an extra call that also sets the `value` property of the input, on top of typing into it. Fixes #18790.
1 parent 807498d commit 34bc6de

File tree

11 files changed

+61
-10
lines changed

11 files changed

+61
-10
lines changed

src/cdk/testing/protractor/protractor-element.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ export class ProtractorElement implements TestElement {
142142
return browser.executeScript(`return arguments[0][arguments[1]]`, this.element, name);
143143
}
144144

145+
async setProperty(name: string, value: any): Promise<void> {
146+
return browser.executeScript(`arguments[0][arguments[1]] = arguments[2]`,
147+
this.element, name, value);
148+
}
149+
145150
async matchesSelector(selector: string): Promise<boolean> {
146151
return browser.executeScript(`
147152
return (Element.prototype.matches ||

src/cdk/testing/test-element.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ export interface TestElement {
110110
/** Gets the value of a property of an element. */
111111
getProperty(name: string): Promise<any>;
112112

113+
/** Sets the value of a property of an element. */
114+
setProperty(name: string, value: any): Promise<void>;
115+
113116
/** Checks whether this element matches the given selector. */
114117
matchesSelector(selector: string): Promise<boolean>;
115118

src/cdk/testing/testbed/unit-test-element.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ export class UnitTestElement implements TestElement {
136136
return (this.element as any)[name];
137137
}
138138

139+
async setProperty(name: string, value: any): Promise<void> {
140+
await this._stabilize();
141+
(this.element as any)[name] = value;
142+
}
143+
139144
async matchesSelector(selector: string): Promise<boolean> {
140145
await this._stabilize();
141146
const elementPrototype = Element.prototype as any;

src/cdk/testing/tests/protractor.e2e.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ describe('ProtractorHarnessEnvironment', () => {
283283
expect(await input.getProperty('value')).toBe('Hello');
284284
});
285285

286+
it('should be able to set the value of a property', async () => {
287+
const input = await harness.input();
288+
await input.setProperty('name', 'hello');
289+
expect(await input.getProperty('name')).toBe('hello');
290+
});
291+
286292
it('should check if selector matches', async () => {
287293
const button = await harness.button();
288294
expect(await button.matchesSelector('button:not(.fake-class)')).toBe(true);

src/cdk/testing/tests/testbed.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,12 @@ describe('TestbedHarnessEnvironment', () => {
368368
expect(await input.getProperty('value')).toBe('Hello');
369369
});
370370

371+
it('should be able to set the value of a property', async () => {
372+
const input = await harness.input();
373+
await input.setProperty('name', 'hello');
374+
expect(await input.getProperty('name')).toBe('hello');
375+
});
376+
371377
it('should check if selector matches', async () => {
372378
const button = await harness.button();
373379
expect(await button.matchesSelector('button:not(.fake-class)')).toBe(true);

src/material/input/testing/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ ng_test_library(
2626
srcs = ["shared.spec.ts"],
2727
deps = [
2828
":testing",
29+
"//src/cdk/platform",
2930
"//src/cdk/testing",
3031
"//src/cdk/testing/testbed",
3132
"//src/material/input",

src/material/input/testing/input-harness.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,10 @@ export class MatInputHarness extends MatFormFieldControlHarness {
111111
if (newValue) {
112112
await inputEl.sendKeys(newValue);
113113
}
114+
115+
// Some input types won't respond to key presses (e.g. `color`) so to be sure that the
116+
// value is set, we also set the property after the keyboard sequence. Note that we don't
117+
// want to do it before, because it can cause the value to be entered twice.
118+
await inputEl.setProperty('value', newValue);
114119
}
115120
}

src/material/input/testing/shared.spec.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {Component} from '@angular/core';
44
import {ComponentFixture, TestBed} from '@angular/core/testing';
55
import {ReactiveFormsModule} from '@angular/forms';
66
import {MatInputModule} from '@angular/material/input';
7+
import {getSupportedInputTypes} from '@angular/cdk/platform';
78
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
89
import {MatInputHarness} from './input-harness';
910

@@ -28,7 +29,7 @@ export function runHarnessTests(
2829

2930
it('should load all input harnesses', async () => {
3031
const inputs = await loader.getAllHarnesses(inputHarness);
31-
expect(inputs.length).toBe(5);
32+
expect(inputs.length).toBe(6);
3233
});
3334

3435
it('should load input with specific id', async () => {
@@ -49,7 +50,7 @@ export function runHarnessTests(
4950

5051
it('should be able to get id of input', async () => {
5152
const inputs = await loader.getAllHarnesses(inputHarness);
52-
expect(inputs.length).toBe(5);
53+
expect(inputs.length).toBe(6);
5354
expect(await inputs[0].getId()).toMatch(/mat-input-\d+/);
5455
expect(await inputs[1].getId()).toMatch(/mat-input-\d+/);
5556
expect(await inputs[2].getId()).toBe('myTextarea');
@@ -59,7 +60,7 @@ export function runHarnessTests(
5960

6061
it('should be able to get name of input', async () => {
6162
const inputs = await loader.getAllHarnesses(inputHarness);
62-
expect(inputs.length).toBe(5);
63+
expect(inputs.length).toBe(6);
6364
expect(await inputs[0].getName()).toBe('favorite-food');
6465
expect(await inputs[1].getName()).toBe('');
6566
expect(await inputs[2].getName()).toBe('');
@@ -69,7 +70,7 @@ export function runHarnessTests(
6970

7071
it('should be able to get value of input', async () => {
7172
const inputs = await loader.getAllHarnesses(inputHarness);
72-
expect(inputs.length).toBe(5);
73+
expect(inputs.length).toBe(6);
7374
expect(await inputs[0].getValue()).toBe('Sushi');
7475
expect(await inputs[1].getValue()).toBe('');
7576
expect(await inputs[2].getValue()).toBe('');
@@ -79,7 +80,7 @@ export function runHarnessTests(
7980

8081
it('should be able to set value of input', async () => {
8182
const inputs = await loader.getAllHarnesses(inputHarness);
82-
expect(inputs.length).toBe(5);
83+
expect(inputs.length).toBe(6);
8384
expect(await inputs[0].getValue()).toBe('Sushi');
8485
expect(await inputs[1].getValue()).toBe('');
8586
expect(await inputs[3].getValue()).toBe('');
@@ -98,7 +99,7 @@ export function runHarnessTests(
9899

99100
it('should be able to get disabled state', async () => {
100101
const inputs = await loader.getAllHarnesses(inputHarness);
101-
expect(inputs.length).toBe(5);
102+
expect(inputs.length).toBe(6);
102103

103104
expect(await inputs[0].isDisabled()).toBe(false);
104105
expect(await inputs[1].isDisabled()).toBe(false);
@@ -113,7 +114,7 @@ export function runHarnessTests(
113114

114115
it('should be able to get readonly state', async () => {
115116
const inputs = await loader.getAllHarnesses(inputHarness);
116-
expect(inputs.length).toBe(5);
117+
expect(inputs.length).toBe(6);
117118

118119
expect(await inputs[0].isReadonly()).toBe(false);
119120
expect(await inputs[1].isReadonly()).toBe(false);
@@ -128,7 +129,7 @@ export function runHarnessTests(
128129

129130
it('should be able to get required state', async () => {
130131
const inputs = await loader.getAllHarnesses(inputHarness);
131-
expect(inputs.length).toBe(5);
132+
expect(inputs.length).toBe(6);
132133

133134
expect(await inputs[0].isRequired()).toBe(false);
134135
expect(await inputs[1].isRequired()).toBe(false);
@@ -143,7 +144,7 @@ export function runHarnessTests(
143144

144145
it('should be able to get placeholder of input', async () => {
145146
const inputs = await loader.getAllHarnesses(inputHarness);
146-
expect(inputs.length).toBe(5);
147+
expect(inputs.length).toBe(6);
147148
expect(await inputs[0].getPlaceholder()).toBe('Favorite food');
148149
expect(await inputs[1].getPlaceholder()).toBe('');
149150
expect(await inputs[2].getPlaceholder()).toBe('Leave a comment');
@@ -153,7 +154,7 @@ export function runHarnessTests(
153154

154155
it('should be able to get type of input', async () => {
155156
const inputs = await loader.getAllHarnesses(inputHarness);
156-
expect(inputs.length).toBe(5);
157+
expect(inputs.length).toBe(6);
157158
expect(await inputs[0].getType()).toBe('text');
158159
expect(await inputs[1].getType()).toBe('number');
159160
expect(await inputs[2].getType()).toBe('textarea');
@@ -180,6 +181,18 @@ export function runHarnessTests(
180181
await input.blur();
181182
expect(getActiveElementTagName()).not.toBe('input');
182183
});
184+
185+
it('should be able to set the value of a control that cannot be typed in', async () => {
186+
// We can't test this against browsers that don't support color inputs.
187+
if (!getSupportedInputTypes().has('color')) {
188+
return;
189+
}
190+
191+
const input = await loader.getHarness(inputHarness.with({selector: '#colorControl'}));
192+
expect(await input.getValue()).toBe('#000000'); // Color inputs default to black.
193+
await input.setValue('#00ff00');
194+
expect((await input.getValue()).toLowerCase()).toBe('#00ff00');
195+
});
183196
}
184197

185198
function getActiveElementTagName() {
@@ -220,6 +233,10 @@ function getActiveElementTagName() {
220233
<option value="first">First</option>
221234
</select>
222235
</mat-form-field>
236+
237+
<mat-form-field>
238+
<input matNativeControl placeholder="Color control" id="colorControl" type="color">
239+
</mat-form-field>
223240
`
224241
})
225242
class InputHarnessTest {

tools/public_api_guard/cdk/testing.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export interface TestElement {
119119
matchesSelector(selector: string): Promise<boolean>;
120120
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
121121
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
122+
setProperty(name: string, value: any): Promise<void>;
122123
text(): Promise<string>;
123124
}
124125

tools/public_api_guard/cdk/testing/protractor.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export declare class ProtractorElement implements TestElement {
1515
matchesSelector(selector: string): Promise<boolean>;
1616
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
1717
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
18+
setProperty(name: string, value: any): Promise<void>;
1819
text(): Promise<string>;
1920
}
2021

tools/public_api_guard/cdk/testing/testbed.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ export declare class UnitTestElement implements TestElement {
3232
matchesSelector(selector: string): Promise<boolean>;
3333
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
3434
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
35+
setProperty(name: string, value: any): Promise<void>;
3536
text(): Promise<string>;
3637
}

0 commit comments

Comments
 (0)