@badisi/wdio-harness 4.3.4 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </h1>
4
4
 
5
5
  <p align="center">
6
- <i>🔬 <a href="https://webdriver.io" alt="wdio">WebdriverIO</a> support for Angular component test harnesses.</i><br/>
6
+ 🔬 <i><a href="https://webdriver.io" alt="wdio">WebdriverIO</a> support for Angular component test harnesses.</i><br/>
7
7
  </p>
8
8
 
9
9
  <p align="center">
package/index.d.mts ADDED
@@ -0,0 +1,164 @@
1
+ import { ComponentHarness, ComponentHarnessConstructor, ElementDimensions, EventData, HarnessEnvironment, HarnessQuery, ModifierKeys, TestElement, TestKey, TextOptions } from "@angular/cdk/testing";
2
+ //#region src/WebdriverIOTestElement.d.ts
3
+ /** Module augmentation to expose the host element as a public api */
4
+ declare module "@angular/cdk/testing" {
5
+ interface ComponentHarness {
6
+ element(): WebdriverIO.Element;
7
+ }
8
+ interface TestElement {
9
+ element(): WebdriverIO.Element;
10
+ }
11
+ }
12
+ /**
13
+ * A `TestElement` implementation for WebdriverIO.
14
+ */
15
+ declare class WebdriverIOTestElement implements TestElement {
16
+ readonly hostElement: WebdriverIO.Element;
17
+ constructor(hostElement: WebdriverIO.Element);
18
+ /** Return the host element. */
19
+ element(): WebdriverIO.Element;
20
+ /** Blur the element. */
21
+ blur(): Promise<void>;
22
+ /** Clear the element's input (for input and textarea elements only). */
23
+ clear(): Promise<void>;
24
+ /**
25
+ * Click the element at the default location for the current environment. If you need to guarantee
26
+ * the element is clicked at a specific location, consider using `click('center')` or
27
+ * `click(x, y)` instead.
28
+ */
29
+ click(modifiers?: ModifierKeys): Promise<void>;
30
+ /** Click the element at the element's center. */
31
+ click(location: "center", modifiers?: ModifierKeys): Promise<void>;
32
+ /**
33
+ * Click the element at the specified coordinates relative to the top-left of the element.
34
+ * @param relativeX Coordinate within the element, along the X-axis at which to click.
35
+ * @param relativeY Coordinate within the element, along the Y-axis at which to click.
36
+ * @param modifiers Modifier keys held while clicking
37
+ */
38
+ click(relativeX: number, relativeY: number, modifiers?: ModifierKeys): Promise<void>;
39
+ /**
40
+ * Right clicks on the element at the specified coordinates relative to the top-left of it.
41
+ * @param relativeX Coordinate within the element, along the X-axis at which to click.
42
+ * @param relativeY Coordinate within the element, along the Y-axis at which to click.
43
+ * @param modifiers Modifier keys held while clicking
44
+ */
45
+ rightClick(relativeX: number, relativeY: number, modifiers?: ModifierKeys): Promise<void>;
46
+ /** Focus the element. */
47
+ focus(): Promise<void>;
48
+ /** Get the computed value of the given CSS property for the element. */
49
+ getCssValue(property: string): Promise<string>;
50
+ /** Hovers the mouse over the element. */
51
+ hover(): Promise<void>;
52
+ /** Moves the mouse away from the element. */
53
+ mouseAway(): Promise<void>;
54
+ /**
55
+ * Sends the given string to the input as a series of key presses. Also fires input events
56
+ * and attempts to add the string to the Element's value.
57
+ */
58
+ sendKeys(...keys: (string | TestKey)[]): Promise<void>;
59
+ /**
60
+ * Sends the given string to the input as a series of key presses. Also fires input events
61
+ * and attempts to add the string to the Element's value.
62
+ */
63
+ sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
64
+ /** Gets the text from the element. */
65
+ text(options?: TextOptions): Promise<string>;
66
+ /**
67
+ * Sets the value of a `contenteditable` element.
68
+ * @param value Value to be set on the element.
69
+ */
70
+ setContenteditableValue(value: string): Promise<void>;
71
+ /** Gets the value for the given attribute from the element. */
72
+ getAttribute(name: string): Promise<string | null>;
73
+ /** Checks whether the element has the given class. */
74
+ hasClass(name: string): Promise<boolean>;
75
+ /** Gets the dimensions of the element. */
76
+ getDimensions(): Promise<ElementDimensions>;
77
+ /** Gets the value of a property of an element. */
78
+ getProperty(name: string): Promise<any>;
79
+ /** Checks whether this element matches the given selector. */
80
+ matchesSelector(selector: string): Promise<boolean>;
81
+ /** Checks whether the element is focused. */
82
+ isFocused(): Promise<boolean>;
83
+ /** Sets the value of a property of an input. */
84
+ setInputValue(value: string): Promise<void>;
85
+ /** Selects the options at the specified indexes inside of a native `select` element. */
86
+ selectOptions(...optionIndexes: number[]): Promise<void>;
87
+ /** Dispatches an event with a particular name. */
88
+ dispatchEvent(name: string, data?: Record<string, EventData>): Promise<void>;
89
+ /** Performs a key-down action. */
90
+ keyDown(value: string): Promise<void>;
91
+ /** Performs a key-up action. */
92
+ keyUp(value: string): Promise<void>;
93
+ /** Dispatches all the events that are part of a click event sequence. */
94
+ private dispatchClickEventSequence;
95
+ /** Writes info to the console outputs. */
96
+ private logAction;
97
+ }
98
+ //#endregion
99
+ //#region src/WebdriverIOHarnessEnvironment.d.ts
100
+ /**
101
+ * A `HarnessEnvironment` implementation for WebdriverIO.
102
+ */
103
+ declare class WebdriverIOHarnessEnvironment extends HarnessEnvironment<WebdriverIO.Element> {
104
+ /**
105
+ * Keep a reference to the `document` element because `rawRootElement`
106
+ * will be the root element of the harness's environment.
107
+ */
108
+ private documentRoot;
109
+ protected constructor(rawRootElement: WebdriverIO.Element, options: {
110
+ documentRoot: WebdriverIO.Element;
111
+ });
112
+ /** Creates a `HarnessLoader` rooted at the document root. */
113
+ static loader(documentRoot: WebdriverIO.Element): Promise<WebdriverIOHarnessEnvironment>;
114
+ /**
115
+ * Flushes change detection and async tasks captured in the Angular zone.
116
+ * In most cases it should not be necessary to call this manually. However, there may be some edge
117
+ * cases where it is needed to fully flush animation events.
118
+ */
119
+ forceStabilize(): Promise<void>;
120
+ waitForTasksOutsideAngular(): Promise<void>;
121
+ /** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
122
+ override createComponentHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>, element: WebdriverIO.Element): T;
123
+ /** Gets a list of all elements matching the given selector under this environment's root element. */
124
+ protected getAllRawElements(selector: string): Promise<WebdriverIO.Element[]>;
125
+ /** Gets the root element for the document. */
126
+ protected getDocumentRoot(): WebdriverIO.Element;
127
+ /** Creates a `TestElement` from a raw element. */
128
+ protected createTestElement(element: WebdriverIO.Element): WebdriverIOTestElement;
129
+ /** Creates a `HarnessLoader` rooted at the given raw element. */
130
+ protected createEnvironment(element: WebdriverIO.Element): HarnessEnvironment<WebdriverIO.Element>;
131
+ }
132
+ //#endregion
133
+ //#region src/index.d.ts
134
+ /**
135
+ * Searches for all instances of the component corresponding to the given harness type under the
136
+ * `HarnessLoader`'s root element, and returns a list of `ComponentHarness` for each instance.
137
+ * @param query A query for a harness to create
138
+ * @return A list of instances of the given harness type
139
+ */
140
+ declare const getAllHarnesses: <T extends ComponentHarness>(query: HarnessQuery<T>) => Promise<T[]>;
141
+ /**
142
+ * Searches for an instance of the component corresponding to the given harness type or host element
143
+ * under the `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If
144
+ * multiple matching components are found, a harness for the first one is returned. If no matching
145
+ * component is found, an error is thrown.
146
+ * @param query A query for a harness to create
147
+ * @param element A raw host element for a harness to create
148
+ * @return An instance of the given harness type
149
+ * @throws If a matching component instance can't be found
150
+ */
151
+ declare const getHarness: <T extends ComponentHarness>(query: HarnessQuery<T>, element?: WebdriverIO.Element) => Promise<T>;
152
+ /**
153
+ * Returns a base harness environment instance.
154
+ * @return An HarnessLoader instance for the current HTML document, rooted at the document's root element
155
+ */
156
+ declare const createHarnessEnvironment: (rootElement?: WebdriverIO.Element) => Promise<WebdriverIOHarnessEnvironment>;
157
+ /**
158
+ * Returns a promise that resolves when the application is "stable".
159
+ * The promise might be rejected due to timeouts, or async tasks in the client
160
+ * app that never finish (timers, etc).
161
+ */
162
+ declare const waitForAngular: () => Promise<void>;
163
+ //#endregion
164
+ export { createHarnessEnvironment, getAllHarnesses, getHarness, waitForAngular };
package/index.mjs ADDED
@@ -0,0 +1,373 @@
1
+ import { $, browser } from "@wdio/globals";
2
+ import { ComponentHarness, HarnessEnvironment, TestKey } from "@angular/cdk/testing";
3
+ import logger from "@wdio/logger";
4
+ //#region src/utils.ts
5
+ const colorize = (msg, color) => process.stdout.isTTY ? `${color}${msg}\x1b[0m` : msg;
6
+ const magenta = (msg) => colorize(msg, "\x1B[35m");
7
+ const green = (msg) => colorize(msg, "\x1B[32m");
8
+ //#endregion
9
+ //#region src/WebdriverIOTestElement.ts
10
+ /** Registers the element logger. */
11
+ const log$1 = logger("wdio-harness");
12
+ /** Maps the `TestKey` constants to WebdriverIO's `Key` constants. */
13
+ const keyMap = {
14
+ [TestKey.BACKSPACE]: "Backspace",
15
+ [TestKey.TAB]: "Tab",
16
+ [TestKey.ENTER]: "Enter",
17
+ [TestKey.SHIFT]: "Shift",
18
+ [TestKey.CONTROL]: "Control",
19
+ [TestKey.ALT]: "Alt",
20
+ [TestKey.ESCAPE]: "Escape",
21
+ [TestKey.PAGE_UP]: "PageUp",
22
+ [TestKey.PAGE_DOWN]: "PageDown",
23
+ [TestKey.END]: "End",
24
+ [TestKey.HOME]: "Home",
25
+ [TestKey.LEFT_ARROW]: "ArrowLeft",
26
+ [TestKey.UP_ARROW]: "ArrowUp",
27
+ [TestKey.RIGHT_ARROW]: "ArrowRight",
28
+ [TestKey.DOWN_ARROW]: "ArrowDown",
29
+ [TestKey.INSERT]: "Insert",
30
+ [TestKey.DELETE]: "Delete",
31
+ [TestKey.F1]: "F1",
32
+ [TestKey.F2]: "F2",
33
+ [TestKey.F3]: "F3",
34
+ [TestKey.F4]: "F4",
35
+ [TestKey.F5]: "F5",
36
+ [TestKey.F6]: "F6",
37
+ [TestKey.F7]: "F7",
38
+ [TestKey.F8]: "F8",
39
+ [TestKey.F9]: "F9",
40
+ [TestKey.F10]: "F10",
41
+ [TestKey.F11]: "F11",
42
+ [TestKey.F12]: "F12",
43
+ [TestKey.META]: "Meta",
44
+ [TestKey.COMMA]: ","
45
+ };
46
+ ComponentHarness.prototype.element = function() {
47
+ return this.locatorFactory.rootElement.element();
48
+ };
49
+ /** Converts a `ModifierKeys` object to a list of WebdriverIO `Key`s. */
50
+ const toWebdriverIOModifierKeys = (modifiers) => {
51
+ const result = [];
52
+ if (modifiers.control) result.push(keyMap[TestKey.CONTROL]);
53
+ if (modifiers.alt) result.push(keyMap[TestKey.ALT]);
54
+ if (modifiers.shift) result.push(keyMap[TestKey.SHIFT]);
55
+ if (modifiers.meta) result.push(keyMap[TestKey.META]);
56
+ return result;
57
+ };
58
+ /**
59
+ * A `TestElement` implementation for WebdriverIO.
60
+ */
61
+ var WebdriverIOTestElement = class {
62
+ constructor(hostElement) {
63
+ this.hostElement = hostElement;
64
+ }
65
+ /** Return the host element. */
66
+ element() {
67
+ return this.hostElement;
68
+ }
69
+ /** Blur the element. */
70
+ async blur() {
71
+ this.logAction("BLUR");
72
+ return browser.executeScript("arguments[0].blur()", [this.hostElement]);
73
+ }
74
+ /** Clear the element's input (for input and textarea elements only). */
75
+ async clear() {
76
+ this.logAction("CLEAR");
77
+ return this.hostElement.clearValue();
78
+ }
79
+ async click(...args) {
80
+ this.logAction("CLICK");
81
+ return this.dispatchClickEventSequence(args, "left");
82
+ }
83
+ async rightClick(...args) {
84
+ this.logAction("RIGHT_CLICK");
85
+ return this.dispatchClickEventSequence(args, "right");
86
+ }
87
+ /** Focus the element. */
88
+ async focus() {
89
+ this.logAction("FOCUS");
90
+ return browser.executeScript("arguments[0].focus()", [this.hostElement]);
91
+ }
92
+ /** Get the computed value of the given CSS property for the element. */
93
+ async getCssValue(property) {
94
+ this.logAction("GET_CSS_VALUE");
95
+ return (await this.hostElement.getCSSProperty(property)).value ?? "";
96
+ }
97
+ /** Hovers the mouse over the element. */
98
+ async hover() {
99
+ this.logAction("HOVER");
100
+ return this.hostElement.moveTo();
101
+ }
102
+ /** Moves the mouse away from the element. */
103
+ async mouseAway() {
104
+ this.logAction("MOUSE_AWAY");
105
+ return this.hostElement.moveTo({
106
+ xOffset: -1,
107
+ yOffset: -1
108
+ });
109
+ }
110
+ async sendKeys(...modifiersAndKeys) {
111
+ let modifiers;
112
+ let rest;
113
+ const first = modifiersAndKeys[0];
114
+ if (first !== void 0 && typeof first !== "string" && typeof first !== "number") {
115
+ modifiers = first;
116
+ rest = modifiersAndKeys.slice(1);
117
+ } else {
118
+ modifiers = {};
119
+ rest = modifiersAndKeys;
120
+ }
121
+ const KeyNULL = String.fromCharCode(57344);
122
+ const modifierKeys = toWebdriverIOModifierKeys(modifiers);
123
+ const keys = rest.map((k) => typeof k === "string" ? k.split("") : [keyMap[k]]).reduce((arr, k) => arr.concat(k), []).reduce((arr, k) => {
124
+ if (modifierKeys.length > 0) return arr.concat(...modifierKeys, k, KeyNULL);
125
+ return arr.concat(k);
126
+ }, []);
127
+ this.logAction("SEND_KEYS", `[${keys.join(", ")}]`);
128
+ if (keys.length !== 0) {
129
+ await this.focus();
130
+ return browser.keys(keys);
131
+ }
132
+ }
133
+ /** Gets the text from the element. */
134
+ async text(options) {
135
+ this.logAction("TEXT", options?.exclude ? `{ exclude: ${options?.exclude} }` : void 0);
136
+ if (options?.exclude) return browser.executeScript(`
137
+ const clone = arguments[0].cloneNode(true);
138
+ const exclusions = clone.querySelectorAll(arguments[1]);
139
+ for (let i = 0; i < exclusions.length; i++) {
140
+ exclusions[i].remove();
141
+ }
142
+ return (clone.textContent ?? '').trim();
143
+ `, [this.hostElement, options.exclude]);
144
+ return browser.executeScript(`return (arguments[0].textContent ?? '').trim()`, [this.hostElement]);
145
+ }
146
+ /**
147
+ * Sets the value of a `contenteditable` element.
148
+ * @param value Value to be set on the element.
149
+ */
150
+ async setContenteditableValue(value) {
151
+ const contenteditableAttr = await this.getAttribute("contenteditable");
152
+ if (contenteditableAttr !== "" && contenteditableAttr !== "true" && contenteditableAttr !== "plaintext-only") throw new Error("setContenteditableValue can only be called on a `contenteditable` element.");
153
+ return browser.executeScript("arguments[0].textContent = arguments[1];", [this.hostElement, value]);
154
+ }
155
+ /** Gets the value for the given attribute from the element. */
156
+ async getAttribute(name) {
157
+ this.logAction("GET_ATTRIBUTE", name);
158
+ return this.hostElement.getAttribute(name);
159
+ }
160
+ /** Checks whether the element has the given class. */
161
+ async hasClass(name) {
162
+ this.logAction("HAS_CLASS", name);
163
+ const classes = await this.getAttribute("class") || "";
164
+ return new Set(classes.split(/\s+/).filter((c) => c)).has(name);
165
+ }
166
+ /** Gets the dimensions of the element. */
167
+ async getDimensions() {
168
+ this.logAction("GET_DIMENSIONS");
169
+ const { width, height } = await this.hostElement.getSize();
170
+ const { x: left, y: top } = await this.hostElement.getLocation();
171
+ return {
172
+ width,
173
+ height,
174
+ left,
175
+ top
176
+ };
177
+ }
178
+ /** Gets the value of a property of an element. */
179
+ async getProperty(name) {
180
+ this.logAction("GET_PROPERTY", name);
181
+ return this.hostElement.getProperty(name);
182
+ }
183
+ /** Checks whether this element matches the given selector. */
184
+ async matchesSelector(selector) {
185
+ this.logAction("MATCHES_SELECTOR", selector);
186
+ return browser.executeScript(`
187
+ return (Element.prototype.matches ?? Element.prototype.msMatchesSelector).call(arguments[0], arguments[1])
188
+ `, [this.hostElement, selector]);
189
+ }
190
+ /** Checks whether the element is focused. */
191
+ async isFocused() {
192
+ this.logAction("IS_FOCUSED");
193
+ return this.hostElement.isFocused();
194
+ }
195
+ /** Sets the value of a property of an input. */
196
+ async setInputValue(value) {
197
+ this.logAction("SET_INPUT_VALUE", value);
198
+ return this.hostElement.setValue(value);
199
+ }
200
+ /** Selects the options at the specified indexes inside of a native `select` element. */
201
+ async selectOptions(...optionIndexes) {
202
+ this.logAction("SELECT_OPTIONS", `[${optionIndexes.join(", ")}]`);
203
+ const options = await this.hostElement.$$("option").getElements();
204
+ const indexes = new Set(optionIndexes);
205
+ if (options.length && indexes.size) {
206
+ await this.setInputValue("");
207
+ for (let i = 0; i < options.length; i++) if (indexes.has(i)) {
208
+ await this.keyDown(keyMap[TestKey.CONTROL]);
209
+ await options[i].click();
210
+ await this.keyUp(keyMap[TestKey.CONTROL]);
211
+ }
212
+ }
213
+ }
214
+ /** Dispatches an event with a particular name. */
215
+ async dispatchEvent(name, data) {
216
+ this.logAction("DISPATCH_EVENT", name);
217
+ return browser.executeScript(`
218
+ const event = document.createEvent('Event');
219
+ event.initEvent(arguments[0]);
220
+ if (arguments[2]) {
221
+ Object.assign(event, arguments[2]);
222
+ }
223
+ arguments[1]['dispatchEvent'](event);
224
+ `, [
225
+ name,
226
+ this.hostElement,
227
+ data
228
+ ]);
229
+ }
230
+ /** Performs a key-down action. */
231
+ async keyDown(value) {
232
+ this.logAction("KEY_DOWN:", value);
233
+ return browser.performActions([{
234
+ id: "keyboard",
235
+ type: "key",
236
+ actions: [{
237
+ type: "keyDown",
238
+ value
239
+ }]
240
+ }]);
241
+ }
242
+ /** Performs a key-up action. */
243
+ async keyUp(value) {
244
+ this.logAction("KEY_UP:", value);
245
+ return browser.performActions([{
246
+ id: "keyboard",
247
+ type: "key",
248
+ actions: [{
249
+ type: "keyUp",
250
+ value
251
+ }]
252
+ }]);
253
+ }
254
+ /** Dispatches all the events that are part of a click event sequence. */
255
+ async dispatchClickEventSequence(args, button) {
256
+ let modifiers = {};
257
+ if (args.length && typeof args[args.length - 1] === "object") modifiers = args.pop();
258
+ const modifierKeys = toWebdriverIOModifierKeys(modifiers);
259
+ const offsetArgs = args.length === 2 ? {
260
+ xOffset: Number(args[0]),
261
+ yOffset: Number(args[1])
262
+ } : void 0;
263
+ await this.hostElement.moveTo(offsetArgs);
264
+ for (const modifierKey of modifierKeys) this.keyDown(modifierKey);
265
+ await this.hostElement.click({ button });
266
+ for (const modifierKey of modifierKeys) this.keyUp(modifierKey);
267
+ }
268
+ /** Writes info to the console outputs. */
269
+ logAction(action, args) {
270
+ log$1.info(`${magenta(action)} ${green(this.hostElement.selector.toString())} ${args ? args : ""}`);
271
+ }
272
+ };
273
+ //#endregion
274
+ //#region src/WebdriverIOHarnessEnvironment.ts
275
+ /** Registers the environment logger. */
276
+ const log = logger("wdio-harness");
277
+ /**
278
+ * A `HarnessEnvironment` implementation for WebdriverIO.
279
+ */
280
+ var WebdriverIOHarnessEnvironment = class WebdriverIOHarnessEnvironment extends HarnessEnvironment {
281
+ constructor(rawRootElement, options) {
282
+ super(rawRootElement);
283
+ this.documentRoot = options.documentRoot;
284
+ }
285
+ /** Creates a `HarnessLoader` rooted at the document root. */
286
+ static async loader(documentRoot) {
287
+ return new WebdriverIOHarnessEnvironment(documentRoot, { documentRoot });
288
+ }
289
+ /**
290
+ * Flushes change detection and async tasks captured in the Angular zone.
291
+ * In most cases it should not be necessary to call this manually. However, there may be some edge
292
+ * cases where it is needed to fully flush animation events.
293
+ */
294
+ async forceStabilize() {}
295
+ async waitForTasksOutsideAngular() {
296
+ throw new Error("Method not implemented.");
297
+ }
298
+ /** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
299
+ createComponentHarness(harnessType, element) {
300
+ return super.createComponentHarness(harnessType, element);
301
+ }
302
+ /** Gets a list of all elements matching the given selector under this environment's root element. */
303
+ async getAllRawElements(selector) {
304
+ log.info(`${magenta("GET_ALL_RAW_ELEMENTS")} ${green(selector.toString())}`);
305
+ return [...await this.rawRootElement.$$(selector).getElements()];
306
+ }
307
+ /** Gets the root element for the document. */
308
+ getDocumentRoot() {
309
+ return this.documentRoot;
310
+ }
311
+ /** Creates a `TestElement` from a raw element. */
312
+ createTestElement(element) {
313
+ return new WebdriverIOTestElement(element);
314
+ }
315
+ /** Creates a `HarnessLoader` rooted at the given raw element. */
316
+ createEnvironment(element) {
317
+ return new WebdriverIOHarnessEnvironment(element, { documentRoot: this.documentRoot });
318
+ }
319
+ };
320
+ //#endregion
321
+ //#region src/index.ts
322
+ /**
323
+ * Searches for all instances of the component corresponding to the given harness type under the
324
+ * `HarnessLoader`'s root element, and returns a list of `ComponentHarness` for each instance.
325
+ * @param query A query for a harness to create
326
+ * @return A list of instances of the given harness type
327
+ */
328
+ const getAllHarnesses = async (query) => {
329
+ return (await createHarnessEnvironment()).getAllHarnesses(query);
330
+ };
331
+ /**
332
+ * Searches for an instance of the component corresponding to the given harness type or host element
333
+ * under the `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If
334
+ * multiple matching components are found, a harness for the first one is returned. If no matching
335
+ * component is found, an error is thrown.
336
+ * @param query A query for a harness to create
337
+ * @param element A raw host element for a harness to create
338
+ * @return An instance of the given harness type
339
+ * @throws If a matching component instance can't be found
340
+ */
341
+ const getHarness = async (query, element) => {
342
+ const env = await createHarnessEnvironment(element);
343
+ if (element) return env.createComponentHarness(query, element);
344
+ return env.getHarness(query);
345
+ };
346
+ /**
347
+ * Returns a base harness environment instance.
348
+ * @return An HarnessLoader instance for the current HTML document, rooted at the document's root element
349
+ */
350
+ const createHarnessEnvironment = async (rootElement) => {
351
+ return WebdriverIOHarnessEnvironment.loader(rootElement ?? await $("//body").getElement());
352
+ };
353
+ /**
354
+ * Returns a promise that resolves when the application is "stable".
355
+ * The promise might be rejected due to timeouts, or async tasks in the client
356
+ * app that never finish (timers, etc).
357
+ */
358
+ const waitForAngular = async () => {
359
+ await browser.waitUntil(() => browser.executeScript(`
360
+ return Array.isArray(window.frameworkStabilizers)
361
+ `, []));
362
+ await browser.executeAsyncScript(`
363
+ const done = arguments[0];
364
+ const promises = window.frameworkStabilizers.map(stabilizer => {
365
+ // Stabilizer will invoke the resolve function with a
366
+ // boolean to indicate whether any work is done.
367
+ return new Promise(resolve => stabilizer(resolve));
368
+ });
369
+ Promise.all(promises).then(done);
370
+ `, []);
371
+ };
372
+ //#endregion
373
+ export { createHarnessEnvironment, getAllHarnesses, getHarness, waitForAngular };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badisi/wdio-harness",
3
- "version": "4.3.4",
3
+ "version": "4.4.0",
4
4
  "description": "WebdriverIO support for Angular component test harnesses.",
