Skip to content

Commit 89193f5

Browse files
mmalerbaqiyiggrkirovtrshaferCaitlin Mott
authored andcommitted
feat(cdk-experimental/testing): Bring in component harness (#16089)
Co-Authored-By: Yi Qi <qiyi@google.com> Co-Authored-By: Rado Kirov <radokirov@google.com> Co-Authored-By: Thomas Shafer <tshafer@google.com> Co-Authored-By: Caitlin Mott <caitlinmott@google.com> Co-Authored-By: Craig Nishina <cnishina@google.com>
1 parent 3bc6020 commit 89193f5

28 files changed

+1337
-4
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,9 @@
9898
# CDK experimental package
9999
/src/cdk-experimental/** @jelbourn
100100
/src/cdk-experimental/dialog/** @jelbourn @josephperrott @crisbeto
101-
/src/cdk-experimental/scrolling/** @mmalerba
102101
/src/cdk-experimental/popover-edit/** @kseamon @andrewseguin
102+
/src/cdk-experimental/scrolling/** @mmalerba
103+
/src/cdk-experimental/testing/** @mmalerba
103104

104105
# Docs examples & guides
105106
/guides/** @jelbourn
@@ -177,6 +178,7 @@
177178
/src/e2e-app/button-toggle/** @jelbourn
178179
/src/e2e-app/card/** @jelbourn
179180
/src/e2e-app/checkbox/** @jelbourn @devversion
181+
/src/e2e-app/component-harness/** @mmalerba
180182
/src/e2e-app/dialog/** @jelbourn @crisbeto
181183
/src/e2e-app/e2e-app/** @jelbourn
182184
/src/e2e-app/example-viewer/** @andrewseguin
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load("//tools:defaults.bzl", "ng_module", "ng_web_test_suite")
4+
load("@npm_angular_bazel//:index.bzl", "protractor_web_test_suite")
5+
6+
ng_module(
7+
name = "testing",
8+
srcs = glob(
9+
["**/*.ts"],
10+
exclude = [
11+
"**/*.spec.ts",
12+
"tests/**",
13+
],
14+
),
15+
module_name = "@angular/cdk-experimental/testing",
16+
deps = [
17+
"//src/cdk/testing",
18+
"@npm//@angular/core",
19+
"@npm//protractor",
20+
],
21+
)
22+
23+
ng_web_test_suite(
24+
name = "unit_tests",
25+
deps = ["//src/cdk-experimental/testing/tests:unit_test_sources"],
26+
)
27+
28+
protractor_web_test_suite(
29+
name = "e2e_tests",
30+
configuration = "//src/e2e-app:protractor.conf.js",
31+
data = [
32+
"//tools/axe-protractor",
33+
"@npm//@angular/bazel",
34+
],
35+
on_prepare = "//src/e2e-app:start-devserver.js",
36+
server = "//src/e2e-app:devserver",
37+
tags = ["e2e"],
38+
deps = [
39+
"//src/cdk-experimental/testing/tests:e2e_test_sources",
40+
"@npm//protractor",
41+
],
42+
)
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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 {TestElement} from './test-element';
10+
11+
/** Options that can be specified when querying for an Element. */
12+
export interface QueryOptions {
13+
/**
14+
* Whether the found element can be null. If allowNull is set, the searching function will always
15+
* try to fetch the element at once. When the element cannot be found, the searching function
16+
* should return null if allowNull is set to true, throw an error if allowNull is set to false.
17+
* If allowNull is not set, the framework will choose the behaviors that make more sense for each
18+
* test type (e.g. for unit test, the framework will make sure the element is not null; otherwise
19+
* throw an error); however, the internal behavior is not guaranteed and user should not rely on
20+
* it. Note that in most cases, you don't need to care about whether an element is present when
21+
* loading the element and don't need to set this parameter unless you do want to check whether
22+
* the element is present when calling the searching function. e.g. you want to make sure some
23+
* element is not there when loading the element in order to check whether a "ngif" works well.
24+
*/
25+
allowNull?: boolean;
26+
/**
27+
* If global is set to true, the selector will match any element on the page and is not limited to
28+
* the root of the harness. If global is unset or set to false, the selector will only find
29+
* elements under the current root.
30+
*/
31+
global?: boolean;
32+
}
33+
34+
/** Interface that is used to find elements in the DOM and create harnesses for them. */
35+
export interface HarnessLocator {
36+
/**
37+
* Get the host element of locator.
38+
*/
39+
host(): TestElement;
40+
41+
/**
42+
* Search the first matched test element.
43+
* @param selector The CSS selector of the test elements.
44+
* @param options Optional, extra searching options
45+
*/
46+
querySelector(selector: string, options?: QueryOptions): Promise<TestElement|null>;
47+
48+
/**
49+
* Search all matched test elements under current root by CSS selector.
50+
* @param selector The CSS selector of the test elements.
51+
*/
52+
querySelectorAll(selector: string): Promise<TestElement[]>;
53+
54+
/**
55+
* Load the first matched Component Harness.
56+
* @param componentHarness Type of user customized harness.
57+
* @param root CSS root selector of the new component harness.
58+
* @param options Optional, extra searching options
59+
*/
60+
load<T extends ComponentHarness>(
61+
componentHarness: ComponentHarnessConstructor<T>, root: string,
62+
options?: QueryOptions): Promise<T|null>;
63+
64+
/**
65+
* Load all Component Harnesses under current root.
66+
* @param componentHarness Type of user customized harness.
67+
* @param root CSS root selector of the new component harnesses.
68+
*/
69+
loadAll<T extends ComponentHarness>(
70+
componentHarness: ComponentHarnessConstructor<T>, root: string): Promise<T[]>;
71+
}
72+
73+
/**
74+
* Base Component Harness
75+
* This base component harness provides the basic ability to locate element and
76+
* sub-component harness. It should be inherited when defining user's own
77+
* harness.
78+
*/
79+
export abstract class ComponentHarness {
80+
constructor(private readonly locator: HarnessLocator) {}
81+
82+
/**
83+
* Get the host element of component harness.
84+
*/
85+
host(): TestElement {
86+
return this.locator.host();
87+
}
88+
89+
/**
90+
* Generate a function to find the first matched test element by CSS
91+
* selector.
92+
* @param selector The CSS selector of the test element.
93+
*/
94+
protected find(selector: string): () => Promise<TestElement>;
95+
96+
/**
97+
* Generate a function to find the first matched test element by CSS
98+
* selector.
99+
* @param selector The CSS selector of the test element.
100+
* @param options Extra searching options
101+
*/
102+
protected find(selector: string, options: QueryOptions & {allowNull: true}):
103+
() => Promise<TestElement|null>;
104+
105+
/**
106+
* Generate a function to find the first matched test element by CSS
107+
* selector.
108+
* @param selector The CSS selector of the test element.
109+
* @param options Extra searching options
110+
*/
111+
protected find(selector: string, options: QueryOptions): () => Promise<TestElement>;
112+
113+
/**
114+
* Generate a function to find the first matched Component Harness.
115+
* @param componentHarness Type of user customized harness.
116+
* @param root CSS root selector of the new component harness.
117+
*/
118+
protected find<T extends ComponentHarness>(
119+
componentHarness: ComponentHarnessConstructor<T>,
120+
root: string): () => Promise<T>;
121+
122+
/**
123+
* Generate a function to find the first matched Component Harness.
124+
* @param componentHarness Type of user customized harness.
125+
* @param root CSS root selector of the new component harness.
126+
* @param options Extra searching options
127+
*/
128+
protected find<T extends ComponentHarness>(
129+
componentHarness: ComponentHarnessConstructor<T>, root: string,
130+
options: QueryOptions & {allowNull: true}): () => Promise<T|null>;
131+
132+
/**
133+
* Generate a function to find the first matched Component Harness.
134+
* @param componentHarness Type of user customized harness.
135+
* @param root CSS root selector of the new component harness.
136+
* @param options Extra searching options
137+
*/
138+
protected find<T extends ComponentHarness>(
139+
componentHarness: ComponentHarnessConstructor<T>, root: string,
140+
options: QueryOptions): () => Promise<T>;
141+
142+
protected find<T extends ComponentHarness>(
143+
selectorOrComponentHarness: string|ComponentHarnessConstructor<T>,
144+
selectorOrOptions?: string|QueryOptions,
145+
options?: QueryOptions): () => Promise<TestElement|T|null> {
146+
if (typeof selectorOrComponentHarness === 'string') {
147+
const selector = selectorOrComponentHarness;
148+
return () => this.locator.querySelector(selector, selectorOrOptions as QueryOptions);
149+
} else {
150+
const componentHarness = selectorOrComponentHarness;
151+
const selector = selectorOrOptions as string;
152+
return () => this.locator.load(componentHarness, selector, options);
153+
}
154+
}
155+
156+
/**
157+
* Generate a function to find all matched test elements by CSS selector.
158+
* @param selector The CSS root selector of elements. It will locate
159+
* elements under the current root.
160+
*/
161+
protected findAll(selector: string): () => Promise<TestElement[]>;
162+
163+
/**
164+
* Generate a function to find all Component Harnesses under current
165+
* component harness.
166+
* @param componentHarness Type of user customized harness.
167+
* @param root CSS root selector of the new component harnesses. It will
168+
* locate harnesses under the current root.
169+
*/
170+
protected findAll<T extends ComponentHarness>(
171+
componentHarness: ComponentHarnessConstructor<T>,
172+
root: string): () => Promise<T[]>;
173+
174+
protected findAll<T extends ComponentHarness>(
175+
selectorOrComponentHarness: string|ComponentHarnessConstructor<T>,
176+
root?: string): () => Promise<TestElement[]|T[]> {
177+
if (typeof selectorOrComponentHarness === 'string') {
178+
const selector = selectorOrComponentHarness;
179+
return () => this.locator.querySelectorAll(selector);
180+
} else {
181+
const componentHarness = selectorOrComponentHarness;
182+
return () => this.locator.loadAll(componentHarness, root as string);
183+
}
184+
}
185+
}
186+
187+
/** Constructor for a ComponentHarness subclass. */
188+
export interface ComponentHarnessConstructor<T extends ComponentHarness> {
189+
new(locator: HarnessLocator): T;
190+
}

src/cdk-experimental/testing/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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 './public-api';

0 commit comments

Comments
 (0)