@badisi/wdio-harness 4.3.4 → 4.4.1

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