5
5
  "homepage": "https://github.com/Badisi/wdio-harness",
6
6
  "license": "MIT",
@@ -8,19 +8,21 @@
8
8
  "name": "Badisi"
9
9
  },
10
10
  "type": "module",
11
- "module": "index.js",
12
- "typings": "index.d.ts",
11
+ "main": "./index.mjs",
12
+ "module": "./index.mjs",
13
+ "types": "./index.d.mts",
13
14
  "exports": {
14
15
  ".": {
15
- "import": "./index.js",
16
- "types": "./index.d.ts",
17
- "default": "./index.js"
16
+ "import": {
17
+ "types": "./index.d.mts",
18
+ "default": "./index.mjs"
19
+ }
18
20
  },
19
21
  "./package.json": "./package.json"
20
22
  },
21
23
  "repository": {
22
24
  "type": "git",
23
- "url": "https://github.com/Badisi/wdio-harness"
25
+ "url": "git+https://github.com/Badisi/wdio-harness.git"
24
26
  },
25
27
  "keywords": [
26
28
  "angular",
@@ -40,6 +42,7 @@
40
42
  },
41
43
  "peerDependencies": {
42
44
  "@angular/cdk": ">= 13 < 22",
45
+ "@wdio/globals": ">= 9 < 10",
43
46
  "@wdio/logger": ">= 9 < 10",
44
47
  "webdriverio": ">= 9 < 10"
45
48
  }
