@testspectra/matchers 1.0.70 → 1.1.0-rc.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/LICENSE.md +48 -0
- package/dist/__tests__/intercept.test.d.ts +1 -0
- package/dist/__tests__/intercept.test.js +183 -0
- package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
- package/dist/__tests__/matcher-contracts.test.js +498 -0
- package/dist/__tests__/matchers.test.d.ts +1 -0
- package/dist/__tests__/matchers.test.js +161 -0
- package/dist/contract.d.ts +141 -0
- package/dist/contract.js +10 -0
- package/dist/index.d.ts +4 -21
- package/dist/index.js +4 -21
- package/dist/intercept/cdp-handler.d.ts +22 -0
- package/dist/intercept/cdp-handler.js +123 -0
- package/dist/intercept/index.d.ts +4 -0
- package/dist/intercept/index.js +4 -0
- package/dist/intercept/mock-handle.d.ts +49 -0
- package/dist/intercept/mock-handle.js +124 -0
- package/dist/intercept/mock-registry.d.ts +18 -0
- package/dist/intercept/mock-registry.js +97 -0
- package/dist/intercept/types.d.ts +43 -0
- package/dist/intercept/types.js +1 -0
- package/dist/matchers.d.ts +15 -4
- package/dist/matchers.js +609 -81
- package/dist/proto.d.ts +18 -283
- package/dist/proto.js +1 -114
- package/dist/reporter.d.ts +30 -0
- package/dist/reporter.js +97 -0
- package/dist/runner/collection.d.ts +33 -20
- package/dist/runner/collection.js +104 -26
- package/dist/runner/single.d.ts +125 -34
- package/dist/runner/single.js +290 -55
- package/dist/semantic.d.ts +11 -0
- package/dist/semantic.js +141 -0
- package/dist/spectra.d.ts +32 -8
- package/dist/spectra.js +157 -40
- package/dist/types.d.ts +1226 -28
- package/package.json +13 -8
- package/src/contract.ts +182 -0
- package/src/index.ts +4 -22
- package/src/proto.ts +22 -433
- package/src/runtime/assertions.ts +450 -0
- package/src/runtime/element_actions.ts +169 -0
- package/src/runtime/element_proxy.ts +159 -0
- package/src/runtime/element_state.ts +91 -0
- package/src/runtime/spectra.ts +109 -0
- package/src/types.ts +1424 -96
- package/tsconfig.json +2 -2
- package/src/matchers.ts +0 -146
- package/src/runner/collection.ts +0 -153
- package/src/runner/single.ts +0 -301
- package/src/spectra.ts +0 -376
package/tsconfig.json
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"strict": true,
|
|
10
10
|
"esModuleInterop": true,
|
|
11
11
|
"skipLibCheck": true,
|
|
12
|
-
"types": ["node"
|
|
12
|
+
"types": ["node"],
|
|
13
13
|
"forceConsistentCasingInFileNames": true
|
|
14
14
|
},
|
|
15
15
|
"include": ["src/**/*"],
|
|
16
|
-
"exclude": ["node_modules", "dist"]
|
|
16
|
+
"exclude": ["node_modules", "dist", "src/**/__tests__/**", "src/runtime/**"]
|
|
17
17
|
}
|
package/src/matchers.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { SingleElementMatcher, MultiElementMatcher, ElementTarget } from "./types.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Resolves an ElementTarget (selector string, WebdriverIO.Element, or ChainablePromiseElement)
|
|
5
|
-
* to an actionable WebdriverIO.Element.
|
|
6
|
-
*
|
|
7
|
-
* @param target - Selector string or element instance.
|
|
8
|
-
* @returns Resolved WebdriverIO.Element instance.
|
|
9
|
-
*/
|
|
10
|
-
export async function resolveElement(target: ElementTarget): Promise<WebdriverIO.Element> {
|
|
11
|
-
if (typeof target === "string") {
|
|
12
|
-
const el = await $(target);
|
|
13
|
-
return el as unknown as WebdriverIO.Element;
|
|
14
|
-
}
|
|
15
|
-
const el = await (target as any);
|
|
16
|
-
return el as unknown as WebdriverIO.Element;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Executes a single element matcher against the WebdriverIO browser / element context.
|
|
21
|
-
*
|
|
22
|
-
* @param target - Optional element target (not required for browser-level assertions).
|
|
23
|
-
* @param matcher - Single element matcher string.
|
|
24
|
-
* @param args - Arguments passed to the matcher.
|
|
25
|
-
*/
|
|
26
|
-
export async function executeSingleMatcher(
|
|
27
|
-
target: ElementTarget | undefined,
|
|
28
|
-
matcher: SingleElementMatcher,
|
|
29
|
-
args: any[]
|
|
30
|
-
): Promise<void> {
|
|
31
|
-
const isBrowserLevel =
|
|
32
|
-
matcher === "have.url" ||
|
|
33
|
-
matcher === "contain.url" ||
|
|
34
|
-
matcher === "have.title" ||
|
|
35
|
-
matcher === "contain.title";
|
|
36
|
-
|
|
37
|
-
if (isBrowserLevel) {
|
|
38
|
-
const expected = args[0];
|
|
39
|
-
switch (matcher) {
|
|
40
|
-
case "have.url":
|
|
41
|
-
await expect(browser).toHaveUrl(expected);
|
|
42
|
-
break;
|
|
43
|
-
case "contain.url":
|
|
44
|
-
await expect(browser).toHaveUrl(expect.stringContaining(expected));
|
|
45
|
-
break;
|
|
46
|
-
case "have.title":
|
|
47
|
-
await expect(browser).toHaveTitle(expected);
|
|
48
|
-
break;
|
|
49
|
-
case "contain.title":
|
|
50
|
-
await expect(browser).toHaveTitle(expect.stringContaining(expected));
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!target) {
|
|
57
|
-
throw new Error(`Assertion matcher '${matcher}' requires an element target.`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const el = await resolveElement(target);
|
|
61
|
-
|
|
62
|
-
switch (matcher) {
|
|
63
|
-
case "be.visible":
|
|
64
|
-
await expect(el).toBeDisplayed();
|
|
65
|
-
break;
|
|
66
|
-
case "not.be.visible":
|
|
67
|
-
await expect(el).not.toBeDisplayed();
|
|
68
|
-
break;
|
|
69
|
-
case "exist":
|
|
70
|
-
await expect(el).toExist();
|
|
71
|
-
break;
|
|
72
|
-
case "not.exist":
|
|
73
|
-
await expect(el).not.toExist();
|
|
74
|
-
break;
|
|
75
|
-
case "be.enabled":
|
|
76
|
-
await expect(el).toBeEnabled();
|
|
77
|
-
break;
|
|
78
|
-
case "be.disabled":
|
|
79
|
-
await expect(el).toBeDisabled();
|
|
80
|
-
break;
|
|
81
|
-
case "be.checked":
|
|
82
|
-
case "be.selected":
|
|
83
|
-
await expect(el).toBeSelected();
|
|
84
|
-
break;
|
|
85
|
-
case "not.be.checked":
|
|
86
|
-
case "not.be.selected":
|
|
87
|
-
await expect(el).not.toBeSelected();
|
|
88
|
-
break;
|
|
89
|
-
case "have.value":
|
|
90
|
-
await expect(el).toHaveValue(args[0]);
|
|
91
|
-
break;
|
|
92
|
-
case "contain.value":
|
|
93
|
-
await expect(el).toHaveValue(expect.stringContaining(args[0]));
|
|
94
|
-
break;
|
|
95
|
-
case "have.text":
|
|
96
|
-
await expect(el).toHaveText(args[0]);
|
|
97
|
-
break;
|
|
98
|
-
case "contain.text":
|
|
99
|
-
await expect(el).toHaveText(expect.stringContaining(args[0]));
|
|
100
|
-
break;
|
|
101
|
-
case "have.class":
|
|
102
|
-
await expect(el).toHaveElementClass(args[0]);
|
|
103
|
-
break;
|
|
104
|
-
case "have.attr":
|
|
105
|
-
if (args.length >= 2) {
|
|
106
|
-
await expect(el).toHaveAttribute(args[0], args[1]);
|
|
107
|
-
} else {
|
|
108
|
-
await expect(el).toHaveAttribute(args[0]);
|
|
109
|
-
}
|
|
110
|
-
break;
|
|
111
|
-
default:
|
|
112
|
-
throw new Error(`Unsupported matcher: ${matcher}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Executes a multi-element collection matcher against WebdriverIO $$.
|
|
118
|
-
*
|
|
119
|
-
* @param selector - CSS/XPath selector for the collection.
|
|
120
|
-
* @param matcher - Multi-element matcher string.
|
|
121
|
-
* @param args - Expected values or count limits.
|
|
122
|
-
*/
|
|
123
|
-
export async function executeMultiMatcher(
|
|
124
|
-
selector: string,
|
|
125
|
-
matcher: MultiElementMatcher,
|
|
126
|
-
args: any[]
|
|
127
|
-
): Promise<void> {
|
|
128
|
-
const elements = await $$(selector);
|
|
129
|
-
|
|
130
|
-
switch (matcher) {
|
|
131
|
-
case "have.length":
|
|
132
|
-
await expect(elements).toBeElementsArrayOfSize(args[0]);
|
|
133
|
-
break;
|
|
134
|
-
case "have.length.greaterThan":
|
|
135
|
-
await expect(elements).toBeElementsArrayOfSize({ gte: args[0] + 1 });
|
|
136
|
-
break;
|
|
137
|
-
case "be.empty":
|
|
138
|
-
await expect(elements).toBeElementsArrayOfSize(0);
|
|
139
|
-
break;
|
|
140
|
-
case "exist":
|
|
141
|
-
await expect(elements).toBeElementsArrayOfSize({ gte: 1 });
|
|
142
|
-
break;
|
|
143
|
-
default:
|
|
144
|
-
throw new Error(`Unsupported multi-element matcher: ${matcher}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
package/src/runner/collection.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { MultiElementMatcher } from "../types.js";
|
|
2
|
-
import { executeMultiMatcher } from "../matchers.js";
|
|
3
|
-
import { SingleElementRunner } from "./single.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Representation of a queued assertion against an element collection.
|
|
7
|
-
*/
|
|
8
|
-
type CollectionAssertion = {
|
|
9
|
-
matcher: MultiElementMatcher;
|
|
10
|
-
args: any[];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Fluent assertion, filtering, and iteration runner for multi-element collections.
|
|
15
|
-
*
|
|
16
|
-
* Implements `PromiseLike<void>`, allowing assertions across matching elements,
|
|
17
|
-
* array index access (`.eq()`, `.first()`, `.last()`), and sequential iteration (`.each()`).
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* // Assert collection size
|
|
22
|
-
* await Spectra.getAll(".cart-item").should("have.length", 3);
|
|
23
|
-
*
|
|
24
|
-
* // Target specific element from collection
|
|
25
|
-
* await Spectra.getAll(".todo-item").first().click();
|
|
26
|
-
* await Spectra.getAll(".todo-item").eq(2).should("have.text", "Buy Milk");
|
|
27
|
-
*
|
|
28
|
-
* // Iteration
|
|
29
|
-
* await Spectra.getAll(".nav-link").each(async (link, i) => {
|
|
30
|
-
* await link.should("be.visible");
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export class MultiElementRunner implements PromiseLike<void> {
|
|
35
|
-
private _selector: string;
|
|
36
|
-
private _assertions: CollectionAssertion[] = [];
|
|
37
|
-
|
|
38
|
-
constructor(selector: string) {
|
|
39
|
-
this._selector = selector;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// --- Collection Assertions ---
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Queues an assertion matcher against the multi-element collection.
|
|
46
|
-
*
|
|
47
|
-
* @param matcher - Multi-element matcher string (e.g. `'have.length'`, `'be.empty'`).
|
|
48
|
-
* @param args - Expected values or limits for the matcher.
|
|
49
|
-
* @example
|
|
50
|
-
* ```ts
|
|
51
|
-
* await Spectra.getAll(".product-card").should("have.length.greaterThan", 0);
|
|
52
|
-
* await Spectra.getAll(".error-badge").should("be.empty");
|
|
53
|
-
* ```
|
|
54
|
-
* @returns Current collection runner instance for chaining.
|
|
55
|
-
*/
|
|
56
|
-
should(matcher: MultiElementMatcher, ...args: any[]): this {
|
|
57
|
-
this._assertions.push({ matcher, args });
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// --- Item Navigation ---
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Returns a `SingleElementRunner` targeting the element at the specified 0-based index.
|
|
65
|
-
*
|
|
66
|
-
* @param index - 0-based index of the element within the matched collection.
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* await Spectra.getAll("li.menu-item").eq(1).click();
|
|
70
|
-
* ```
|
|
71
|
-
* @returns `SingleElementRunner` for the indexed element.
|
|
72
|
-
*/
|
|
73
|
-
eq(index: number): SingleElementRunner {
|
|
74
|
-
const indexedSelector = `(${this._selector})[${index + 1}]`;
|
|
75
|
-
return new SingleElementRunner(indexedSelector);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Returns a `SingleElementRunner` targeting the first element in the collection (`index = 0`).
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```ts
|
|
83
|
-
* await Spectra.getAll(".result-item").first().click();
|
|
84
|
-
* ```
|
|
85
|
-
* @returns `SingleElementRunner` for the first element.
|
|
86
|
-
*/
|
|
87
|
-
first(): SingleElementRunner {
|
|
88
|
-
return this.eq(0);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Returns a `SingleElementRunner` targeting the last element in the collection.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```ts
|
|
96
|
-
* await Spectra.getAll(".step-indicator").last().should("have.class", "current");
|
|
97
|
-
* ```
|
|
98
|
-
* @returns `SingleElementRunner` for the last element.
|
|
99
|
-
*/
|
|
100
|
-
last(): SingleElementRunner {
|
|
101
|
-
const indexedSelector = `(${this._selector})[last()]`;
|
|
102
|
-
return new SingleElementRunner(indexedSelector);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// --- Iteration Callback ---
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Iterates asynchronously through every matched element in the collection sequentially.
|
|
109
|
-
*
|
|
110
|
-
* @param callback - Async function called for each element with `(runner, index)`.
|
|
111
|
-
* @example
|
|
112
|
-
* ```ts
|
|
113
|
-
* await Spectra.getAll(".checkbox").each(async (item, index) => {
|
|
114
|
-
* await item.should("be.visible");
|
|
115
|
-
* });
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
async each(
|
|
119
|
-
callback: (el: SingleElementRunner, index: number) => Promise<void>
|
|
120
|
-
): Promise<void> {
|
|
121
|
-
const elements = await $$(this._selector);
|
|
122
|
-
const count = await (elements as any).length;
|
|
123
|
-
for (let i = 0; i < count; i++) {
|
|
124
|
-
const runner = this.eq(i);
|
|
125
|
-
await callback(runner, i);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// --- Execution when awaited ---
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Standard Promise `then` implementation.
|
|
133
|
-
* Executes all queued collection assertions sequentially when awaited.
|
|
134
|
-
*/
|
|
135
|
-
async then<TResult1 = void, TResult2 = never>(
|
|
136
|
-
onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,
|
|
137
|
-
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
|
138
|
-
): Promise<TResult1 | TResult2> {
|
|
139
|
-
try {
|
|
140
|
-
for (const assert of this._assertions) {
|
|
141
|
-
await executeMultiMatcher(this._selector, assert.matcher, assert.args);
|
|
142
|
-
}
|
|
143
|
-
const res = (onfulfilled ? await onfulfilled() : undefined) as TResult1;
|
|
144
|
-
return res;
|
|
145
|
-
} catch (err) {
|
|
146
|
-
if (onrejected) {
|
|
147
|
-
return onrejected(err);
|
|
148
|
-
}
|
|
149
|
-
throw err;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
package/src/runner/single.ts
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import { SingleElementMatcher, ElementTarget, TypeOptions } from "../types.js";
|
|
2
|
-
import { executeSingleMatcher, resolveElement } from "../matchers.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Representation of a queued assertion against an element or page.
|
|
6
|
-
*/
|
|
7
|
-
type AssertionItem = {
|
|
8
|
-
target?: ElementTarget;
|
|
9
|
-
matcher: SingleElementMatcher;
|
|
10
|
-
args: any[];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Fluent interaction and assertion runner for a single element or browser context.
|
|
15
|
-
*
|
|
16
|
-
* Implements `PromiseLike<void>`, allowing it to be awaited directly or chained with
|
|
17
|
-
* multiple actions and `.should(...)` assertions.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* // Direct action + chained assertion
|
|
22
|
-
* await Spectra.get("#submit-btn")
|
|
23
|
-
* .click()
|
|
24
|
-
* .should("not.be.visible");
|
|
25
|
-
*
|
|
26
|
-
* // Query + multiple assertions
|
|
27
|
-
* await Spectra.get(LoginPage.usernameInput)
|
|
28
|
-
* .should("be.visible")
|
|
29
|
-
* .should("be.enabled")
|
|
30
|
-
* .should("have.value", "admin");
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export class SingleElementRunner implements PromiseLike<void> {
|
|
34
|
-
private _target: ElementTarget | undefined;
|
|
35
|
-
private _action: (() => Promise<any>) | null = null;
|
|
36
|
-
private _assertions: AssertionItem[] = [];
|
|
37
|
-
|
|
38
|
-
constructor(target?: ElementTarget, action?: () => Promise<any>) {
|
|
39
|
-
this._target = target;
|
|
40
|
-
this._action = action || null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// --- Chained Actions on current target ---
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Clicks on the target element.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```ts
|
|
50
|
-
* await Spectra.get("#login-btn").click();
|
|
51
|
-
* ```
|
|
52
|
-
* @returns Current runner instance for method chaining.
|
|
53
|
-
*/
|
|
54
|
-
click(): this {
|
|
55
|
-
const target = this.requireTarget("click");
|
|
56
|
-
this._action = async () => {
|
|
57
|
-
const el = await resolveElement(target);
|
|
58
|
-
await el.click();
|
|
59
|
-
};
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Double-clicks on the target element.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```ts
|
|
68
|
-
* await Spectra.get(".file-row").doubleClick();
|
|
69
|
-
* ```
|
|
70
|
-
* @returns Current runner instance for method chaining.
|
|
71
|
-
*/
|
|
72
|
-
doubleClick(): this {
|
|
73
|
-
const target = this.requireTarget("doubleClick");
|
|
74
|
-
this._action = async () => {
|
|
75
|
-
const el = await resolveElement(target);
|
|
76
|
-
await el.doubleClick();
|
|
77
|
-
};
|
|
78
|
-
return this;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Performs a long-press touch or hold gesture on the target element.
|
|
83
|
-
*
|
|
84
|
-
* @param durationMs - Duration in milliseconds to hold (default: 1000).
|
|
85
|
-
* @example
|
|
86
|
-
* ```ts
|
|
87
|
-
* await Spectra.get("#context-menu-trigger").longPress(1500);
|
|
88
|
-
* ```
|
|
89
|
-
* @returns Current runner instance for method chaining.
|
|
90
|
-
*/
|
|
91
|
-
longPress(durationMs = 1000): this {
|
|
92
|
-
const target = this.requireTarget("longPress");
|
|
93
|
-
this._action = async () => {
|
|
94
|
-
const el = await resolveElement(target);
|
|
95
|
-
if (typeof (el as any).touchAction === "function") {
|
|
96
|
-
await (el as any).touchAction([
|
|
97
|
-
{ action: "longPress", ms: durationMs },
|
|
98
|
-
{ action: "release" },
|
|
99
|
-
]);
|
|
100
|
-
} else {
|
|
101
|
-
await el.click();
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Types string value into the target input element.
|
|
109
|
-
*
|
|
110
|
-
* @param value - Text string to type into the element.
|
|
111
|
-
* @param options - Optional typing options (e.g. `clearFirst: true`).
|
|
112
|
-
* @example
|
|
113
|
-
* ```ts
|
|
114
|
-
* await Spectra.get("#search-input").type("iPhone 15", { clearFirst: true });
|
|
115
|
-
* ```
|
|
116
|
-
* @returns Current runner instance for method chaining.
|
|
117
|
-
*/
|
|
118
|
-
type(value: string, options?: TypeOptions): this {
|
|
119
|
-
const target = this.requireTarget("type");
|
|
120
|
-
this._action = async () => {
|
|
121
|
-
const el = await resolveElement(target);
|
|
122
|
-
if (options?.clearFirst) {
|
|
123
|
-
await el.clearValue();
|
|
124
|
-
}
|
|
125
|
-
await el.setValue(value);
|
|
126
|
-
};
|
|
127
|
-
return this;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Clears the current value of the target input element.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```ts
|
|
135
|
-
* await Spectra.get("#search-input").clear();
|
|
136
|
-
* ```
|
|
137
|
-
* @returns Current runner instance for method chaining.
|
|
138
|
-
*/
|
|
139
|
-
clear(): this {
|
|
140
|
-
const target = this.requireTarget("clear");
|
|
141
|
-
this._action = async () => {
|
|
142
|
-
const el = await resolveElement(target);
|
|
143
|
-
await el.clearValue();
|
|
144
|
-
};
|
|
145
|
-
return this;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Selects an option from a `<select>` dropdown by its visible text.
|
|
150
|
-
*
|
|
151
|
-
* @param value - Visible text of the option to select.
|
|
152
|
-
* @example
|
|
153
|
-
* ```ts
|
|
154
|
-
* await Spectra.get("#country-select").select("United States");
|
|
155
|
-
* ```
|
|
156
|
-
* @returns Current runner instance for method chaining.
|
|
157
|
-
*/
|
|
158
|
-
select(value: string): this {
|
|
159
|
-
const target = this.requireTarget("select");
|
|
160
|
-
this._action = async () => {
|
|
161
|
-
const el = await resolveElement(target);
|
|
162
|
-
await el.selectByVisibleText(value);
|
|
163
|
-
};
|
|
164
|
-
return this;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Moves the mouse cursor over the target element (hover).
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* ```ts
|
|
172
|
-
* await Spectra.get("#dropdown-menu-trigger").hover();
|
|
173
|
-
* ```
|
|
174
|
-
* @returns Current runner instance for method chaining.
|
|
175
|
-
*/
|
|
176
|
-
hover(): this {
|
|
177
|
-
const target = this.requireTarget("hover");
|
|
178
|
-
this._action = async () => {
|
|
179
|
-
const el = await resolveElement(target);
|
|
180
|
-
await el.moveTo();
|
|
181
|
-
};
|
|
182
|
-
return this;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Waits for the target element to become displayed in the DOM.
|
|
187
|
-
*
|
|
188
|
-
* @param timeoutMs - Maximum duration in milliseconds to wait (default: 10000).
|
|
189
|
-
* @example
|
|
190
|
-
* ```ts
|
|
191
|
-
* await Spectra.get("#modal-dialog").waitForElement(5000);
|
|
192
|
-
* ```
|
|
193
|
-
* @returns Current runner instance for method chaining.
|
|
194
|
-
*/
|
|
195
|
-
waitForElement(timeoutMs = 10000): this {
|
|
196
|
-
const target = this.requireTarget("waitForElement");
|
|
197
|
-
this._action = async () => {
|
|
198
|
-
const el = await resolveElement(target);
|
|
199
|
-
await el.waitForDisplayed({ timeout: timeoutMs });
|
|
200
|
-
};
|
|
201
|
-
return this;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// --- Fluent Assertion Matchers ---
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Queues an assertion matcher against the current target element or page.
|
|
208
|
-
*
|
|
209
|
-
* @param matcher - Single element matcher string (e.g. `'be.visible'`, `'have.text'`).
|
|
210
|
-
* @param args - Additional parameters for the matcher (e.g. expected text or value).
|
|
211
|
-
* @example
|
|
212
|
-
* ```ts
|
|
213
|
-
* await Spectra.get("#status-badge").should("be.visible");
|
|
214
|
-
* await Spectra.get("#username").should("have.value", "alice");
|
|
215
|
-
* await Spectra.get("#banner").should("have.class", "alert-success");
|
|
216
|
-
* ```
|
|
217
|
-
* @returns Current runner instance for method chaining.
|
|
218
|
-
*/
|
|
219
|
-
should(matcher: SingleElementMatcher, ...args: any[]): this;
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Queues an assertion matcher targeting a specific element.
|
|
223
|
-
*
|
|
224
|
-
* @param target - Custom element target for this specific assertion.
|
|
225
|
-
* @param matcher - Single element matcher string.
|
|
226
|
-
* @param args - Additional parameters for the matcher.
|
|
227
|
-
* @example
|
|
228
|
-
* ```ts
|
|
229
|
-
* await Spectra.should(LoginPage.submitBtn, "be.enabled");
|
|
230
|
-
* ```
|
|
231
|
-
* @returns Current runner instance for method chaining.
|
|
232
|
-
*/
|
|
233
|
-
should(target: ElementTarget, matcher: SingleElementMatcher, ...args: any[]): this;
|
|
234
|
-
|
|
235
|
-
should(a: ElementTarget | SingleElementMatcher, b?: any, ...rest: any[]): this {
|
|
236
|
-
const isFirstArgMatcher =
|
|
237
|
-
typeof a === "string" &&
|
|
238
|
-
(a.includes(".") ||
|
|
239
|
-
a.includes("exist") ||
|
|
240
|
-
a.includes("visible") ||
|
|
241
|
-
a.includes("enabled") ||
|
|
242
|
-
a.includes("disabled") ||
|
|
243
|
-
a.includes("selected") ||
|
|
244
|
-
a.includes("checked"));
|
|
245
|
-
|
|
246
|
-
if (b !== undefined && !isFirstArgMatcher) {
|
|
247
|
-
// Form: should(target, matcher, ...args)
|
|
248
|
-
this._assertions.push({
|
|
249
|
-
target: a as ElementTarget,
|
|
250
|
-
matcher: b as SingleElementMatcher,
|
|
251
|
-
args: rest,
|
|
252
|
-
});
|
|
253
|
-
} else {
|
|
254
|
-
// Form: should(matcher, ...args) -> on current target
|
|
255
|
-
this._assertions.push({
|
|
256
|
-
target: this._target,
|
|
257
|
-
matcher: a as SingleElementMatcher,
|
|
258
|
-
args: b !== undefined ? [b, ...rest] : rest,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
return this;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// --- Execution when awaited ---
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Standard Promise `then` implementation.
|
|
268
|
-
* Executes queued action first (if any), followed sequentially by all queued assertions.
|
|
269
|
-
*/
|
|
270
|
-
async then<TResult1 = void, TResult2 = never>(
|
|
271
|
-
onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,
|
|
272
|
-
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
|
273
|
-
): Promise<TResult1 | TResult2> {
|
|
274
|
-
try {
|
|
275
|
-
if (this._action) {
|
|
276
|
-
await this._action();
|
|
277
|
-
}
|
|
278
|
-
for (const assert of this._assertions) {
|
|
279
|
-
await executeSingleMatcher(assert.target, assert.matcher, assert.args);
|
|
280
|
-
}
|
|
281
|
-
const res = (onfulfilled ? await onfulfilled() : undefined) as TResult1;
|
|
282
|
-
return res;
|
|
283
|
-
} catch (err) {
|
|
284
|
-
if (onrejected) {
|
|
285
|
-
return onrejected(err);
|
|
286
|
-
}
|
|
287
|
-
throw err;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Helper that throws an error if an action is called without a defined target.
|
|
293
|
-
*/
|
|
294
|
-
private requireTarget(actionName: string): ElementTarget {
|
|
295
|
-
if (!this._target) {
|
|
296
|
-
throw new Error(`Cannot execute '${actionName}' without specifying an element target.`);
|
|
297
|
-
}
|
|
298
|
-
return this._target;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|