@testspectra/matchers 1.1.11-rc.5 → 1.1.13
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/dist/__tests__/matcher-contracts.test.d.ts +1 -0
- package/dist/__tests__/matcher-contracts.test.js +498 -0
- package/dist/contract.d.ts +5 -42
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/matchers.d.ts +10 -3
- package/dist/matchers.js +576 -135
- package/dist/proto.d.ts +1 -0
- package/dist/reporter.js +13 -1
- package/dist/runner/collection.js +32 -6
- package/dist/runner/single.d.ts +2 -2
- package/dist/runner/single.js +24 -7
- package/dist/semantic.d.ts +11 -0
- package/dist/semantic.js +141 -0
- package/dist/types.d.ts +34 -1
- package/dist/worker_config.d.ts +97 -0
- package/dist/worker_config.js +17 -0
- package/package.json +1 -1
- package/src/contract.ts +5 -36
- package/src/index.ts +1 -0
- package/src/proto.ts +2 -0
- package/src/types.ts +37 -0
- package/src/worker_config.ts +102 -0
- package/testspectra-matchers-1.1.13.tgz +0 -0
- package/tsconfig.json +1 -1
- package/src/runtime/assertions.ts +0 -454
- package/src/runtime/element_actions.ts +0 -169
- package/src/runtime/element_proxy.ts +0 -199
- package/src/runtime/element_state.ts +0 -94
- package/src/runtime/spectra.ts +0 -110
- package/testspectra-matchers-1.1.11-rc.5.tgz +0 -0
package/dist/proto.d.ts
CHANGED
package/dist/reporter.js
CHANGED
|
@@ -67,7 +67,19 @@ export function formatArgs(args) {
|
|
|
67
67
|
*/
|
|
68
68
|
export async function trackCommand(category, commandText, fn) {
|
|
69
69
|
const start = Date.now();
|
|
70
|
-
const
|
|
70
|
+
const payloadObj = typeof commandText === 'object' ? { ...commandText } : { text: commandText };
|
|
71
|
+
if (typeof globalThis !== 'undefined') {
|
|
72
|
+
const g = globalThis;
|
|
73
|
+
const currentTest = g.__CURRENT_TEST_TITLE__ || g.__CURRENT_TEST_ID__;
|
|
74
|
+
if (currentTest && !payloadObj.test) {
|
|
75
|
+
payloadObj.test = currentTest;
|
|
76
|
+
}
|
|
77
|
+
const workerId = g.__TESTSPECTRA_WORKER_ID__;
|
|
78
|
+
if (workerId !== undefined && payloadObj.workerId === undefined) {
|
|
79
|
+
payloadObj.workerId = workerId;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const payload = JSON.stringify(payloadObj);
|
|
71
83
|
console.log(`[TESTSPECTRA_STEP_START] ${payload}`);
|
|
72
84
|
try {
|
|
73
85
|
const result = await fn();
|
|
@@ -84,9 +84,22 @@ export class MultiElementRunner {
|
|
|
84
84
|
eq(index) {
|
|
85
85
|
const targetSelector = `${this._selector}[${index}]`;
|
|
86
86
|
const elPromise = (async () => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const getElements = typeof $$ !== 'undefined' ? $$ : globalThis.$$;
|
|
88
|
+
if (typeof getElements === 'function') {
|
|
89
|
+
const elements = await getElements(this._selector);
|
|
90
|
+
if (typeof elements?.nth === 'function') {
|
|
91
|
+
return elements.nth(index);
|
|
92
|
+
}
|
|
93
|
+
if (index === 0 && typeof elements?.first === 'function') {
|
|
94
|
+
return elements.first();
|
|
95
|
+
}
|
|
96
|
+
if (elements && elements[index] !== undefined) {
|
|
97
|
+
return elements[index];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const getDollar = typeof $ !== 'undefined' ? $ : globalThis.$;
|
|
101
|
+
if (typeof getDollar === 'function') {
|
|
102
|
+
return getDollar(targetSelector);
|
|
90
103
|
}
|
|
91
104
|
return { selector: targetSelector };
|
|
92
105
|
})();
|
|
@@ -117,10 +130,23 @@ export class MultiElementRunner {
|
|
|
117
130
|
last() {
|
|
118
131
|
const targetSelector = `${this._selector}[last()]`;
|
|
119
132
|
const elPromise = (async () => {
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
const getElements = typeof $$ !== 'undefined' ? $$ : globalThis.$$;
|
|
134
|
+
if (typeof getElements === 'function') {
|
|
135
|
+
const elements = await getElements(this._selector);
|
|
136
|
+
if (typeof elements?.last === 'function') {
|
|
137
|
+
return elements.last();
|
|
138
|
+
}
|
|
139
|
+
if (typeof elements?.nth === 'function') {
|
|
140
|
+
return elements.nth(-1);
|
|
141
|
+
}
|
|
122
142
|
const arr = Array.isArray(elements) ? elements : await elements;
|
|
123
|
-
|
|
143
|
+
if (arr && arr.length > 0) {
|
|
144
|
+
return arr[arr.length - 1];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const getDollar = typeof $ !== 'undefined' ? $ : globalThis.$;
|
|
148
|
+
if (typeof getDollar === 'function') {
|
|
149
|
+
return getDollar(targetSelector);
|
|
124
150
|
}
|
|
125
151
|
return { selector: targetSelector };
|
|
126
152
|
})();
|
package/dist/runner/single.d.ts
CHANGED
|
@@ -90,11 +90,11 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
|
|
|
90
90
|
*/
|
|
91
91
|
select(value: string): this;
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* Hovers over the target element.
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
96
|
* ```ts
|
|
97
|
-
* await Spectra.get("#dropdown-
|
|
97
|
+
* await Spectra.get("#dropdown-trigger").hover();
|
|
98
98
|
* ```
|
|
99
99
|
* @returns Current runner instance for method chaining.
|
|
100
100
|
*/
|
package/dist/runner/single.js
CHANGED
|
@@ -120,9 +120,17 @@ export class SingleElementRunner {
|
|
|
120
120
|
this._action = async () => {
|
|
121
121
|
const el = await resolveElement(target);
|
|
122
122
|
if (options?.clearFirst) {
|
|
123
|
-
|
|
123
|
+
if (typeof el.clearValue === 'function')
|
|
124
|
+
await el.clearValue();
|
|
125
|
+
else if (typeof el.clear === 'function')
|
|
126
|
+
await el.clear();
|
|
124
127
|
}
|
|
125
|
-
|
|
128
|
+
if (typeof el.setValue === 'function')
|
|
129
|
+
await el.setValue(value);
|
|
130
|
+
else if (typeof el.fill === 'function')
|
|
131
|
+
await el.fill(value);
|
|
132
|
+
else if (typeof el.type === 'function')
|
|
133
|
+
await el.type(value);
|
|
126
134
|
};
|
|
127
135
|
return this;
|
|
128
136
|
}
|
|
@@ -140,7 +148,10 @@ export class SingleElementRunner {
|
|
|
140
148
|
this._actionPayload = { type: 'action', key: 'clear', target: getRawTarget(target), args: [] };
|
|
141
149
|
this._action = async () => {
|
|
142
150
|
const el = await resolveElement(target);
|
|
143
|
-
|
|
151
|
+
if (typeof el.clearValue === 'function')
|
|
152
|
+
await el.clearValue();
|
|
153
|
+
else if (typeof el.clear === 'function')
|
|
154
|
+
await el.clear();
|
|
144
155
|
};
|
|
145
156
|
return this;
|
|
146
157
|
}
|
|
@@ -159,16 +170,19 @@ export class SingleElementRunner {
|
|
|
159
170
|
this._actionPayload = { type: 'action', key: 'selectOption', target: getRawTarget(target), args: [value] };
|
|
160
171
|
this._action = async () => {
|
|
161
172
|
const el = await resolveElement(target);
|
|
162
|
-
|
|
173
|
+
if (typeof el.selectByVisibleText === 'function')
|
|
174
|
+
await el.selectByVisibleText(value);
|
|
175
|
+
else if (typeof el.selectOption === 'function')
|
|
176
|
+
await el.selectOption({ label: value });
|
|
163
177
|
};
|
|
164
178
|
return this;
|
|
165
179
|
}
|
|
166
180
|
/**
|
|
167
|
-
*
|
|
181
|
+
* Hovers over the target element.
|
|
168
182
|
*
|
|
169
183
|
* @example
|
|
170
184
|
* ```ts
|
|
171
|
-
* await Spectra.get("#dropdown-
|
|
185
|
+
* await Spectra.get("#dropdown-trigger").hover();
|
|
172
186
|
* ```
|
|
173
187
|
* @returns Current runner instance for method chaining.
|
|
174
188
|
*/
|
|
@@ -177,7 +191,10 @@ export class SingleElementRunner {
|
|
|
177
191
|
this._actionPayload = { type: 'action', key: 'hover', target: getRawTarget(target), args: [] };
|
|
178
192
|
this._action = async () => {
|
|
179
193
|
const el = await resolveElement(target);
|
|
180
|
-
|
|
194
|
+
if (typeof el.moveTo === 'function')
|
|
195
|
+
await el.moveTo();
|
|
196
|
+
else if (typeof el.hover === 'function')
|
|
197
|
+
await el.hover();
|
|
181
198
|
};
|
|
182
199
|
return this;
|
|
183
200
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* # @testspectra/matchers - Semantic Natural Language Formatter
|
|
4
|
+
*
|
|
5
|
+
* Formats commands, actions, and assertions into clean, grammatically
|
|
6
|
+
* correct English sentences for real-time reporting.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatSemanticTarget(target: any, isCollection?: boolean): string;
|
|
9
|
+
export declare function formatSemanticAction(action: string, target: any, args?: any[]): string;
|
|
10
|
+
export declare function formatSemanticMatcher(target: any, matcher: string, args?: any[], isCollection?: boolean): string;
|
|
11
|
+
export declare function formatSemanticBrowser(method: string, args?: any[]): string;
|
package/dist/semantic.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* # @testspectra/matchers - Semantic Natural Language Formatter
|
|
4
|
+
*
|
|
5
|
+
* Formats commands, actions, and assertions into clean, grammatically
|
|
6
|
+
* correct English sentences for real-time reporting.
|
|
7
|
+
*/
|
|
8
|
+
export function formatSemanticTarget(target, isCollection = false) {
|
|
9
|
+
if (target === undefined || target === null)
|
|
10
|
+
return isCollection ? 'collection' : 'element';
|
|
11
|
+
if (typeof target === 'string') {
|
|
12
|
+
return isCollection ? `collection "${target}"` : `element "${target}"`;
|
|
13
|
+
}
|
|
14
|
+
if (typeof target === 'object') {
|
|
15
|
+
if (target.selector) {
|
|
16
|
+
return isCollection ? `collection "${target.selector}"` : `element "${target.selector}"`;
|
|
17
|
+
}
|
|
18
|
+
if (target.constructor && target.constructor.name && target.constructor.name !== 'Object') {
|
|
19
|
+
return `element "${target.constructor.name}"`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return `element "${String(target)}"`;
|
|
23
|
+
}
|
|
24
|
+
export function formatSemanticAction(action, target, args = []) {
|
|
25
|
+
const targetStr = formatSemanticTarget(target, false);
|
|
26
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
27
|
+
switch (action) {
|
|
28
|
+
case 'type':
|
|
29
|
+
return `Type "${arg0}" into ${targetStr}`;
|
|
30
|
+
case 'click':
|
|
31
|
+
return `Click on ${targetStr}`;
|
|
32
|
+
case 'doubleClick':
|
|
33
|
+
return `Double-click on ${targetStr}`;
|
|
34
|
+
case 'rightClick':
|
|
35
|
+
return `Right-click on ${targetStr}`;
|
|
36
|
+
case 'check':
|
|
37
|
+
return `Check checkbox ${targetStr}`;
|
|
38
|
+
case 'uncheck':
|
|
39
|
+
return `Uncheck checkbox ${targetStr}`;
|
|
40
|
+
case 'selectOption':
|
|
41
|
+
return `Select option "${arg0}" in ${targetStr}`;
|
|
42
|
+
case 'clear':
|
|
43
|
+
return `Clear text in ${targetStr}`;
|
|
44
|
+
case 'hover':
|
|
45
|
+
return `Hover over ${targetStr}`;
|
|
46
|
+
case 'scrollIntoView':
|
|
47
|
+
return `Scroll ${targetStr} into view`;
|
|
48
|
+
case 'navigate':
|
|
49
|
+
return `Navigate to "${arg0}"`;
|
|
50
|
+
case 'wait':
|
|
51
|
+
return `Wait for ${arg0}ms`;
|
|
52
|
+
case 'pause':
|
|
53
|
+
return `Pause execution for ${arg0}ms`;
|
|
54
|
+
default:
|
|
55
|
+
return `Perform ${action} on ${targetStr}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function formatSemanticMatcher(target, matcher, args = [], isCollection = false) {
|
|
59
|
+
const targetStr = formatSemanticTarget(target, isCollection);
|
|
60
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
61
|
+
const arg1 = args[1] !== undefined ? String(args[1]) : '';
|
|
62
|
+
switch (matcher) {
|
|
63
|
+
case 'be.visible':
|
|
64
|
+
return `Expect ${targetStr} to be visible`;
|
|
65
|
+
case 'not.be.visible':
|
|
66
|
+
return `Expect ${targetStr} not to be visible`;
|
|
67
|
+
case 'exist':
|
|
68
|
+
return `Expect ${targetStr} to exist in DOM`;
|
|
69
|
+
case 'not.exist':
|
|
70
|
+
return `Expect ${targetStr} not to exist in DOM`;
|
|
71
|
+
case 'be.enabled':
|
|
72
|
+
return `Expect ${targetStr} to be enabled`;
|
|
73
|
+
case 'be.disabled':
|
|
74
|
+
return `Expect ${targetStr} to be disabled`;
|
|
75
|
+
case 'be.checked':
|
|
76
|
+
return `Expect ${targetStr} to be checked`;
|
|
77
|
+
case 'not.be.checked':
|
|
78
|
+
return `Expect ${targetStr} not to be checked`;
|
|
79
|
+
case 'have.text':
|
|
80
|
+
return `Expect ${targetStr} to have text "${arg0}"`;
|
|
81
|
+
case 'not.have.text':
|
|
82
|
+
return `Expect ${targetStr} not to have text "${arg0}"`;
|
|
83
|
+
case 'contain.text':
|
|
84
|
+
return `Expect ${targetStr} to contain text "${arg0}"`;
|
|
85
|
+
case 'not.contain.text':
|
|
86
|
+
return `Expect ${targetStr} not to contain text "${arg0}"`;
|
|
87
|
+
case 'have.value':
|
|
88
|
+
return `Expect ${targetStr} to have value "${arg0}"`;
|
|
89
|
+
case 'not.have.value':
|
|
90
|
+
return `Expect ${targetStr} not to have value "${arg0}"`;
|
|
91
|
+
case 'contain.value':
|
|
92
|
+
return `Expect ${targetStr} to contain value "${arg0}"`;
|
|
93
|
+
case 'not.contain.value':
|
|
94
|
+
return `Expect ${targetStr} not to contain value "${arg0}"`;
|
|
95
|
+
case 'have.class':
|
|
96
|
+
return `Expect ${targetStr} to have class "${arg0}"`;
|
|
97
|
+
case 'not.have.class':
|
|
98
|
+
return `Expect ${targetStr} not to have class "${arg0}"`;
|
|
99
|
+
case 'have.attr':
|
|
100
|
+
return args.length >= 2
|
|
101
|
+
? `Expect ${targetStr} to have attribute "${arg0}" = "${arg1}"`
|
|
102
|
+
: `Expect ${targetStr} to have attribute "${arg0}"`;
|
|
103
|
+
case 'not.have.attr':
|
|
104
|
+
return `Expect ${targetStr} not to have attribute "${arg0}"`;
|
|
105
|
+
case 'be.focused':
|
|
106
|
+
return `Expect ${targetStr} to be focused`;
|
|
107
|
+
case 'be.selected':
|
|
108
|
+
return `Expect ${targetStr} to be selected`;
|
|
109
|
+
case 'be.empty':
|
|
110
|
+
return `Expect ${targetStr} to be empty`;
|
|
111
|
+
case 'not.be.empty':
|
|
112
|
+
return `Expect ${targetStr} not to be empty`;
|
|
113
|
+
case 'have.length':
|
|
114
|
+
return `Expect ${targetStr} count to equal ${arg0}`;
|
|
115
|
+
case 'have.length.greaterThan':
|
|
116
|
+
return `Expect ${targetStr} count to be greater than ${arg0}`;
|
|
117
|
+
case 'have.length.lessThan':
|
|
118
|
+
return `Expect ${targetStr} count to be less than ${arg0}`;
|
|
119
|
+
case 'have.length.atLeast':
|
|
120
|
+
return `Expect ${targetStr} count to be at least ${arg0}`;
|
|
121
|
+
case 'have.length.atMost':
|
|
122
|
+
return `Expect ${targetStr} count to be at most ${arg0}`;
|
|
123
|
+
default:
|
|
124
|
+
return `Expect ${targetStr} to match ${matcher}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function formatSemanticBrowser(method, args = []) {
|
|
128
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
129
|
+
switch (method) {
|
|
130
|
+
case 'shouldContainTitle':
|
|
131
|
+
return `Expect browser title to contain "${arg0}"`;
|
|
132
|
+
case 'shouldHaveTitle':
|
|
133
|
+
return `Expect browser title to be "${arg0}"`;
|
|
134
|
+
case 'shouldContainUrl':
|
|
135
|
+
return `Expect browser URL to contain "${arg0}"`;
|
|
136
|
+
case 'shouldHaveUrl':
|
|
137
|
+
return `Expect browser URL to be "${arg0}"`;
|
|
138
|
+
default:
|
|
139
|
+
return `Expect browser to ${method} "${arg0}"`;
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Canonical action keys supported across TestSpectra runner and database models.
|
|
3
3
|
* @see backend/src/models/test_step.rs
|
|
4
4
|
*/
|
|
5
|
-
export type ActionKey = 'navigate' | 'click' | 'type' | 'clear' | 'select' | 'scroll' | 'swipe' | 'wait' | 'waitForElement' | 'pressKey' | 'longPress' | 'doubleClick' | 'hover' | 'dragDrop' | 'back' | 'refresh';
|
|
5
|
+
export type ActionKey = 'navigate' | 'click' | 'type' | 'clear' | 'select' | 'scroll' | 'swipe' | 'wait' | 'waitForElement' | 'pressKey' | 'longPress' | 'doubleClick' | 'hover' | 'dragDrop' | 'upload' | 'back' | 'refresh';
|
|
6
6
|
/**
|
|
7
7
|
* Canonical assertion keys supported across TestSpectra runner and database models.
|
|
8
8
|
* @see backend/src/models/test_step.rs
|
|
@@ -685,6 +685,28 @@ export interface SingleElementProxy extends ElementReceiverAssertions<Promise<vo
|
|
|
685
685
|
* ```
|
|
686
686
|
*/
|
|
687
687
|
clear(): Promise<void>;
|
|
688
|
+
/**
|
|
689
|
+
* Uploads one or more files to this file input element (`<input type="file">`).
|
|
690
|
+
* Accepts a file path string, fixture object/path (e.g. `Fixture.sampleImage`), `File` instance, or an array of them.
|
|
691
|
+
*
|
|
692
|
+
* @param files File path, fixture path, or File instance to attach.
|
|
693
|
+
* @example
|
|
694
|
+
* ```ts
|
|
695
|
+
* await Spectra.get('#file-upload').upload(Fixture.sampleImage);
|
|
696
|
+
* await Spectra.get('input[type="file"]').upload('fixtures/sample.pdf');
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
upload(files: unknown): Promise<void>;
|
|
700
|
+
/**
|
|
701
|
+
* Uploads one or more files to this file input element (alias to `upload()`).
|
|
702
|
+
*
|
|
703
|
+
* @param files File path, fixture path, or File instance to attach.
|
|
704
|
+
* @example
|
|
705
|
+
* ```ts
|
|
706
|
+
* await Spectra.get('#file-upload').uploadFile(Fixture.sampleImage);
|
|
707
|
+
* ```
|
|
708
|
+
*/
|
|
709
|
+
uploadFile(files: unknown): Promise<void>;
|
|
688
710
|
/**
|
|
689
711
|
* Selects an option in a `<select>` element by its visible text or value.
|
|
690
712
|
*
|
|
@@ -1006,6 +1028,17 @@ export interface QueuedMockResponse {
|
|
|
1006
1028
|
/** Milliseconds to wait before fulfilling this response — simulates a late/slow network reply. */
|
|
1007
1029
|
delayMs?: number;
|
|
1008
1030
|
}
|
|
1031
|
+
/**
|
|
1032
|
+
* A transport-agnostic selected mock response: the next `respondOnce` queue item wins (FIFO),
|
|
1033
|
+
* otherwise the rule default. Produced by the shared `consumeMockResponse` helper; each
|
|
1034
|
+
* transport (CDP `Fetch.fulfillRequest`, in-page `Response`, mobile mock proxy) renders it.
|
|
1035
|
+
*/
|
|
1036
|
+
export interface ConsumedMockResponse {
|
|
1037
|
+
body: unknown;
|
|
1038
|
+
statusCode: number;
|
|
1039
|
+
headers: Record<string, string>;
|
|
1040
|
+
delayMs?: number;
|
|
1041
|
+
}
|
|
1009
1042
|
/**
|
|
1010
1043
|
* Mock rule configuration for CDP & Mobile network request interception.
|
|
1011
1044
|
*/
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared worker-runtime configuration contract.
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth for the shape of `globalThis.__TESTSPECTRA_CONFIG__`, the
|
|
5
|
+
* runtime config object every host injects before the shared DSL is exercised:
|
|
6
|
+
*
|
|
7
|
+
* - E2E: the Rust orchestrator writes it into the Bun worker script
|
|
8
|
+
* (`core/orchestrator/src/worker_runtime.rs`) — one worker per spec batch.
|
|
9
|
+
* - Component testing: `@testspectra/react`'s browser prelude publishes it
|
|
10
|
+
* (`tools/react/src/prelude.ts`) — one page per spec file.
|
|
11
|
+
*
|
|
12
|
+
* `core/worker-runtime/src/harness/config.ts`'s `getWorkerConfig()` reads it back with the
|
|
13
|
+
* priority: in-memory override → this global → `process.env.TESTSPECTRA_CONFIG` → hard defaults,
|
|
14
|
+
* so both hosts resolve runtime knobs (step delay, implicit wait, env vars, network monitoring)
|
|
15
|
+
* identically instead of each keeping a private config shape.
|
|
16
|
+
*/
|
|
17
|
+
/** Metadata for an auto-discovered Page Object class. */
|
|
18
|
+
export interface PageObjectInfo {
|
|
19
|
+
name: string;
|
|
20
|
+
file_path: string;
|
|
21
|
+
}
|
|
22
|
+
/** Metadata for an auto-discovered Fixture data file. */
|
|
23
|
+
export interface FixtureInfo {
|
|
24
|
+
name: string;
|
|
25
|
+
file_path: string;
|
|
26
|
+
is_json: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Metadata for an auto-discovered Step definition. */
|
|
29
|
+
export interface StepInfo {
|
|
30
|
+
name: string;
|
|
31
|
+
file_path: string;
|
|
32
|
+
}
|
|
33
|
+
/** Metadata for an auto-discovered custom Action. */
|
|
34
|
+
export interface ActionInfo {
|
|
35
|
+
name: string;
|
|
36
|
+
file_path: string;
|
|
37
|
+
}
|
|
38
|
+
/** Metadata for an auto-discovered lifecycle Hook. */
|
|
39
|
+
export interface HookInfo {
|
|
40
|
+
hook_type: 'before' | 'after' | 'beforeEach' | 'afterEach' | string;
|
|
41
|
+
file_path: string;
|
|
42
|
+
}
|
|
43
|
+
/** Auto-import manifest containing all discovered support files. */
|
|
44
|
+
export interface AutoImportManifestConfig {
|
|
45
|
+
page_objects?: PageObjectInfo[];
|
|
46
|
+
fixtures?: FixtureInfo[];
|
|
47
|
+
steps?: StepInfo[];
|
|
48
|
+
actions?: ActionInfo[];
|
|
49
|
+
global_hooks?: HookInfo[];
|
|
50
|
+
suite_hooks?: Record<string, HookInfo[]>;
|
|
51
|
+
}
|
|
52
|
+
/** One targeted test case inside a worker's batch. */
|
|
53
|
+
export interface TestCaseTargetItem {
|
|
54
|
+
id: string;
|
|
55
|
+
title: string;
|
|
56
|
+
filePath: string;
|
|
57
|
+
suite: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Runtime configuration injected by a host (Rust orchestrator or `@testspectra/react`) via
|
|
61
|
+
* `globalThis.__TESTSPECTRA_CONFIG__`.
|
|
62
|
+
*/
|
|
63
|
+
export interface TestSpectraWorkerConfig {
|
|
64
|
+
workerId?: number;
|
|
65
|
+
rootDir: string;
|
|
66
|
+
testFilePath?: string;
|
|
67
|
+
testFiles?: TestCaseTargetItem[];
|
|
68
|
+
activePlatform: string;
|
|
69
|
+
configuredBaseUrl: string;
|
|
70
|
+
cdpWsUrl: string;
|
|
71
|
+
driverServerUrl: string;
|
|
72
|
+
manifest?: AutoImportManifestConfig;
|
|
73
|
+
/** Target app package (Android). Paired with androidSerial/adbPath for on-demand adb actions. */
|
|
74
|
+
appPackage?: string;
|
|
75
|
+
/** Target adb device serial for this worker (Android only). */
|
|
76
|
+
androidSerial?: string;
|
|
77
|
+
/** Resolved `adb` binary path (Android only), paired with androidSerial above. */
|
|
78
|
+
adbPath?: string;
|
|
79
|
+
/** Host port this worker's own MobileMockProxyServer should listen on (Android only). */
|
|
80
|
+
mockProxyPort?: number;
|
|
81
|
+
implicitWaitMs?: number;
|
|
82
|
+
timeoutMs?: number;
|
|
83
|
+
stepDelayMs?: number;
|
|
84
|
+
/** Video recording options or boolean toggle */
|
|
85
|
+
video?: boolean | Record<string, any>;
|
|
86
|
+
/** Configured host aliases (e.g. auth: 'http://auth.local'). */
|
|
87
|
+
hosts?: Record<string, string>;
|
|
88
|
+
/** Mirrors `spectra.config.ts`'s `executionConfig.networkMonitoringEnabled` (default: true). */
|
|
89
|
+
networkMonitoringEnabled?: boolean;
|
|
90
|
+
/** Mirrors `spectra.config.ts`'s `executionConfig.monitoredDomains`. Empty/absent = capture all. */
|
|
91
|
+
monitoredDomains?: {
|
|
92
|
+
domain: string;
|
|
93
|
+
enabled: boolean;
|
|
94
|
+
}[];
|
|
95
|
+
/** Mirrors `spectra.config.ts`'s `executionConfig.environmentVariables`. */
|
|
96
|
+
environmentVariables?: Record<string, string>;
|
|
97
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared worker-runtime configuration contract.
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth for the shape of `globalThis.__TESTSPECTRA_CONFIG__`, the
|
|
5
|
+
* runtime config object every host injects before the shared DSL is exercised:
|
|
6
|
+
*
|
|
7
|
+
* - E2E: the Rust orchestrator writes it into the Bun worker script
|
|
8
|
+
* (`core/orchestrator/src/worker_runtime.rs`) — one worker per spec batch.
|
|
9
|
+
* - Component testing: `@testspectra/react`'s browser prelude publishes it
|
|
10
|
+
* (`tools/react/src/prelude.ts`) — one page per spec file.
|
|
11
|
+
*
|
|
12
|
+
* `core/worker-runtime/src/harness/config.ts`'s `getWorkerConfig()` reads it back with the
|
|
13
|
+
* priority: in-memory override → this global → `process.env.TESTSPECTRA_CONFIG` → hard defaults,
|
|
14
|
+
* so both hosts resolve runtime knobs (step delay, implicit wait, env vars, network monitoring)
|
|
15
|
+
* identically instead of each keeping a private config shape.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/package.json
CHANGED
package/src/contract.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* DSL layer contains zero platform branching.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { TestSpectraWorkerConfig } from './worker_config.js';
|
|
11
12
|
import type {
|
|
12
13
|
CDPNetworkEntry,
|
|
13
14
|
MockInterceptHandle,
|
|
@@ -19,38 +20,10 @@ import type {
|
|
|
19
20
|
} from './types.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
+
* @deprecated Renamed to `TestSpectraWorkerConfig` (now shared by E2E and component testing).
|
|
24
|
+
* Kept as an alias for backwards compatibility.
|
|
23
25
|
*/
|
|
24
|
-
export
|
|
25
|
-
workerId?: number;
|
|
26
|
-
rootDir: string;
|
|
27
|
-
testFilePath?: string;
|
|
28
|
-
testFiles?: Array<{ id: string; title: string; filePath: string; suite: string }>;
|
|
29
|
-
activePlatform: string;
|
|
30
|
-
configuredBaseUrl: string;
|
|
31
|
-
cdpWsUrl: string;
|
|
32
|
-
driverServerUrl: string;
|
|
33
|
-
manifest?: Record<string, any>;
|
|
34
|
-
/** Target app package (Android). Paired with androidSerial/adbPath for on-demand adb actions. */
|
|
35
|
-
appPackage?: string;
|
|
36
|
-
/** Target adb device serial for this worker (Android only). */
|
|
37
|
-
androidSerial?: string;
|
|
38
|
-
/** Resolved `adb` binary path (Android only), paired with androidSerial above. */
|
|
39
|
-
adbPath?: string;
|
|
40
|
-
/** Host port this worker's own MobileMockProxyServer should listen on (Android only) — derived
|
|
41
|
-
* per-device the same way androidSerial/the driver-server port already are, so each device in
|
|
42
|
-
* a multi-device run gets its own proxy instead of every worker racing for one shared port. */
|
|
43
|
-
mockProxyPort?: number;
|
|
44
|
-
implicitWaitMs?: number;
|
|
45
|
-
timeoutMs?: number;
|
|
46
|
-
stepDelayMs?: number;
|
|
47
|
-
/** Mirrors `spectra.config.ts`'s `executionConfig.networkMonitoringEnabled` (default: true). */
|
|
48
|
-
networkMonitoringEnabled?: boolean;
|
|
49
|
-
/** Mirrors `spectra.config.ts`'s `executionConfig.monitoredDomains`. Empty/absent = capture all. */
|
|
50
|
-
monitoredDomains?: { domain: string; enabled: boolean }[];
|
|
51
|
-
/** Mirrors `spectra.config.ts`'s `executionConfig.environmentVariables`. */
|
|
52
|
-
environmentVariables?: Record<string, string>;
|
|
53
|
-
}
|
|
26
|
+
export type RuntimeWorkerConfig = TestSpectraWorkerConfig;
|
|
54
27
|
|
|
55
28
|
/**
|
|
56
29
|
* Cross-cutting shared runtime state injected into the assembled worker script.
|
|
@@ -181,6 +154,7 @@ export interface PlatformDriverBridge extends SpectraBrowserBridge {
|
|
|
181
154
|
dragDrop(sourceSelector: string | ScopedSelector, targetSelector: string, sourceIndex?: number | null): Promise<void>;
|
|
182
155
|
longPress(selector: string | ScopedSelector, duration: number, index?: number | null): Promise<void>;
|
|
183
156
|
scrollIntoView(selector: string | ScopedSelector, index?: number | null): Promise<void>;
|
|
157
|
+
upload(selector: string | ScopedSelector, files: unknown, index?: number | null): Promise<void>;
|
|
184
158
|
|
|
185
159
|
// --- Element State Inspection ---
|
|
186
160
|
getText(selector: string | ScopedSelector, index?: number | null): Promise<string>;
|
|
@@ -216,8 +190,3 @@ export interface PlatformDriverBridge extends SpectraBrowserBridge {
|
|
|
216
190
|
consoleErrors: string[];
|
|
217
191
|
recordedNetwork: CDPNetworkEntry[];
|
|
218
192
|
}
|
|
219
|
-
|
|
220
|
-
declare global {
|
|
221
|
-
// eslint-disable-next-line no-var
|
|
222
|
-
var __TS_RUNTIME__: RuntimeContext;
|
|
223
|
-
}
|
package/src/index.ts
CHANGED
package/src/proto.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type ActionKey =
|
|
|
17
17
|
| 'doubleClick'
|
|
18
18
|
| 'hover'
|
|
19
19
|
| 'dragDrop'
|
|
20
|
+
| 'upload'
|
|
20
21
|
| 'back'
|
|
21
22
|
| 'refresh';
|
|
22
23
|
|
|
@@ -855,6 +856,30 @@ export interface SingleElementProxy extends ElementReceiverAssertions<Promise<vo
|
|
|
855
856
|
*/
|
|
856
857
|
clear(): Promise<void>;
|
|
857
858
|
|
|
859
|
+
/**
|
|
860
|
+
* Uploads one or more files to this file input element (`<input type="file">`).
|
|
861
|
+
* Accepts a file path string, fixture object/path (e.g. `Fixture.sampleImage`), `File` instance, or an array of them.
|
|
862
|
+
*
|
|
863
|
+
* @param files File path, fixture path, or File instance to attach.
|
|
864
|
+
* @example
|
|
865
|
+
* ```ts
|
|
866
|
+
* await Spectra.get('#file-upload').upload(Fixture.sampleImage);
|
|
867
|
+
* await Spectra.get('input[type="file"]').upload('fixtures/sample.pdf');
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
870
|
+
upload(files: unknown): Promise<void>;
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Uploads one or more files to this file input element (alias to `upload()`).
|
|
874
|
+
*
|
|
875
|
+
* @param files File path, fixture path, or File instance to attach.
|
|
876
|
+
* @example
|
|
877
|
+
* ```ts
|
|
878
|
+
* await Spectra.get('#file-upload').uploadFile(Fixture.sampleImage);
|
|
879
|
+
* ```
|
|
880
|
+
*/
|
|
881
|
+
uploadFile(files: unknown): Promise<void>;
|
|
882
|
+
|
|
858
883
|
/**
|
|
859
884
|
* Selects an option in a `<select>` element by its visible text or value.
|
|
860
885
|
*
|
|
@@ -1204,6 +1229,18 @@ export interface QueuedMockResponse {
|
|
|
1204
1229
|
delayMs?: number;
|
|
1205
1230
|
}
|
|
1206
1231
|
|
|
1232
|
+
/**
|
|
1233
|
+
* A transport-agnostic selected mock response: the next `respondOnce` queue item wins (FIFO),
|
|
1234
|
+
* otherwise the rule default. Produced by the shared `consumeMockResponse` helper; each
|
|
1235
|
+
* transport (CDP `Fetch.fulfillRequest`, in-page `Response`, mobile mock proxy) renders it.
|
|
1236
|
+
*/
|
|
1237
|
+
export interface ConsumedMockResponse {
|
|
1238
|
+
body: unknown;
|
|
1239
|
+
statusCode: number;
|
|
1240
|
+
headers: Record<string, string>;
|
|
1241
|
+
delayMs?: number;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1207
1244
|
/**
|
|
1208
1245
|
* Mock rule configuration for CDP & Mobile network request interception.
|
|
1209
1246
|
*/
|