@@ -1,34 +0,0 @@
1
- import { type ComponentHarness, type ComponentHarnessConstructor, HarnessEnvironment } from '@angular/cdk/testing';
2
- import { WebdriverIOTestElement } from './WebdriverIOTestElement.js';
3
- /**
4
- * A `HarnessEnvironment` implementation for WebdriverIO.
5
- */
6
- export declare class WebdriverIOHarnessEnvironment extends HarnessEnvironment<WebdriverIO.Element> {
7
- /**
8
- * Keep a reference to the `document` element because `rawRootElement`
9
- * will be the root element of the harness's environment.
10
- */
11
- private documentRoot;
12
- protected constructor(rawRootElement: WebdriverIO.Element, options: {
13
- documentRoot: WebdriverIO.Element;
14
- });
15
- /** Creates a `HarnessLoader` rooted at the document root. */
16
- static loader(documentRoot: WebdriverIO.Element): Promise<WebdriverIOHarnessEnvironment>;
17
- /**
18
- * Flushes change detection and async tasks captured in the Angular zone.
19
- * In most cases it should not be necessary to call this manually. However, there may be some edge
20
- * cases where it is needed to fully flush animation events.
21
- */
22
- forceStabilize(): Promise<void>;
23
- waitForTasksOutsideAngular(): Promise<void>;
24
- /** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
25
- createComponentHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>, element: WebdriverIO.Element): T;
26
- /** Gets a list of all elements matching the given selector under this environment's root element. */
27
- protected getAllRawElements(selector: string): Promise<WebdriverIO.Element[]>;
28
- /** Gets the root element for the document. */
29
- protected getDocumentRoot(): WebdriverIO.Element;
30
- /** Creates a `TestElement` from a raw element. */
31
- protected createTestElement(element: WebdriverIO.Element): WebdriverIOTestElement;
32
- /** Creates a `HarnessLoader` rooted at the given raw element. */
33
- protected createEnvironment(element: WebdriverIO.Element): HarnessEnvironment<WebdriverIO.Element>;
34
- }
@@ -1,61 +0,0 @@
1
- import { HarnessEnvironment } from '@angular/cdk/testing';
2
- import logger from '@wdio/logger';
3
- import { WebdriverIOTestElement } from './WebdriverIOTestElement.js';
4
- import { green, magenta } from './utils';
5
- /** Registers the environment logger. */
6
- const log = logger('wdio-harness');
7
- /**
8
- * A `HarnessEnvironment` implementation for WebdriverIO.
9
- */
10
- export class WebdriverIOHarnessEnvironment extends HarnessEnvironment {
11
- /**
12
- * Keep a reference to the `document` element because `rawRootElement`
13
- * will be the root element of the harness's environment.
14
- */
15
- documentRoot;
16
- constructor(rawRootElement, options) {
17
- super(rawRootElement);
18
- this.documentRoot = options.documentRoot;
19
- }
20
- /** Creates a `HarnessLoader` rooted at the document root. */
21
- static async loader(documentRoot) {
22
- return new WebdriverIOHarnessEnvironment(documentRoot, { documentRoot });
23
- }
24
- /**
25
- * Flushes change detection and async tasks captured in the Angular zone.
26
- * In most cases it should not be necessary to call this manually. However, there may be some edge
27
- * cases where it is needed to fully flush animation events.
28
- */
29
- async forceStabilize() {
30
- /* await browser.executeAsyncScript(`
31
- const done = arguments[0];
32
- window.requestAnimationFrame(done);
33
- `, []);*/
34
- }
35
- async waitForTasksOutsideAngular() {
36
- throw new Error('Method not implemented.');
37
- }
38
- /** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
39
- createComponentHarness(harnessType, element) {
40
- return super.createComponentHarness(harnessType, element);
41
- }
42
- /** Gets a list of all elements matching the given selector under this environment's root element. */
43
- async getAllRawElements(selector) {
44
- log.info(`${magenta('GET_ALL_RAW_ELEMENTS')} ${green(selector.toString())}`);
45
- return [...(await this.rawRootElement.$$(selector).getElements())];
46
- }
47
- /** Gets the root element for the document. */
48
- getDocumentRoot() {
49
- return this.documentRoot;
50
- }
51
- /** Creates a `TestElement` from a raw element. */
52
- createTestElement(element) {
53
- return new WebdriverIOTestElement(element);
54
- }
55
- /** Creates a `HarnessLoader` rooted at the given raw element. */
56
- createEnvironment(element) {
57
- return new WebdriverIOHarnessEnvironment(element, {
58
- documentRoot: this.documentRoot
59
- });
60
- }
61
- }
@@ -1,96 +0,0 @@
1
- import { type ElementDimensions, type EventData, type ModifierKeys, type TestElement, TestKey, type TextOptions } from '@angular/cdk/testing';
2
- /** Module augmentation to expose the host element as a public api */
3
- declare module '@angular/cdk/testing' {
4
- interface ComponentHarness {
5
- element(): WebdriverIO.Element;
6
- }
7
- interface TestElement {
8
- element(): WebdriverIO.Element;
9
- }
10
- }
11
- /**
12
- * A `TestElement` implementation for WebdriverIO.
13
- */
14
- export declare class WebdriverIOTestElement implements TestElement {
15
- readonly hostElement: WebdriverIO.Element;
16
- constructor(hostElement: WebdriverIO.Element);
17
- /** Return the host element. */
18
- element(): WebdriverIO.Element;
19
- /** Blur the element. */
20
- blur(): Promise<void>;
21
- /** Clear the element's input (for input and textarea elements only). */
22
- clear(): Promise<void>;
23
- /**
24
- * Click the element at the default location for the current environment. If you need to guarantee
25
- * the element is clicked at a specific location, consider using `click('center')` or
26
- * `click(x, y)` instead.
27
- */
28
- click(modifiers?: ModifierKeys): Promise<void>;
29
- /** Click the element at the element's center. */
30
- click(location: 'center', modifiers?: ModifierKeys): Promise<void>;
31
- /**
32
- * Click the element at the specified coordinates relative to the top-left of the element.
33
- * @param relativeX Coordinate within the element, along the X-axis at which to click.
34
- * @param relativeY Coordinate within the element, along the Y-axis at which to click.
35
- * @param modifiers Modifier keys held while clicking
36
- */
37
- click(relativeX: number, relativeY: number, modifiers?: ModifierKeys): Promise<void>;
38
- /**
39
- * Right clicks on the element at the specified coordinates relative to the top-left of it.
40
- * @param relativeX Coordinate within the element, along the X-axis at which to click.
41
- * @param relativeY Coordinate within the element, along the Y-axis at which to click.
42
- * @param modifiers Modifier keys held while clicking
43
- */
44
- rightClick(relativeX: number, relativeY: number, modifiers?: ModifierKeys): Promise<void>;
45
- /** Focus the element. */
46
- focus(): Promise<void>;
47
- /** Get the computed value of the given CSS property for the element. */
48
- getCssValue(property: string): Promise<string>;
49
- /** Hovers the mouse over the element. */
50
- hover(): Promise<void>;
51
- /** Moves the mouse away from the element. */
52
- mouseAway(): Promise<void>;
53
- /**
54
- * Sends the given string to the input as a series of key presses. Also fires input events
55
- * and attempts to add the string to the Element's value.
56
- */
57
- sendKeys(...keys: (string | TestKey)[]): Promise<void>;
58
- /**
59
- * Sends the given string to the input as a series of key presses. Also fires input events
60
- * and attempts to add the string to the Element's value.
61
- */
62
- sendKeys(modifiers: ModifierKeys, ...keys: (string | TestKey)[]): Promise<void>;
63
- /** Gets the text from the element. */
64
- text(options?: TextOptions): Promise<string>;
65
- /**
66
- * Sets the value of a `contenteditable` element.
67
- * @param value Value to be set on the element.
68
- */
69
- setContenteditableValue(value: string): Promise<void>;
70
- /** Gets the value for the given attribute from the element. */
71
- getAttribute(name: string): Promise<string | null>;
72
- /** Checks whether the element has the given class. */
73
- hasClass(name: string): Promise<boolean>;
74
- /** Gets the dimensions of the element. */
75
- getDimensions(): Promise<ElementDimensions>;
76
- /** Gets the value of a property of an element. */
77
- getProperty(name: string): Promise<any>;
78
- /** Checks whether this element matches the given selector. */
79
- matchesSelector(selector: string): Promise<boolean>;
80
- /** Checks whether the element is focused. */
81
- isFocused(): Promise<boolean>;
82
- /** Sets the value of a property of an input. */
83
- setInputValue(value: string): Promise<void>;
84
- /** Selects the options at the specified indexes inside of a native `select` element. */
85
- selectOptions(...optionIndexes: number[]): Promise<void>;
86
- /** Dispatches an event with a particular name. */
87
- dispatchEvent(name: string, data?: Record<string, EventData>): Promise<void>;
88
- /** Performs a key-down action. */
89
- keyDown(value: string): Promise<void>;
90
- /** Performs a key-up action. */
91
- keyUp(value: string): Promise<void>;
92
- /** Dispatches all the events that are part of a click event sequence. */
93
- private dispatchClickEventSequence;
94
- /** Writes info to the console outputs. */
95
- private logAction;
96
- }
@@ -1,290 +0,0 @@
1
- import { ComponentHarness, TestKey } from '@angular/cdk/testing';
2
- import { browser } from '@wdio/globals';
3
- import logger from '@wdio/logger';
4
- import { green, magenta } from './utils';
5
- var Button;
6
- (function (Button) {
7
- Button["LEFT"] = "left";
8
- Button["MIDDLE"] = "middle";
9
- Button["RIGHT"] = "right";
10
- })(Button || (Button = {}));
11
- /** Registers the element logger. */
12
- const log = logger('wdio-harness');
13
- /** Maps the `TestKey` constants to WebdriverIO's `Key` constants. */
14
- const keyMap = {
15
- [TestKey.BACKSPACE]: 'Backspace',
16
- [TestKey.TAB]: 'Tab',
17
- [TestKey.ENTER]: 'Enter',
18
- [TestKey.SHIFT]: 'Shift',
19
- [TestKey.CONTROL]: 'Control',
20
- [TestKey.ALT]: 'Alt',
21
- [TestKey.ESCAPE]: 'Escape',
22
- [TestKey.PAGE_UP]: 'PageUp',
23
- [TestKey.PAGE_DOWN]: 'PageDown',
24
- [TestKey.END]: 'End',
25
- [TestKey.HOME]: 'Home',
26
- [TestKey.LEFT_ARROW]: 'ArrowLeft',
27
- [TestKey.UP_ARROW]: 'ArrowUp',
28
- [TestKey.RIGHT_ARROW]: 'ArrowRight',
29
- [TestKey.DOWN_ARROW]: 'ArrowDown',
30
- [TestKey.INSERT]: 'Insert',
31
- [TestKey.DELETE]: 'Delete',
32
- [TestKey.F1]: 'F1',
33
- [TestKey.F2]: 'F2',
34
- [TestKey.F3]: 'F3',
35
- [TestKey.F4]: 'F4',
36
- [TestKey.F5]: 'F5',
37
- [TestKey.F6]: 'F6',
38
- [TestKey.F7]: 'F7',
39
- [TestKey.F8]: 'F8',
40
- [TestKey.F9]: 'F9',
41
- [TestKey.F10]: 'F10',
42
- [TestKey.F11]: 'F11',
43
- [TestKey.F12]: 'F12',
44
- [TestKey.META]: 'Meta',
45
- [TestKey.COMMA]: ','
46
- };
47
- ComponentHarness.prototype.element = function () {
48
- return this.locatorFactory.rootElement.element();
49
- };
50
- /** Converts a `ModifierKeys` object to a list of WebdriverIO `Key`s. */
51
- const toWebdriverIOModifierKeys = (modifiers) => {
52
- const result = [];
53
- if (modifiers.control) {
54
- result.push(keyMap[TestKey.CONTROL]);
55
- }
56
- if (modifiers.alt) {
57
- result.push(keyMap[TestKey.ALT]);
58
- }
59
- if (modifiers.shift) {
60
- result.push(keyMap[TestKey.SHIFT]);
61
- }
62
- if (modifiers.meta) {
63
- result.push(keyMap[TestKey.META]);
64
- }
65
- return result;
66
- };
67
- /**
68
- * A `TestElement` implementation for WebdriverIO.
69
- */
70
- export class WebdriverIOTestElement {
71
- hostElement;
72
- constructor(hostElement) {
73
- this.hostElement = hostElement;
74
- }
75
- /** Return the host element. */
76
- element() {
77
- return this.hostElement;
78
- }
79
- /** Blur the element. */
80
- async blur() {
81
- this.logAction('BLUR');
82
- return browser.executeScript('arguments[0].blur()', [this.hostElement]);
83
- }
84
- /** Clear the element's input (for input and textarea elements only). */
85
- async clear() {
86
- this.logAction('CLEAR');
87
- return this.hostElement.clearValue();
88
- }
89
- async click(...args) {
90
- this.logAction('CLICK');
91
- return this.dispatchClickEventSequence(args, Button.LEFT);
92
- }
93
- async rightClick(...args) {
94
- this.logAction('RIGHT_CLICK');
95
- return this.dispatchClickEventSequence(args, Button.RIGHT);
96
- }
97
- /** Focus the element. */
98
- async focus() {
99
- this.logAction('FOCUS');
100
- return browser.executeScript('arguments[0].focus()', [this.hostElement]);
101
- }
102
- /** Get the computed value of the given CSS property for the element. */
103
- async getCssValue(property) {
104
- this.logAction('GET_CSS_VALUE');
105
- return (await this.hostElement.getCSSProperty(property)).value ?? '';
106
- }
107
- /** Hovers the mouse over the element. */
108
- async hover() {
109
- this.logAction('HOVER');
110
- return this.hostElement.moveTo();
111
- }
112
- /** Moves the mouse away from the element. */
113
- async mouseAway() {
114
- this.logAction('MOUSE_AWAY');
115
- return this.hostElement.moveTo({ xOffset: -1, yOffset: -1 });
116
- }
117
- async sendKeys(...modifiersAndKeys) {
118
- let modifiers;
119
- let rest;
120
- const first = modifiersAndKeys[0];
121
- if (first !== undefined && typeof first !== 'string' && typeof first !== 'number') {
122
- modifiers = first;
123
- rest = modifiersAndKeys.slice(1);
124
- }
125
- else {
126
- modifiers = {};
127
- rest = modifiersAndKeys;
128
- }
129
- const KeyNULL = String.fromCharCode(57344);
130
- const modifierKeys = toWebdriverIOModifierKeys(modifiers);
131
- const keys = rest
132
- .map(k => (typeof k === 'string' ? k.split('') : [keyMap[k]]))
133
- .reduce((arr, k) => arr.concat(k), [])
134
- .reduce((arr, k) => {
135
- if (modifierKeys.length > 0) {
136
- return arr.concat(...modifierKeys, k, KeyNULL);
137
- }
138
- return arr.concat(k);
139
- }, []);
140
- this.logAction('SEND_KEYS', `[${keys.join(', ')}]`);
141
- if (keys.length !== 0) {
142
- await this.focus();
143
- return browser.keys(keys);
144
- }
145
- }
146
- /** Gets the text from the element. */
147
- async text(options) {
148
- this.logAction('TEXT', options?.exclude ? `{ exclude: ${options?.exclude} }` : undefined);
149
- if (options?.exclude) {
150
- return browser.executeScript(`
151
- const clone = arguments[0].cloneNode(true);
152
- const exclusions = clone.querySelectorAll(arguments[1]);
153
- for (let i = 0; i < exclusions.length; i++) {
154
- exclusions[i].remove();
155
- }
156
- return (clone.textContent ?? '').trim();
157
- `, [this.hostElement, options.exclude]);
158
- }
159
- // We don't go through WebdriverIO's `getText`, because it excludes text from hidden elements.
160
- return browser.executeScript(`return (arguments[0].textContent ?? '').trim()`, [this.hostElement]);
161
- }
162
- /**
163
- * Sets the value of a `contenteditable` element.
164
- * @param value Value to be set on the element.
165
- */
166
- async setContenteditableValue(value) {
167
- const contenteditableAttr = await this.getAttribute('contenteditable');
168
- if (contenteditableAttr !== '' &&
169
- contenteditableAttr !== 'true' &&
170
- contenteditableAttr !== 'plaintext-only') {
171
- throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
172
- }
173
- return browser.executeScript('arguments[0].textContent = arguments[1];', [this.hostElement, value]);
174
- }
175
- /** Gets the value for the given attribute from the element. */
176
- async getAttribute(name) {
177
- this.logAction('GET_ATTRIBUTE', name);
178
- return this.hostElement.getAttribute(name);
179
- }
180
- /** Checks whether the element has the given class. */
181
- async hasClass(name) {
182
- this.logAction('HAS_CLASS', name);
183
- const classes = (await this.getAttribute('class')) || '';
184
- return new Set(classes.split(/\s+/).filter(c => c)).has(name);
185
- }
186
- /** Gets the dimensions of the element. */
187
- async getDimensions() {
188
- this.logAction('GET_DIMENSIONS');
189
- const { width, height } = await this.hostElement.getSize();
190
- const { x: left, y: top } = await this.hostElement.getLocation();
191
- return { width, height, left, top };
192
- }
193
- /** Gets the value of a property of an element. */
194
- async getProperty(name) {
195
- this.logAction('GET_PROPERTY', name);
196
- return this.hostElement.getProperty(name);
197
- }
198
- /** Checks whether this element matches the given selector. */
199
- async matchesSelector(selector) {
200
- this.logAction('MATCHES_SELECTOR', selector);
201
- return browser.executeScript(`
202
- return (Element.prototype.matches ?? Element.prototype.msMatchesSelector).call(arguments[0], arguments[1])
203
- `, [this.hostElement, selector]);
204
- }
205
- /** Checks whether the element is focused. */
206
- async isFocused() {
207
- this.logAction('IS_FOCUSED');
208
- return this.hostElement.isFocused();
209
- }
210
- /** Sets the value of a property of an input. */
211
- async setInputValue(value) {
212
- this.logAction('SET_INPUT_VALUE', value);
213
- return this.hostElement.setValue(value);
214
- }
215
- /** Selects the options at the specified indexes inside of a native `select` element. */
216
- async selectOptions(...optionIndexes) {
217
- this.logAction('SELECT_OPTIONS', `[${optionIndexes.join(', ')}]`);
218
- const options = await this.hostElement.$$('option').getElements();
219
- const indexes = new Set(optionIndexes); // Convert to a set to remove duplicates.
220
- if (options.length && indexes.size) {
221
- // Reset the value so all the selected states are cleared. We can
222
- // reuse the input-specific method since the logic is the same.
223
- await this.setInputValue('');
224
- for (let i = 0; i < options.length; i++) {
225
- if (indexes.has(i)) {
226
- // We have to hold the control key while clicking on options so that multiple can be
227
- // selected in multi-selection mode. The key doesn't do anything for single selection.
228
- await this.keyDown(keyMap[TestKey.CONTROL]);
229
- await options[i].click();
230
- await this.keyUp(keyMap[TestKey.CONTROL]);
231
- }
232
- }
233
- }
234
- }
235
- /** Dispatches an event with a particular name. */
236
- async dispatchEvent(name, data) {
237
- this.logAction('DISPATCH_EVENT', name);
238
- return browser.executeScript(`
239
- const event = document.createEvent('Event');
240
- event.initEvent(arguments[0]);
241
- if (arguments[2]) {
242
- Object.assign(event, arguments[2]);
243
- }
244
- arguments[1]['dispatchEvent'](event);
245
- `, [name, this.hostElement, data]);
246
- }
247
- /** Performs a key-down action. */
248
- async keyDown(value) {
249
- this.logAction('KEY_DOWN:', value);
250
- return browser.performActions([{
251
- id: 'keyboard',
252
- type: 'key',
253
- actions: [{ type: 'keyDown', value }],
254
- }]);
255
- }
256
- /** Performs a key-up action. */
257
- async keyUp(value) {
258
- this.logAction('KEY_UP:', value);
259
- return browser.performActions([{
260
- id: 'keyboard',
261
- type: 'key',
262
- actions: [{ type: 'keyUp', value }],
263
- }]);
264
- }
265
- // --- HELPER(s) ---
266
- /** Dispatches all the events that are part of a click event sequence. */
267
- async dispatchClickEventSequence(args, button) {
268
- let modifiers = {};
269
- if (args.length && typeof args[args.length - 1] === 'object') {
270
- modifiers = args.pop();
271
- }
272
- const modifierKeys = toWebdriverIOModifierKeys(modifiers);
273
- // Omitting the offset argument to mouseMove results in clicking the center.
274
- // This is the default behavior we want, so we use an empty array of offsetArgs if
275
- // no args remain after popping the modifiers from the args passed to this function.
276
- const offsetArgs = (args.length === 2 ? { xOffset: Number(args[0]), yOffset: Number(args[1]) } : undefined);
277
- await this.hostElement.moveTo(offsetArgs);
278
- for (const modifierKey of modifierKeys) {
279
- this.keyDown(modifierKey);
280
- }
281
- await this.hostElement.click({ button });
282
- for (const modifierKey of modifierKeys) {
283
- this.keyUp(modifierKey);
284
- }
285
- }
286
- /** Writes info to the console outputs. */
287
- logAction(action, args) {
288
- log.info(`${magenta(action)} ${green(this.hostElement.selector.toString())} ${args ? args : ''}`);
289
- }
290
- }
package/index.d.ts DELETED
@@ -1,31 +0,0 @@
1
- import type { ComponentHarness, HarnessQuery } from '@angular/cdk/testing';
2
- import { WebdriverIOHarnessEnvironment } from './WebdriverIOHarnessEnvironment.js';
3
- /**
4
- * Searches for all instances of the component corresponding to the given harness type under the
5
- * `HarnessLoader`'s root element, and returns a list of `ComponentHarness` for each instance.
6
- * @param query A query for a harness to create
7
- * @return A list of instances of the given harness type
8
- */
9
- export declare const getAllHarnesses: <T extends ComponentHarness>(query: HarnessQuery<T>) => Promise<T[]>;
10
- /**
11
- * Searches for an instance of the component corresponding to the given harness type or host element
12
- * under the `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If
13
- * multiple matching components are found, a harness for the first one is returned. If no matching
14
- * component is found, an error is thrown.
15
- * @param query A query for a harness to create
16
- * @param element A raw host element for a harness to create
17
- * @return An instance of the given harness type
18
- * @throws If a matching component instance can't be found
19
- */
20
- export declare const getHarness: <T extends ComponentHarness>(query: HarnessQuery<T>, element?: WebdriverIO.Element) => Promise<T>;
21
- /**
22
- * Returns a base harness environment instance.
23
- * @return An HarnessLoader instance for the current HTML document, rooted at the document's root element
24
- */
25
- export declare const createHarnessEnvironment: (rootElement?: WebdriverIO.Element) => Promise<WebdriverIOHarnessEnvironment>;
26
- /**
27
- * Returns a promise that resolves when the application is "stable".
28
- * The promise might be rejected due to timeouts, or async tasks in the client
29
- * app that never finish (timers, etc).
30
- */
31
- export declare const waitForAngular: () => Promise<void>;
package/index.js DELETED
@@ -1,54 +0,0 @@
1
- import { $, browser } from '@wdio/globals';
2
- import { WebdriverIOHarnessEnvironment } from './WebdriverIOHarnessEnvironment.js';
3
- /**
4
- * Searches for all instances of the component corresponding to the given harness type under the
5
- * `HarnessLoader`'s root element, and returns a list of `ComponentHarness` for each instance.
6
- * @param query A query for a harness to create
7
- * @return A list of instances of the given harness type
8
- */
9
- export const getAllHarnesses = async (query) => {
10
- return (await createHarnessEnvironment()).getAllHarnesses(query);
11
- };
12
- /**
13
- * Searches for an instance of the component corresponding to the given harness type or host element
14
- * under the `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If
15
- * multiple matching components are found, a harness for the first one is returned. If no matching
16
- * component is found, an error is thrown.
17
- * @param query A query for a harness to create
18
- * @param element A raw host element for a harness to create
19
- * @return An instance of the given harness type
20
- * @throws If a matching component instance can't be found
21
- */
22
- export const getHarness = async (query, element) => {
23
- const env = await createHarnessEnvironment(element);
24
- if (element) {
25
- return env.createComponentHarness(query, element);
26
- }
27
- return env.getHarness(query);
28
- };
29
- /**
30
- * Returns a base harness environment instance.
31
- * @return An HarnessLoader instance for the current HTML document, rooted at the document's root element
32
- */
33
- export const createHarnessEnvironment = async (rootElement) => {
34
- return WebdriverIOHarnessEnvironment.loader(rootElement ?? await $('//body').getElement());
35
- };
36
- /**
37
- * Returns a promise that resolves when the application is "stable".
38
- * The promise might be rejected due to timeouts, or async tasks in the client
39
- * app that never finish (timers, etc).
40
- */
41
- export const waitForAngular = async () => {
42
- await browser.waitUntil(() => browser.executeScript(`
43
- return Array.isArray(window.frameworkStabilizers)
44
- `, []));
45
- await browser.executeAsyncScript(`
46
- const done = arguments[0];
47
- const promises = window.frameworkStabilizers.map(stabilizer => {
48
- // Stabilizer will invoke the resolve function with a
49
- // boolean to indicate whether any work is done.
50
- return new Promise(resolve => stabilizer(resolve));
51
- });
52
- Promise.all(promises).then(done);
53
- `, []);
54
- };
package/utils.d.ts DELETED
@@ -1,3 +0,0 @@
1
- declare const magenta: (msg: string) => string;
2
- declare const green: (msg: string) => string;
3
- export { magenta, green };
package/utils.js DELETED
@@ -1,4 +0,0 @@
1
- const colorize = (msg, color) => process.stdout.isTTY ? `${color}${msg}\x1b[0m` : msg;
2
- const magenta = (msg) => colorize(msg, '\x1b[35m');
3
- const green = (msg) => colorize(msg, '\x1b[32m');
4
- export { magenta, green };