@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/proto.d.ts CHANGED
@@ -29,3 +29,4 @@ declare global {
29
29
  }
30
30
  }
31
31
  }
32
+ export type SpectraEnv = globalThis.SpectraEnv;
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 payload = typeof commandText === 'object' ? JSON.stringify(commandText) : commandText;
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
- if (typeof $$ !== 'undefined') {
88
- const elements = await $$(this._selector);
89
- return elements[index];
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
- if (typeof $$ !== 'undefined') {
121
- const elements = await $$(this._selector);
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
- return arr[arr.length - 1];
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
  })();
@@ -90,11 +90,11 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
90
90
  */
91
91
  select(value: string): this;
92
92
  /**
93
- * Moves the mouse cursor over the target element (hover).
93
+ * Hovers over the target element.
94
94
  *
95
95
  * @example
96
96
  * ```ts
97
- * await Spectra.get("#dropdown-menu-trigger").hover();
97
+ * await Spectra.get("#dropdown-trigger").hover();
98
98
  * ```
99
99
  * @returns Current runner instance for method chaining.
100
100
  */
@@ -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
- await el.clearValue();
123
+ if (typeof el.clearValue === 'function')
124
+ await el.clearValue();
125
+ else if (typeof el.clear === 'function')
126
+ await el.clear();
124
127
  }
125
- await el.setValue(value);
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
- await el.clearValue();
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
- await el.selectByVisibleText(value);
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
- * Moves the mouse cursor over the target element (hover).
181
+ * Hovers over the target element.
168
182
  *
169
183
  * @example
170
184
  * ```ts
171
- * await Spectra.get("#dropdown-menu-trigger").hover();
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
- await el.moveTo();
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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/matchers",
3
- "version": "1.1.11-rc.5",
3
+ "version": "1.1.13",
4
4
  "description": "Canonical TypeScript contracts and ambient type definitions for TestSpectra",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
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
- * Runtime configuration injected by the Rust orchestrator via `globalThis.__TESTSPECTRA_CONFIG__`.
23
+ * @deprecated Renamed to `TestSpectraWorkerConfig` (now shared by E2E and component testing).
24
+ * Kept as an alias for backwards compatibility.
23
25
  */
24
- export interface RuntimeWorkerConfig {
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
@@ -6,5 +6,6 @@
6
6
  */
7
7
 
8
8
  export * from './types.js';
9
+ export * from './worker_config.js';
9
10
  export * from './contract.js';
10
11
  export * from './proto.js';
package/src/proto.ts CHANGED
@@ -33,3 +33,5 @@ declare global {
33
33
  interface Browser extends SpectraBrowserBridge {}
34
34
  }
35
35
  }
36
+
37
+ export type SpectraEnv = globalThis.SpectraEnv;
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
  */