Skip to content

Commit 22268c3

Browse files
mmalerbajelbourn
authored andcommitted
refactor(cdk-experimental/testing): refactor the ComponentHarness API (#16234)
Renames several APIs for readability
1 parent 30127fa commit 22268c3

16 files changed

+973
-698
lines changed

src/cdk-experimental/testing/component-harness.ts

Lines changed: 190 additions & 126 deletions
Large diffs are not rendered by default.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {
10+
AsyncFn,
11+
ComponentHarness,
12+
ComponentHarnessConstructor,
13+
HarnessLoader,
14+
LocatorFactory
15+
} from './component-harness';
16+
import {TestElement} from './test-element';
17+
18+
/**
19+
* Base harness environment class that can be extended to allow `ComponentHarness`es to be used in
20+
* different test environments (e.g. testbed, protractor, etc.). This class implements the
21+
* functionality of both a `HarnessLoader` and `LocatorFactory`. This class is generic on the raw
22+
* element type, `E`, used by the particular test environment.
23+
*/
24+
export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFactory {
25+
// Implemented as part of the `LocatorFactory` interface.
26+
rootElement: TestElement;
27+
28+
protected constructor(protected rawRootElement: E) {
29+
this.rootElement = this.createTestElement(rawRootElement);
30+
}
31+
32+
// Implemented as part of the `LocatorFactory` interface.
33+
documentRootLocatorFactory(): LocatorFactory {
34+
return this.createEnvironment(this.getDocumentRoot());
35+
}
36+
37+
// Implemented as part of the `LocatorFactory` interface.
38+
locatorFor(selector: string): AsyncFn<TestElement>;
39+
locatorFor<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
40+
AsyncFn<T>;
41+
locatorFor<T extends ComponentHarness>(
42+
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement | T> {
43+
return async () => {
44+
if (typeof arg === 'string') {
45+
const element = await this.getRawElement(arg);
46+
if (element) {
47+
return this.createTestElement(element);
48+
}
49+
} else {
50+
const element = await this.getRawElement(arg.hostSelector);
51+
if (element) {
52+
return this.createComponentHarness(arg, element);
53+
}
54+
}
55+
const selector = typeof arg === 'string' ? arg : arg.hostSelector;
56+
throw Error(`Expected to find element matching selector: "${selector}", but none was found`);
57+
};
58+
}
59+
60+
// Implemented as part of the `LocatorFactory` interface.
61+
locatorForOptional(selector: string): AsyncFn<TestElement | null>;
62+
locatorForOptional<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
63+
AsyncFn<T | null>;
64+
locatorForOptional<T extends ComponentHarness>(
65+
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement | T | null> {
66+
return async () => {
67+
if (typeof arg === 'string') {
68+
const element = await this.getRawElement(arg);
69+
return element ? this.createTestElement(element) : null;
70+
} else {
71+
const element = await this.getRawElement(arg.hostSelector);
72+
return element ? this.createComponentHarness(arg, element) : null;
73+
}
74+
};
75+
}
76+
77+
// Implemented as part of the `LocatorFactory` interface.
78+
locatorForAll(selector: string): AsyncFn<TestElement[]>;
79+
locatorForAll<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
80+
AsyncFn<T[]>;
81+
locatorForAll<T extends ComponentHarness>(
82+
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement[] | T[]> {
83+
return async () => {
84+
if (typeof arg === 'string') {
85+
return (await this.getAllRawElements(arg)).map(e => this.createTestElement(e));
86+
} else {
87+
return (await this.getAllRawElements(arg.hostSelector))
88+
.map(e => this.createComponentHarness(arg, e));
89+
}
90+
};
91+
}
92+
93+
// Implemented as part of the `HarnessLoader` interface.
94+
getHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
95+
Promise<T> {
96+
return this.locatorFor(harnessType)();
97+
}
98+
99+
// Implemented as part of the `HarnessLoader` interface.
100+
getAllHarnesses<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
101+
Promise<T[]> {
102+
return this.locatorForAll(harnessType)();
103+
}
104+
105+
// Implemented as part of the `HarnessLoader` interface.
106+
async getChildLoader(selector: string): Promise<HarnessLoader> {
107+
const element = await this.getRawElement(selector);
108+
if (element) {
109+
return this.createEnvironment(element);
110+
}
111+
throw Error(`Expected to find element matching selector: "${selector}", but none was found`);
112+
}
113+
114+
// Implemented as part of the `HarnessLoader` interface.
115+
async getAllChildLoaders(selector: string): Promise<HarnessLoader[]> {
116+
return (await this.getAllRawElements(selector)).map(e => this.createEnvironment(e));
117+
}
118+
119+
/** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
120+
protected createComponentHarness<T extends ComponentHarness>(
121+
harnessType: ComponentHarnessConstructor<T>, element: E): T {
122+
return new harnessType(this.createEnvironment(element));
123+
}
124+
125+
/** Gets the root element for the document. */
126+
protected abstract getDocumentRoot(): E;
127+
128+
/** Creates a `TestElement` from a raw element. */
129+
protected abstract createTestElement(element: E): TestElement;
130+
131+
/** Creates a `HarnessLoader` rooted at the given raw element. */
132+
protected abstract createEnvironment(element: E): HarnessEnvironment<E>;
133+
134+
/**
135+
* Gets the first element matching the given selector under this environment's root element, or
136+
* null if no elements match.
137+
*/
138+
protected abstract getRawElement(selector: string): Promise<E | null>;
139+
140+
/**
141+
* Gets a list of all elements matching the given selector under this environment's root element.
142+
*/
143+
protected abstract getAllRawElements(selector: string): Promise<E[]>;
144+
}

src/cdk-experimental/testing/protractor.ts

Lines changed: 0 additions & 170 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './protractor-element';
10+
export * from './protractor-harness-environment';
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {browser, ElementFinder} from 'protractor';
10+
import {TestElement} from '../test-element';
11+
12+
/** A `TestElement` implementation for Protractor. */
13+
export class ProtractorElement implements TestElement {
14+
constructor(readonly element: ElementFinder) {}
15+
16+
async blur(): Promise<void> {
17+
return browser.executeScript('arguments[0].blur()', this.element);
18+
}
19+
20+
async clear(): Promise<void> {
21+
return this.element.clear();
22+
}
23+
24+
async click(): Promise<void> {
25+
return this.element.click();
26+
}
27+
28+
async focus(): Promise<void> {
29+
return browser.executeScript('arguments[0].focus()', this.element);
30+
}
31+
32+
async getCssValue(property: string): Promise<string> {
33+
return this.element.getCssValue(property);
34+
}
35+
36+
async hover(): Promise<void> {
37+
return browser.actions()
38+
.mouseMove(await this.element.getWebElement())
39+
.perform();
40+
}
41+
42+
async sendKeys(keys: string): Promise<void> {
43+
return this.element.sendKeys(keys);
44+
}
45+
46+
async text(): Promise<string> {
47+
return this.element.getText();
48+
}
49+
50+
async getAttribute(name: string): Promise<string|null> {
51+
return this.element.getAttribute(name);
52+
}
53+
}

0 commit comments

Comments
 (0)