Skip to content

Commit 62bd54e

Browse files
committed
feat(cdk/testing): add method to set the text of an element
Adds the `TestElement.setText` method that sets the text content of a node. We need this in order to set the value on a `contenteditable` element.
1 parent a7842cc commit 62bd54e

10 files changed

+71
-0
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,20 @@ export class ProtractorElement implements TestElement {
199199
return browser.executeScript(`return (arguments[0].textContent || '').trim()`, this.element);
200200
}
201201

202+
/**
203+
* Sets the value of a `contenteditable` element.
204+
* @param value Value to be set on the element.
205+
*/
206+
async setContenteditableValue(value: string): Promise<void> {
207+
const contenteditableAttr = await this.getAttribute('contenteditable');
208+
209+
if (contenteditableAttr !== '' && contenteditableAttr !== 'true') {
210+
throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
211+
}
212+
213+
return browser.executeScript(`arguments[0].textContent = arguments[1];`, this.element, value);
214+
}
215+
202216
/** Gets the value for the given attribute from the element. */
203217
async getAttribute(name: string): Promise<string | null> {
204218
return browser.executeScript(

src/cdk/testing/selenium-webdriver/selenium-web-driver-element.ts

+19
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,25 @@ export class SeleniumWebDriverElement implements TestElement {
154154
);
155155
}
156156

157+
/**
158+
* Sets the value of a `contenteditable` element.
159+
* @param value Value to be set on the element.
160+
*/
161+
async setContenteditableValue(value: string): Promise<void> {
162+
const contenteditableAttr = await this.getAttribute('contenteditable');
163+
164+
if (contenteditableAttr !== '' && contenteditableAttr !== 'true') {
165+
throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
166+
}
167+
168+
await this._stabilize();
169+
return this._executeScript(
170+
(element: Element, valueToSet: string) => (element.textContent = valueToSet),
171+
this.element(),
172+
value,
173+
);
174+
}
175+
157176
/** Gets the value for the given attribute from the element. */
158177
async getAttribute(name: string): Promise<string | null> {
159178
await this._stabilize();

src/cdk/testing/test-element.ts

+7
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ export interface TestElement {
137137
*/
138138
text(options?: TextOptions): Promise<string>;
139139

140+
/**
141+
* Sets the value of a `contenteditable` element.
142+
* @param value Value to be set on the element.
143+
* @breaking-change 16.0.0 Will become a required method.
144+
*/
145+
setContenteditableValue?(value: string): Promise<void>;
146+
140147
/** Gets the value for the given attribute from the element. */
141148
getAttribute(name: string): Promise<string | null>;
142149

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

+15
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,21 @@ export class UnitTestElement implements TestElement {
185185
return (this.element.textContent || '').trim();
186186
}
187187

188+
/**
189+
* Sets the value of a `contenteditable` element.
190+
* @param value Value to be set on the element.
191+
*/
192+
async setContenteditableValue(value: string): Promise<void> {
193+
const contenteditableAttr = await this.getAttribute('contenteditable');
194+
195+
if (contenteditableAttr !== '' && contenteditableAttr !== 'true') {
196+
throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
197+
}
198+
199+
await this._stabilize();
200+
this.element.textContent = value;
201+
}
202+
188203
/** Gets the value for the given attribute from the element. */
189204
async getAttribute(name: string): Promise<string | null> {
190205
await this._stabilize();

src/cdk/testing/tests/cross-environment.spec.ts

+11
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,17 @@ export function crossEnvironmentSpecs(
716716
const hiddenElement = await harness.hidden();
717717
expect(await hiddenElement.text()).toBe('Hello');
718718
});
719+
720+
it('should be able to set the value of a contenteditable element', async () => {
721+
const element = await harness.contenteditable();
722+
expect(await element.text()).not.toBe('hello');
723+
724+
// @breaking-change 16.0.0 Remove non-null assertion once `setContenteditableValue`
725+
// becomes a required method.
726+
await element.setContenteditableValue!('hello');
727+
728+
expect(await element.text()).toBe('hello');
729+
});
719730
});
720731
}
721732

src/cdk/testing/tests/harnesses/main-component-harness.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class MainComponentHarness extends ComponentHarness {
4040
readonly numberInput = this.locatorFor('#number-input');
4141
readonly numberInputValue = this.locatorFor('#number-input-value');
4242
readonly contextmenuTestResult = this.locatorFor('.contextmenu-test-result');
43+
readonly contenteditable = this.locatorFor('#contenteditable');
4344
// Allow null for element
4445
readonly nullItem = this.locatorForOptional('wrong locator');
4546
// Allow null for component harness

src/cdk/testing/tests/test-main-component.html

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ <h1 style="height: 100px; width: 200px;">Main Component</h1>
1414
<label>AsyncCounter:</label>
1515
<div id="asyncCounter">{{asyncCounter}}</div>
1616
</div>
17+
<span id="contenteditable" contenteditable>initial value</span>
1718
<div class="inputs">
1819
<input [(ngModel)]="input" id="input" aria-label="input" (keydown)="onKeyDown($event)">
1920
<span class="special-key">{{specialKey}}</span>

tools/public_api_guard/cdk/testing-selenium-webdriver.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class SeleniumWebDriverElement implements TestElement {
3939
selectOptions(...optionIndexes: number[]): Promise<void>;
4040
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
4141
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
42+
setContenteditableValue(value: string): Promise<void>;
4243
setInputValue(newValue: string): Promise<void>;
4344
text(options?: TextOptions): Promise<string>;
4445
}

tools/public_api_guard/cdk/testing-testbed.md

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export class UnitTestElement implements TestElement {
6161
selectOptions(...optionIndexes: number[]): Promise<void>;
6262
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
6363
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
64+
setContenteditableValue(value: string): Promise<void>;
6465
setInputValue(value: string): Promise<void>;
6566
text(options?: TextOptions): Promise<string>;
6667
}

tools/public_api_guard/cdk/testing.md

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ export interface TestElement {
251251
selectOptions(...optionIndexes: number[]): Promise<void>;
252252
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
253253
sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
254+
setContenteditableValue?(value: string): Promise<void>;
254255
setInputValue(value: string): Promise<void>;
255256
text(options?: TextOptions): Promise<string>;
256257
}

0 commit comments

Comments
 (0)