@testspectra/matchers 1.1.0 → 1.1.8-rc.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/matchers",
3
- "version": "1.1.0",
3
+ "version": "1.1.8-rc.1",
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",
@@ -11,15 +11,16 @@
11
11
  "default": "./dist/index.js"
12
12
  }
13
13
  },
14
- "devDependencies": {
15
- "@types/node": "^20.14.0",
16
- "typescript": "^5.6.3"
17
- },
18
- "type": "module",
19
- "license": "SEE LICENSE IN LICENSE.md",
20
14
  "scripts": {
21
15
  "build": "tsc",
22
16
  "watch": "tsc -w",
23
- "typecheck": "tsc --noEmit"
24
- }
25
- }
17
+ "typecheck": "tsc --noEmit",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "catalog:",
22
+ "typescript": "catalog:"
23
+ },
24
+ "type": "module",
25
+ "license": "SEE LICENSE IN LICENSE.md"
26
+ }
@@ -0,0 +1,223 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # PlatformDriverBridge Contract
4
+ *
5
+ * The unified abstract driver contract consumed by `@testspectra/matchers` element proxies,
6
+ * fluent matchers, and the `Spectra` automation object. Every platform-specific driver
7
+ * (`WebCdpDriverBridge`, `AndroidTcpDriverBridge`) implements this interface so that the
8
+ * DSL layer contains zero platform branching.
9
+ */
10
+
11
+ import type {
12
+ CDPNetworkEntry,
13
+ MockInterceptHandle,
14
+ MockRule,
15
+ ScopedSelector,
16
+ ScrollOptions,
17
+ SpectraBrowserBridge,
18
+ SwipeOptions,
19
+ } from './types.js';
20
+
21
+ /**
22
+ * Runtime configuration injected by the Rust orchestrator via `globalThis.__TESTSPECTRA_CONFIG__`.
23
+ */
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
+ }
54
+
55
+ /**
56
+ * Cross-cutting shared runtime state injected into the assembled worker script.
57
+ *
58
+ * The worker runtime is emitted as a single self-contained TypeScript module (concatenated
59
+ * by the Rust orchestrator). This object is created once in the harness prelude and mutated
60
+ * in place (`activeDriver` is bound after the platform bridge is instantiated), so fragments
61
+ * access it lazily at call-time through `globalThis.__TS_RUNTIME__`.
62
+ */
63
+ export interface RuntimeContext {
64
+ config: RuntimeWorkerConfig;
65
+ activeDriver: PlatformDriverBridge | null;
66
+ mobileMockRules: MockRule[];
67
+ lastFocusedSelector: string | null;
68
+ currentTargetItem?: any;
69
+
70
+ emitEvent(tag: string, payload: string): void;
71
+ trackStep<T>(
72
+ category: 'action' | 'assertion' | 'browser',
73
+ payloadObj: Record<string, any>,
74
+ fn: () => Promise<T>,
75
+ ): Promise<T>;
76
+ pollCondition(fn: () => Promise<boolean> | boolean, timeoutMs?: number, intervalMs?: number): Promise<boolean>;
77
+ getImplicitWaitMs(): number;
78
+ getAssertionTimeoutMs(): number;
79
+ matchesUrlPattern(pattern: string, url: string): boolean;
80
+ /** Whether a captured network request/response for `url` should be recorded, per
81
+ * `config.networkMonitoringEnabled` / `config.monitoredDomains`. */
82
+ shouldRecordNetworkEntry(url: string): boolean;
83
+ createMockHandle(rule: MockRule): MockInterceptHandle;
84
+ parseMockRule(patternOrOptions?: any, maybeMethodOrHandler?: any, maybeFixture?: any, maybeOptions?: any): MockRule;
85
+
86
+ // Opt-in duration logging (see core/orchestrator/src/worker/prelude.ts). Present at runtime only
87
+ // when TESTSPECTRA_DURATION_LOG is set; the wrappers are pass-through no-ops otherwise.
88
+ recordDuration?: (label: string, category: string, durationMs: number, extra?: Record<string, unknown>) => void;
89
+ withDuration?: <T>(
90
+ label: string,
91
+ category: string,
92
+ fn: () => Promise<T>,
93
+ extra?: Record<string, unknown>,
94
+ ) => Promise<T>;
95
+ durationLogPath?: string | null;
96
+
97
+ // Matchers runtime factories — registered onto the context by the `tools/matchers` fragments
98
+ // at load time, then consumed by sibling fragments (concatenated single-scope assembly).
99
+ createElementActions?: (selector: string | ScopedSelector, index: number | null) => Record<string, any>;
100
+ createElementState?: (selector: string | ScopedSelector, index: number | null) => Record<string, any>;
101
+ createElementAssertions?: (
102
+ selector: string | ScopedSelector,
103
+ index: number | null,
104
+ state: Record<string, any>,
105
+ ) => Record<string, any>;
106
+ createElementProxy?: (selector: string | ScopedSelector, index?: number | null) => any;
107
+ createCollectionProxy?: (selector: string | ScopedSelector) => any;
108
+ resolveTargetProxy?: (target: any) => any;
109
+ buildSpectra?: () => any;
110
+
111
+ // Platform driver classes — registered by the driver bridge fragments at load time.
112
+ cdpBridgeClass?: any;
113
+ androidBridgeClass?: any;
114
+ mobileMockProxyClass?: any;
115
+ mobileMockProxy?: any;
116
+
117
+ // BDD harness lifecycle hooks & entrypoints — `bdd_runner.ts` owns the hook slots and
118
+ // `runSuite`; `auto_import.ts` owns `bootstrapEnvironment`. Routed through this shared context
119
+ // (rather than plain module-scoped bindings) because the harness fragments are concatenated
120
+ // into one script by the Rust orchestrator but remain separate ES modules for unit testing.
121
+ beforeHook?: (() => Promise<void> | void) | null;
122
+ afterHook?: (() => Promise<void> | void) | null;
123
+ beforeEachHook?: (() => Promise<void> | void) | null;
124
+ afterEachHook?: (() => Promise<void> | void) | null;
125
+ bootstrapEnvironment?: () => Promise<void>;
126
+ runSuite?: () => Promise<void>;
127
+ }
128
+
129
+ /**
130
+ * The unified platform driver contract. It extends the browser surface documented in
131
+ * `SpectraBrowserBridge` (navigation, script execution, network interception, browser-level
132
+ * assertions) with the element interaction and state-inspection methods needed by the DSL,
133
+ * so the `tools/matchers` layer never branches on `activePlatform`.
134
+ */
135
+ export interface PlatformDriverBridge extends SpectraBrowserBridge {
136
+ /** Establishes connection to the underlying runtime endpoint (WebSocket / TCP Socket). */
137
+ connect(): Promise<void>;
138
+
139
+ /** Cleans up open connections, servers, and listeners. */
140
+ close(): Promise<void>;
141
+
142
+ /** Navigates to target URL (Web) or Activity / Deep Link (Android). */
143
+ navigate(targetUrl: string): Promise<void>;
144
+
145
+ /** Navigates one step backward in browser/navigation history. */
146
+ back(): Promise<void>;
147
+
148
+ /** Navigates one step forward in browser/navigation history. */
149
+ forward(): Promise<void>;
150
+
151
+ /** Reloads / refreshes the current active page. */
152
+ refresh(): Promise<void>;
153
+
154
+ /** Sets the browser viewport dimensions (width and height in pixels). */
155
+ setViewport(width: number, height: number): Promise<void>;
156
+
157
+ /** Pauses execution for the given duration in milliseconds. */
158
+ pause(ms: number): Promise<void>;
159
+
160
+ /** Intercepts and mocks HTTP network requests matching the specified pattern or options. */
161
+ intercept(
162
+ patternOrOptions: string | { url: string; method?: string; response?: unknown },
163
+ method?: string,
164
+ fixture?: unknown,
165
+ options?: { statusCode?: number; headers?: Record<string, string>; delayMs?: number },
166
+ ): Promise<MockInterceptHandle>;
167
+
168
+ // --- Element Actions ---
169
+ // `selector` accepts a `ScopedSelector` when resolved via `element.get()`/`.getAll()` chaining
170
+ // (see tools/matchers/src/types.ts); when it is one, its own `.index` is authoritative and the
171
+ // separate `index` argument below is unused. `dragDrop`'s destination stays `string`-only —
172
+ // scoping it is out of scope (see the plan's non-goals).
173
+ click(selector: string | ScopedSelector, index?: number | null): Promise<void>;
174
+ doubleClick(selector: string | ScopedSelector, index?: number | null): Promise<void>;
175
+ rightClick(selector: string | ScopedSelector, index?: number | null): Promise<void>;
176
+ setValue(selector: string | ScopedSelector, value: unknown, index?: number | null): Promise<void>;
177
+ clearValue(selector: string | ScopedSelector, index?: number | null): Promise<void>;
178
+ select(selector: string | ScopedSelector, option: string, index?: number | null): Promise<void>;
179
+ hover(selector: string | ScopedSelector, index?: number | null): Promise<void>;
180
+ focus(selector: string | ScopedSelector, index?: number | null): Promise<void>;
181
+ dragDrop(sourceSelector: string | ScopedSelector, targetSelector: string, sourceIndex?: number | null): Promise<void>;
182
+ longPress(selector: string | ScopedSelector, duration: number, index?: number | null): Promise<void>;
183
+ scrollIntoView(selector: string | ScopedSelector, index?: number | null): Promise<void>;
184
+
185
+ // --- Element State Inspection ---
186
+ getText(selector: string | ScopedSelector, index?: number | null): Promise<string>;
187
+ getValue(selector: string | ScopedSelector, index?: number | null): Promise<string>;
188
+ isDisplayed(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
189
+ isExisting(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
190
+ isEnabled(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
191
+ isSelected(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
192
+ isFocused(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
193
+ isClickable(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
194
+ hasClass(selector: string | ScopedSelector, className: string, index?: number | null): Promise<boolean>;
195
+ getAttribute(selector: string | ScopedSelector, name: string, index?: number | null): Promise<string | null>;
196
+ getCSSProperty(selector: string | ScopedSelector, name: string, index?: number | null): Promise<{ value: string }>;
197
+ count(selector: string | ScopedSelector): Promise<number>;
198
+
199
+ // --- Gestures & Keys ---
200
+ pressKey(key: string | number): Promise<void>;
201
+ scroll(options?: ScrollOptions): Promise<void>;
202
+ swipe(options: SwipeOptions): Promise<void>;
203
+
204
+ /**
205
+ * Grants an Android runtime permission on demand (e.g. right after confirming an in-app
206
+ * rationale dialog), via `adb shell pm grant` on the host. No-op on platforms without an
207
+ * OS-level runtime permission model (e.g. web).
208
+ */
209
+ grantPermission(name: string): Promise<void>;
210
+
211
+ /** Clears all active network interception rules. */
212
+ clearMocks(): void;
213
+
214
+ // --- State accessed by the harness & tests ---
215
+ mockRules: MockRule[];
216
+ consoleErrors: string[];
217
+ recordedNetwork: CDPNetworkEntry[];
218
+ }
219
+
220
+ declare global {
221
+ // eslint-disable-next-line no-var
222
+ var __TS_RUNTIME__: RuntimeContext;
223
+ }
package/src/index.ts CHANGED
@@ -6,4 +6,5 @@
6
6
  */
7
7
 
8
8
  export * from './types.js';
9
+ export * from './contract.js';
9
10
  export * from './proto.js';
package/src/proto.ts CHANGED
@@ -21,6 +21,14 @@ declare global {
21
21
  */
22
22
  var Step: Record<string, any>;
23
23
 
24
+ /**
25
+ * Typed shape of `Spectra.env`. Empty by default — each `spectra.config.ts`
26
+ * `executionConfig.environmentVariables` key is merged in as a `readonly <KEY>: string` member
27
+ * by the CLI's generated `.testspectra/types/env.d.ts` (plain TypeScript interface merging, the
28
+ * same mechanism `@types/node` uses to let projects augment `NodeJS.ProcessEnv`).
29
+ */
30
+ interface SpectraEnv {}
31
+
24
32
  namespace WebdriverIO {
25
33
  interface Browser extends SpectraBrowserBridge {}
26
34
  }