@phyto/driver-tauri 0.1.12 → 0.1.18
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/index.d.ts +139 -11
- package/dist/index.js +139 -15
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* have to depend on the `phyto` package.
|
|
2
|
+
* Locator strategies accepted by **action** driver methods (`click`,
|
|
3
|
+
* `type`, `select`, `waitFor`, etc.). Duplicated here so consumers
|
|
4
|
+
* don't have to depend on the `phyto` package.
|
|
5
5
|
*
|
|
6
6
|
* - `css`: standard CSS selector (the most flexible — also produced
|
|
7
7
|
* when a plain string is passed to any driver method).
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
* `textContent` or `aria-label`/`aria-labelledby`).
|
|
10
10
|
* - `text`: any element whose textContent includes the given string.
|
|
11
11
|
* - `testId`: shorthand for `[data-testid="..."]`.
|
|
12
|
-
* - `
|
|
12
|
+
* - `label`: form control associated with a `<label>`'s `htmlFor` or
|
|
13
|
+
* nested input.
|
|
14
|
+
*
|
|
15
|
+
* Component locators are intentionally absent — they're scoped to
|
|
16
|
+
* the inspection command surface (`InspectionLocator`). See
|
|
17
|
+
* `packages/phyto/src/protocol.ts` for the rationale (action
|
|
18
|
+
* semantics get murky once a component contains multiple form
|
|
19
|
+
* controls; use `{ testId }` / `{ label }` / `{ role }` instead).
|
|
13
20
|
*/
|
|
14
21
|
type Locator = {
|
|
15
22
|
css: string;
|
|
@@ -20,7 +27,22 @@ type Locator = {
|
|
|
20
27
|
text: string;
|
|
21
28
|
} | {
|
|
22
29
|
testId: string;
|
|
23
|
-
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve via a `<label>` element. Distinct from `{ text }` —
|
|
33
|
+
* `label` finds the form control associated with a `<label>`'s
|
|
34
|
+
* `htmlFor` or nested input; `text` matches any element by
|
|
35
|
+
* `textContent`.
|
|
36
|
+
*/
|
|
37
|
+
| {
|
|
38
|
+
label: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Broader locator union for inspection commands (e.g. raw
|
|
42
|
+
* `get-component-state` calls via `app.command()`). Adds the
|
|
43
|
+
* `{ component }` strategy on top of the action set.
|
|
44
|
+
*/
|
|
45
|
+
type InspectionLocator = Locator | {
|
|
24
46
|
component: string;
|
|
25
47
|
props?: Record<string, unknown>;
|
|
26
48
|
};
|
|
@@ -46,6 +68,57 @@ interface TransportResult<T = unknown> {
|
|
|
46
68
|
value?: T;
|
|
47
69
|
error?: string;
|
|
48
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Reason types the harness reports in `FailureContext.reason`. Duplicated
|
|
73
|
+
* here so consumers don't need to depend on the `phyto` package just to
|
|
74
|
+
* narrow `PhytoCommandError.context.reason`.
|
|
75
|
+
*/
|
|
76
|
+
type FailureReason = "not-found" | "not-visible" | "disabled" | "timeout" | "multiple-matches" | "unknown";
|
|
77
|
+
/**
|
|
78
|
+
* Structured failure context attached by the in-page harness. Surfaced
|
|
79
|
+
* on every `PhytoCommandError` thrown by `command()` so callers can
|
|
80
|
+
* inspect element state, the locator, and the visible-testid breadcrumb
|
|
81
|
+
* without parsing the error message.
|
|
82
|
+
*/
|
|
83
|
+
interface PhytoFailureContext {
|
|
84
|
+
reason: FailureReason;
|
|
85
|
+
locator?: unknown;
|
|
86
|
+
elementState?: {
|
|
87
|
+
exists: boolean;
|
|
88
|
+
visible: boolean;
|
|
89
|
+
disabled: boolean;
|
|
90
|
+
tagName?: string;
|
|
91
|
+
textContent?: string;
|
|
92
|
+
visibleTestIds?: string[];
|
|
93
|
+
};
|
|
94
|
+
componentTree?: unknown;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Error thrown by `command()` (and downstream action methods) when the
|
|
98
|
+
* harness returns a structured `{ ok: false }`. Carries the full
|
|
99
|
+
* `FailureContext` envelope as `.context`, plus the name of the adapter
|
|
100
|
+
* that produced the failure as `.handledBy`. The base `.message`
|
|
101
|
+
* includes a short human-readable render of the context — reason,
|
|
102
|
+
* locator, element-state highlights, and the first few visible testids
|
|
103
|
+
* — so even a bare `console.error(err)` shows enough to triage.
|
|
104
|
+
*
|
|
105
|
+
* Before this change the driver threw a bare `Error(result.error)` and
|
|
106
|
+
* silently discarded the context. The new shape is backwards-compatible
|
|
107
|
+
* for code that only reads `.message`, but `instanceof PhytoCommandError`
|
|
108
|
+
* lets richer callers branch on `.context.reason` directly.
|
|
109
|
+
*/
|
|
110
|
+
declare class PhytoCommandError extends Error {
|
|
111
|
+
readonly name = "PhytoCommandError";
|
|
112
|
+
readonly context?: PhytoFailureContext;
|
|
113
|
+
readonly handledBy?: string;
|
|
114
|
+
/**
|
|
115
|
+
* The bare harness-level error string. Preserved so callers that
|
|
116
|
+
* historically pattern-matched on `err.message` substrings have a
|
|
117
|
+
* stable, unprefixed surface to grep against.
|
|
118
|
+
*/
|
|
119
|
+
readonly rawError: string;
|
|
120
|
+
constructor(rawError: string, context: PhytoFailureContext | undefined, handledBy: string | undefined);
|
|
121
|
+
}
|
|
49
122
|
interface WaitOptions {
|
|
50
123
|
/** Timeout in ms (default: driver's defaultTimeout) */
|
|
51
124
|
timeout?: number;
|
|
@@ -108,9 +181,34 @@ declare class TauriDriver {
|
|
|
108
181
|
* Send a declarative command to the in-page harness via POST /command.
|
|
109
182
|
* Returns the structured result from the harness.
|
|
110
183
|
*
|
|
111
|
-
*
|
|
184
|
+
* On a structured harness failure (`{ ok: false }` returned from the
|
|
185
|
+
* adapter chain) this throws a `PhytoCommandError` carrying the full
|
|
186
|
+
* `FailureContext` envelope — element state, locator, and the visible-
|
|
187
|
+
* testid breadcrumb — rather than a bare `Error(result.error)`. The
|
|
188
|
+
* old behaviour silently discarded the context that every adapter
|
|
189
|
+
* goes to the trouble of building, leaving callers with nothing but a
|
|
190
|
+
* short, decontextualised string ("Element not found").
|
|
191
|
+
*
|
|
192
|
+
* Callers that only inspect `err.message` still see a richer message
|
|
193
|
+
* (rendered from the context); callers that want to branch can
|
|
194
|
+
* `instanceof PhytoCommandError` and read `.context.reason` directly.
|
|
112
195
|
*/
|
|
113
196
|
command<T = unknown>(payload: Record<string, unknown>): Promise<T>;
|
|
197
|
+
/**
|
|
198
|
+
* Send a command and return the full `CommandResult` envelope (so
|
|
199
|
+
* callers can inspect routing — `handledBy` — and structured
|
|
200
|
+
* failure context).
|
|
201
|
+
*
|
|
202
|
+
* Unlike `command()`, this does *not* throw on `{ ok: false }`. It
|
|
203
|
+
* throws only when the transport itself fails.
|
|
204
|
+
*/
|
|
205
|
+
commandResult<T = unknown>(payload: Record<string, unknown>): Promise<{
|
|
206
|
+
ok: boolean;
|
|
207
|
+
value?: T;
|
|
208
|
+
error?: string;
|
|
209
|
+
handledBy?: string;
|
|
210
|
+
context?: PhytoFailureContext;
|
|
211
|
+
}>;
|
|
114
212
|
/**
|
|
115
213
|
* Perform the protocol version handshake with the in-page harness.
|
|
116
214
|
* Returns true if the harness is available and compatible.
|
|
@@ -153,15 +251,45 @@ declare class TauriDriver {
|
|
|
153
251
|
* Returns the target that matched, so the caller can branch.
|
|
154
252
|
*/
|
|
155
253
|
waitForAny<T extends LocatorInput>(targets: T[], options?: WaitOptions): Promise<T>;
|
|
156
|
-
/**
|
|
157
|
-
* Wait until an element is no longer in the DOM.
|
|
158
|
-
*/
|
|
159
|
-
waitForGone(target: LocatorInput, options?: WaitOptions): Promise<void>;
|
|
160
254
|
/**
|
|
161
255
|
* Invoke a Tauri command from the webview.
|
|
162
256
|
* Calls the Tauri backend directly via window.__TAURI_INTERNALS__.invoke().
|
|
163
257
|
*/
|
|
164
258
|
invoke<T = unknown>(command: string, args?: Record<string, unknown>): Promise<T>;
|
|
259
|
+
/**
|
|
260
|
+
* Generic "select an option in this dropdown" command.
|
|
261
|
+
*
|
|
262
|
+
* `target` identifies the *select control* — the input the user
|
|
263
|
+
* clicks to open the dropdown — using the same `Locator` vocabulary
|
|
264
|
+
* as every other driver method:
|
|
265
|
+
*
|
|
266
|
+
* await app.select({ label: "Country" }, "Canada");
|
|
267
|
+
* await app.select({ testId: "country-select" }, "Canada");
|
|
268
|
+
* await app.select({ role: "combobox", name: "Country" }, "Canada");
|
|
269
|
+
* await app.select('[data-testid="country-select"]', "Canada");
|
|
270
|
+
*
|
|
271
|
+
* `value` is the *option content* — the visible text of the option
|
|
272
|
+
* to choose (falls back to the option's underlying `value` attribute
|
|
273
|
+
* for native `<select>`).
|
|
274
|
+
*
|
|
275
|
+
* Dispatched via the harness's probe-based adapter chain: the
|
|
276
|
+
* Mantine adapter claims it when the locator resolves to a Mantine
|
|
277
|
+
* combobox; the DOM adapter claims it when the locator resolves to
|
|
278
|
+
* a native `<select>`. Either way the same call shape works.
|
|
279
|
+
*
|
|
280
|
+
* Returns the structured envelope including `handledBy` (the adapter
|
|
281
|
+
* that handled the call) so tests can verify routing.
|
|
282
|
+
*/
|
|
283
|
+
select(target: LocatorInput, value: string, options?: WaitOptions): Promise<SelectCommandResult>;
|
|
284
|
+
}
|
|
285
|
+
/** Structured return shape for `driver.select(...)`. */
|
|
286
|
+
interface SelectCommandResult {
|
|
287
|
+
/** Adapter that handled the command (e.g. `"mantine"`, `"dom"`). */
|
|
288
|
+
handledBy: string;
|
|
289
|
+
/** Component category reported by the adapter (e.g. `"Select"`). */
|
|
290
|
+
component: string;
|
|
291
|
+
/** The text or value actually selected (post-confirmation). */
|
|
292
|
+
selectedValue: string;
|
|
165
293
|
}
|
|
166
294
|
|
|
167
|
-
export { type InteractionOptions, type Locator, type LocatorInput, TauriDriver, type TauriDriverOptions, type TransportResult, type WaitOptions };
|
|
295
|
+
export { type FailureReason, type InspectionLocator, type InteractionOptions, type Locator, type LocatorInput, PhytoCommandError, type PhytoFailureContext, type SelectCommandResult, TauriDriver, type TauriDriverOptions, type TransportResult, type WaitOptions };
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,61 @@ var PROTOCOL_VERSION = 1;
|
|
|
4
4
|
function toLocator(input) {
|
|
5
5
|
return typeof input === "string" ? { css: input } : input;
|
|
6
6
|
}
|
|
7
|
+
var PhytoCommandError = class _PhytoCommandError extends Error {
|
|
8
|
+
name = "PhytoCommandError";
|
|
9
|
+
context;
|
|
10
|
+
handledBy;
|
|
11
|
+
/**
|
|
12
|
+
* The bare harness-level error string. Preserved so callers that
|
|
13
|
+
* historically pattern-matched on `err.message` substrings have a
|
|
14
|
+
* stable, unprefixed surface to grep against.
|
|
15
|
+
*/
|
|
16
|
+
rawError;
|
|
17
|
+
constructor(rawError, context, handledBy) {
|
|
18
|
+
super(renderCommandErrorMessage(rawError, context, handledBy));
|
|
19
|
+
this.rawError = rawError;
|
|
20
|
+
this.context = context;
|
|
21
|
+
this.handledBy = handledBy;
|
|
22
|
+
Object.setPrototypeOf(this, _PhytoCommandError.prototype);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function renderCommandErrorMessage(rawError, context, handledBy) {
|
|
26
|
+
const lines = [rawError];
|
|
27
|
+
if (context?.reason) lines.push(` reason: ${context.reason}`);
|
|
28
|
+
if (context?.locator !== void 0) {
|
|
29
|
+
lines.push(` locator: ${stringifyLocator(context.locator)}`);
|
|
30
|
+
}
|
|
31
|
+
const state = context?.elementState;
|
|
32
|
+
if (state) {
|
|
33
|
+
const parts = [];
|
|
34
|
+
if (state.tagName) parts.push(`tag=${state.tagName}`);
|
|
35
|
+
parts.push(`exists=${state.exists}`);
|
|
36
|
+
parts.push(`visible=${state.visible}`);
|
|
37
|
+
parts.push(`disabled=${state.disabled}`);
|
|
38
|
+
if (state.textContent) {
|
|
39
|
+
parts.push(`text=${JSON.stringify(truncate(state.textContent, 80))}`);
|
|
40
|
+
}
|
|
41
|
+
lines.push(` element: ${parts.join(", ")}`);
|
|
42
|
+
if (state.visibleTestIds && state.visibleTestIds.length > 0) {
|
|
43
|
+
const shown = state.visibleTestIds.slice(0, 10);
|
|
44
|
+
const more = state.visibleTestIds.length - shown.length;
|
|
45
|
+
const tail = more > 0 ? `, \u2026(+${more} more)` : "";
|
|
46
|
+
lines.push(` visibleTestIds: ${shown.join(", ")}${tail}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (handledBy) lines.push(` handledBy: ${handledBy}`);
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
function stringifyLocator(locator) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.stringify(locator);
|
|
55
|
+
} catch {
|
|
56
|
+
return String(locator);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function truncate(s, n) {
|
|
60
|
+
return s.length > n ? `${s.slice(0, n)}\u2026` : s;
|
|
61
|
+
}
|
|
7
62
|
var TauriDriver = class {
|
|
8
63
|
process = null;
|
|
9
64
|
binary;
|
|
@@ -133,7 +188,17 @@ Re-build the Tauri app against a matching version of tauri-plugin-phyto, or upda
|
|
|
133
188
|
* Send a declarative command to the in-page harness via POST /command.
|
|
134
189
|
* Returns the structured result from the harness.
|
|
135
190
|
*
|
|
136
|
-
*
|
|
191
|
+
* On a structured harness failure (`{ ok: false }` returned from the
|
|
192
|
+
* adapter chain) this throws a `PhytoCommandError` carrying the full
|
|
193
|
+
* `FailureContext` envelope — element state, locator, and the visible-
|
|
194
|
+
* testid breadcrumb — rather than a bare `Error(result.error)`. The
|
|
195
|
+
* old behaviour silently discarded the context that every adapter
|
|
196
|
+
* goes to the trouble of building, leaving callers with nothing but a
|
|
197
|
+
* short, decontextualised string ("Element not found").
|
|
198
|
+
*
|
|
199
|
+
* Callers that only inspect `err.message` still see a richer message
|
|
200
|
+
* (rendered from the context); callers that want to branch can
|
|
201
|
+
* `instanceof PhytoCommandError` and read `.context.reason` directly.
|
|
137
202
|
*/
|
|
138
203
|
async command(payload) {
|
|
139
204
|
const res = await fetch(`${this.baseUrl}/command`, {
|
|
@@ -147,12 +212,35 @@ Re-build the Tauri app against a matching version of tauri-plugin-phyto, or upda
|
|
|
147
212
|
}
|
|
148
213
|
const commandResult = result.value;
|
|
149
214
|
if (!commandResult || !commandResult.ok) {
|
|
150
|
-
|
|
151
|
-
|
|
215
|
+
const rawError = commandResult?.error ?? "Command failed with unknown error";
|
|
216
|
+
throw new PhytoCommandError(
|
|
217
|
+
rawError,
|
|
218
|
+
commandResult?.context,
|
|
219
|
+
commandResult?.handledBy
|
|
152
220
|
);
|
|
153
221
|
}
|
|
154
222
|
return commandResult.value;
|
|
155
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Send a command and return the full `CommandResult` envelope (so
|
|
226
|
+
* callers can inspect routing — `handledBy` — and structured
|
|
227
|
+
* failure context).
|
|
228
|
+
*
|
|
229
|
+
* Unlike `command()`, this does *not* throw on `{ ok: false }`. It
|
|
230
|
+
* throws only when the transport itself fails.
|
|
231
|
+
*/
|
|
232
|
+
async commandResult(payload) {
|
|
233
|
+
const res = await fetch(`${this.baseUrl}/command`, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: { "Content-Type": "application/json" },
|
|
236
|
+
body: JSON.stringify(payload)
|
|
237
|
+
});
|
|
238
|
+
const result = await res.json();
|
|
239
|
+
if (!result.ok || !result.value) {
|
|
240
|
+
throw new Error(result.error ?? "Command transport failed");
|
|
241
|
+
}
|
|
242
|
+
return result.value;
|
|
243
|
+
}
|
|
156
244
|
/**
|
|
157
245
|
* Perform the protocol version handshake with the in-page harness.
|
|
158
246
|
* Returns true if the harness is available and compatible.
|
|
@@ -254,18 +342,6 @@ Re-build the Tauri app against a matching version of tauri-plugin-phyto, or upda
|
|
|
254
342
|
});
|
|
255
343
|
return targets[matchedIndex];
|
|
256
344
|
}
|
|
257
|
-
/**
|
|
258
|
-
* Wait until an element is no longer in the DOM.
|
|
259
|
-
*/
|
|
260
|
-
async waitForGone(target, options) {
|
|
261
|
-
await this.command({
|
|
262
|
-
action: "wait-for-gone",
|
|
263
|
-
locator: toLocator(target),
|
|
264
|
-
options: {
|
|
265
|
-
timeout: options?.timeout ?? this.defaultTimeout
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
345
|
/**
|
|
270
346
|
* Invoke a Tauri command from the webview.
|
|
271
347
|
* Calls the Tauri backend directly via window.__TAURI_INTERNALS__.invoke().
|
|
@@ -277,11 +353,59 @@ Re-build the Tauri app against a matching version of tauri-plugin-phyto, or upda
|
|
|
277
353
|
args: args ?? {}
|
|
278
354
|
});
|
|
279
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Generic "select an option in this dropdown" command.
|
|
358
|
+
*
|
|
359
|
+
* `target` identifies the *select control* — the input the user
|
|
360
|
+
* clicks to open the dropdown — using the same `Locator` vocabulary
|
|
361
|
+
* as every other driver method:
|
|
362
|
+
*
|
|
363
|
+
* await app.select({ label: "Country" }, "Canada");
|
|
364
|
+
* await app.select({ testId: "country-select" }, "Canada");
|
|
365
|
+
* await app.select({ role: "combobox", name: "Country" }, "Canada");
|
|
366
|
+
* await app.select('[data-testid="country-select"]', "Canada");
|
|
367
|
+
*
|
|
368
|
+
* `value` is the *option content* — the visible text of the option
|
|
369
|
+
* to choose (falls back to the option's underlying `value` attribute
|
|
370
|
+
* for native `<select>`).
|
|
371
|
+
*
|
|
372
|
+
* Dispatched via the harness's probe-based adapter chain: the
|
|
373
|
+
* Mantine adapter claims it when the locator resolves to a Mantine
|
|
374
|
+
* combobox; the DOM adapter claims it when the locator resolves to
|
|
375
|
+
* a native `<select>`. Either way the same call shape works.
|
|
376
|
+
*
|
|
377
|
+
* Returns the structured envelope including `handledBy` (the adapter
|
|
378
|
+
* that handled the call) so tests can verify routing.
|
|
379
|
+
*/
|
|
380
|
+
async select(target, value, options) {
|
|
381
|
+
const locator = toLocator(target);
|
|
382
|
+
const res = await this.commandResult({
|
|
383
|
+
action: "select",
|
|
384
|
+
locator,
|
|
385
|
+
value,
|
|
386
|
+
options: {
|
|
387
|
+
timeout: options?.timeout ?? this.defaultTimeout
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
if (!res.ok) {
|
|
391
|
+
throw new PhytoCommandError(
|
|
392
|
+
res.error ?? `select() failed with unknown error`,
|
|
393
|
+
res.context,
|
|
394
|
+
res.handledBy
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
handledBy: res.handledBy ?? "unknown",
|
|
399
|
+
component: res.value?.component ?? "Select",
|
|
400
|
+
selectedValue: res.value?.selectedValue ?? value
|
|
401
|
+
};
|
|
402
|
+
}
|
|
280
403
|
};
|
|
281
404
|
function sleep(ms) {
|
|
282
405
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
283
406
|
}
|
|
284
407
|
export {
|
|
408
|
+
PhytoCommandError,
|
|
285
409
|
TauriDriver
|
|
286
410
|
};
|
|
287
411
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { ChildProcess, spawn } from \"node:child_process\";\n\n/** Protocol version — must match the harness. */\nconst PROTOCOL_VERSION = 1;\n\n// ─── Locator types ──────────────────────────────────────────────────\n\n/**\n * Union of supported locator strategies. The same shapes the harness\n * accepts on the wire — duplicated here so users of the driver don't\n * have to depend on the `phyto` package.\n *\n * - `css`: standard CSS selector (the most flexible — also produced\n * when a plain string is passed to any driver method).\n * - `role`: ARIA role with optional accessible name (exact match on\n * `textContent` or `aria-label`/`aria-labelledby`).\n * - `text`: any element whose textContent includes the given string.\n * - `testId`: shorthand for `[data-testid=\"...\"]`.\n * - `component`: framework-aware lookup (currently React only).\n */\nexport type Locator =\n | { css: string }\n | { role: string; name?: string }\n | { text: string }\n | { testId: string }\n | { component: string; props?: Record<string, unknown> };\n\n/** Argument shape accepted by element-targeting driver methods. */\nexport type LocatorInput = string | Locator;\n\n/**\n * Normalize a `LocatorInput` to a `Locator`. A bare string is treated\n * as a CSS selector — preserves backward compatibility with the\n * original string-only API.\n */\nfunction toLocator(input: LocatorInput): Locator {\n return typeof input === \"string\" ? { css: input } : input;\n}\n\n// ─── Types ──────────────────────────────────────────────────────────\n\nexport interface TauriDriverOptions {\n /** Path to the built Tauri binary */\n binary: string;\n /** Port for the automation server (default: 9876) */\n port?: number;\n /** Timeout in ms for waitUntilReady polling (default: 30000) */\n readyTimeout?: number;\n /** Poll interval in ms for waitUntilReady (default: 250) */\n readyInterval?: number;\n /** Additional environment variables for the app process */\n env?: Record<string, string>;\n /** Default timeout in ms for all wait operations (default: 5000) */\n defaultTimeout?: number;\n}\n\n/** Transport-level response wrapper from the Tauri automation server. */\nexport interface TransportResult<T = unknown> {\n ok: boolean;\n value?: T;\n error?: string;\n}\n\nexport interface WaitOptions {\n /** Timeout in ms (default: driver's defaultTimeout) */\n timeout?: number;\n /** Poll interval in ms (default: 100) */\n interval?: number;\n}\n\nexport interface InteractionOptions extends WaitOptions {\n /** If true, also wait for the element to not be disabled (default: false) */\n enabled?: boolean;\n}\n\n// ─── Driver ─────────────────────────────────────────────────────────\n\nexport class TauriDriver {\n private process: ChildProcess | null = null;\n private readonly binary: string;\n private readonly port: number;\n private readonly readyTimeout: number;\n private readonly readyInterval: number;\n private readonly env: Record<string, string>;\n private readonly baseUrl: string;\n private readonly defaultTimeout: number;\n\n constructor(options: TauriDriverOptions) {\n this.binary = options.binary;\n this.port = options.port ?? 9876;\n this.readyTimeout = options.readyTimeout ?? 120_000;\n this.readyInterval = options.readyInterval ?? 250;\n this.env = options.env ?? {};\n this.baseUrl = `http://localhost:${this.port}`;\n this.defaultTimeout = options.defaultTimeout ?? 5000;\n }\n\n /**\n * Launch the Tauri app and wait until the automation server is ready.\n */\n async launch(): Promise<void> {\n this.process = spawn(this.binary, [], {\n stdio: \"pipe\",\n env: { ...process.env, ...this.env },\n });\n\n // Forward stderr for debugging\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n const line = data.toString().trim();\n if (line) {\n process.stderr.write(`[app] ${line}\\n`);\n }\n });\n\n this.process.on(\"error\", (err) => {\n throw new Error(`Failed to launch app binary: ${err.message}`);\n });\n\n await this.waitUntilReady();\n }\n\n /**\n * Poll the /health endpoint until the automation server is responsive,\n * then verify wire-protocol compatibility via /info.\n *\n * Fails fast (without retry) on a version mismatch — re-polling won't\n * fix a stale plugin binary.\n */\n async waitUntilReady(): Promise<void> {\n const deadline = Date.now() + this.readyTimeout;\n\n while (Date.now() < deadline) {\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: AbortSignal.timeout(2000),\n });\n if (res.ok) {\n await this.assertPluginCompatible();\n return;\n }\n } catch {\n // Server not ready yet — keep polling\n }\n await sleep(this.readyInterval);\n }\n\n throw new Error(\n `Automation server did not become ready within ${this.readyTimeout}ms`\n );\n }\n\n /**\n * Fetch plugin metadata (wire protocol + plugin version) from /info.\n * Used by `waitUntilReady` to assert the plugin's protocol version\n * matches this driver's `PROTOCOL_VERSION`.\n */\n async fetchInfo(): Promise<{\n protocol_version: number;\n plugin_version: string;\n plugin: string;\n }> {\n const res = await fetch(`${this.baseUrl}/info`, {\n signal: AbortSignal.timeout(2000),\n });\n if (!res.ok) {\n throw new Error(\n `GET /info failed with status ${res.status}. Is tauri-plugin-phyto installed and registered?`\n );\n }\n return (await res.json()) as {\n protocol_version: number;\n plugin_version: string;\n plugin: string;\n };\n }\n\n /**\n * Verify the running plugin's wire protocol matches this driver's.\n * Throws a clear error on mismatch — both versions named so the user\n * knows which side is stale.\n */\n private async assertPluginCompatible(): Promise<void> {\n const info = await this.fetchInfo();\n if (info.protocol_version !== PROTOCOL_VERSION) {\n throw new Error(\n `Phyto protocol version mismatch:\\n` +\n ` - @phyto/driver-tauri: wire protocol v${PROTOCOL_VERSION}\\n` +\n ` - tauri-plugin-phyto (v${info.plugin_version}): wire protocol v${info.protocol_version}\\n` +\n `\\n` +\n `Re-build the Tauri app against a matching version of tauri-plugin-phyto, ` +\n `or update the npm packages to match the plugin.`\n );\n }\n }\n\n /**\n * Shut down the app process.\n */\n async shutdown(): Promise<void> {\n if (this.process) {\n this.process.kill(\"SIGTERM\");\n\n // Give it a moment to exit gracefully, then force kill\n await Promise.race([\n new Promise<void>((resolve) => {\n this.process?.on(\"exit\", () => resolve());\n }),\n sleep(5000).then(() => {\n this.process?.kill(\"SIGKILL\");\n }),\n ]);\n\n this.process = null;\n }\n }\n\n /**\n * Evaluate arbitrary JavaScript in the webview and return the result.\n * This is an escape hatch for operations not covered by the command\n * protocol (e.g. custom app-specific scripts).\n */\n async evaluate<T = unknown>(script: string): Promise<T> {\n return this.command<T>({\n action: \"eval\",\n script,\n });\n }\n\n /**\n * Send a declarative command to the in-page harness via POST /command.\n * Returns the structured result from the harness.\n *\n * Throws if the harness returns `{ ok: false }`.\n */\n async command<T = unknown>(payload: Record<string, unknown>): Promise<T> {\n const res = await fetch(`${this.baseUrl}/command`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n const result: TransportResult<{ ok: boolean; value?: T; error?: string }> =\n await res.json();\n\n // The /command endpoint wraps the harness result in a TransportResult envelope.\n // result.ok = true means the eval succeeded (harness didn't throw).\n // result.value contains the CommandResult from the harness.\n if (!result.ok) {\n throw new Error(result.error ?? \"Command transport failed\");\n }\n\n const commandResult = result.value;\n if (!commandResult || !commandResult.ok) {\n throw new Error(\n commandResult?.error ?? \"Command failed with unknown error\"\n );\n }\n\n return commandResult.value as T;\n }\n\n /**\n * Perform the protocol version handshake with the in-page harness.\n * Returns true if the harness is available and compatible.\n */\n async handshake(): Promise<void> {\n await this.command({\n action: \"handshake\",\n protocolVersion: PROTOCOL_VERSION,\n });\n }\n\n // ─── Public API ─────────────────────────────────────────────────\n\n /**\n * Click an element matching the given target. Accepts a CSS selector\n * string or any `Locator` object (e.g. `{ role: \"button\", name: \"Next\" }`\n * for text-based button matching, or `{ testId: \"...\" }`).\n * Auto-waits for the element to be visible before clicking.\n * With `{ enabled: true }`, also waits for `!el.disabled` — useful for\n * buttons gated by form validation.\n */\n async click(target: LocatorInput, options?: InteractionOptions): Promise<void> {\n await this.command({\n action: \"click\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n enabled: options?.enabled,\n },\n });\n }\n\n /**\n * Type text into an input/textarea matching the given target.\n * Auto-waits for the element to be visible before typing.\n * Finds the actual `<input>` or `<textarea>` element (even inside wrapper\n * components — useful for Mantine, which puts `data-testid` on the\n * wrapper `<div>`), sets the value using the React-compatible native\n * setter, and dispatches input/change events so React's synthetic\n * event system fires onChange handlers.\n */\n async type(\n target: LocatorInput,\n text: string,\n options?: InteractionOptions\n ): Promise<void> {\n await this.command({\n action: \"type\",\n locator: toLocator(target),\n text,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Get the text content of an element matching the given target.\n * Auto-waits for the element to be visible before reading.\n */\n async textContent(\n target: LocatorInput,\n options?: InteractionOptions\n ): Promise<string> {\n return this.command<string>({\n action: \"text-content\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until an element matching the target is in the DOM and visible.\n */\n async waitFor(target: LocatorInput, options?: WaitOptions): Promise<void> {\n await this.command({\n action: \"wait-for\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until an element matching the target contains the specified text.\n */\n async waitForText(\n target: LocatorInput,\n text: string,\n options?: WaitOptions\n ): Promise<void> {\n await this.command({\n action: \"wait-for-text\",\n locator: toLocator(target),\n text,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until any of the given targets matches a visible element.\n * Returns the target that matched, so the caller can branch.\n */\n async waitForAny<T extends LocatorInput>(\n targets: T[],\n options?: WaitOptions\n ): Promise<T> {\n const locators = targets.map(toLocator);\n const matchedIndex = await this.command<number>({\n action: \"wait-for-any\",\n locators,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n return targets[matchedIndex];\n }\n\n /**\n * Wait until an element is no longer in the DOM.\n */\n async waitForGone(\n target: LocatorInput,\n options?: WaitOptions\n ): Promise<void> {\n await this.command({\n action: \"wait-for-gone\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Invoke a Tauri command from the webview.\n * Calls the Tauri backend directly via window.__TAURI_INTERNALS__.invoke().\n */\n async invoke<T = unknown>(\n command: string,\n args?: Record<string, unknown>\n ): Promise<T> {\n return this.command<T>({\n action: \"invoke\",\n command,\n args: args ?? {},\n });\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { ChildProcess, spawn } from \"node:child_process\";\n\n/** Protocol version — must match the harness. */\nconst PROTOCOL_VERSION = 1;\n\n// ─── Locator types ──────────────────────────────────────────────────\n\n/**\n * Locator strategies accepted by **action** driver methods (`click`,\n * `type`, `select`, `waitFor`, etc.). Duplicated here so consumers\n * don't have to depend on the `phyto` package.\n *\n * - `css`: standard CSS selector (the most flexible — also produced\n * when a plain string is passed to any driver method).\n * - `role`: ARIA role with optional accessible name (exact match on\n * `textContent` or `aria-label`/`aria-labelledby`).\n * - `text`: any element whose textContent includes the given string.\n * - `testId`: shorthand for `[data-testid=\"...\"]`.\n * - `label`: form control associated with a `<label>`'s `htmlFor` or\n * nested input.\n *\n * Component locators are intentionally absent — they're scoped to\n * the inspection command surface (`InspectionLocator`). See\n * `packages/phyto/src/protocol.ts` for the rationale (action\n * semantics get murky once a component contains multiple form\n * controls; use `{ testId }` / `{ label }` / `{ role }` instead).\n */\nexport type Locator =\n | { css: string }\n | { role: string; name?: string }\n | { text: string }\n | { testId: string }\n /**\n * Resolve via a `<label>` element. Distinct from `{ text }` —\n * `label` finds the form control associated with a `<label>`'s\n * `htmlFor` or nested input; `text` matches any element by\n * `textContent`.\n */\n | { label: string };\n\n/**\n * Broader locator union for inspection commands (e.g. raw\n * `get-component-state` calls via `app.command()`). Adds the\n * `{ component }` strategy on top of the action set.\n */\nexport type InspectionLocator =\n | Locator\n | { component: string; props?: Record<string, unknown> };\n\n/** Argument shape accepted by element-targeting driver methods. */\nexport type LocatorInput = string | Locator;\n\n/**\n * Normalize a `LocatorInput` to a `Locator`. A bare string is treated\n * as a CSS selector — preserves backward compatibility with the\n * original string-only API.\n */\nfunction toLocator(input: LocatorInput): Locator {\n return typeof input === \"string\" ? { css: input } : input;\n}\n\n// ─── Types ──────────────────────────────────────────────────────────\n\nexport interface TauriDriverOptions {\n /** Path to the built Tauri binary */\n binary: string;\n /** Port for the automation server (default: 9876) */\n port?: number;\n /** Timeout in ms for waitUntilReady polling (default: 30000) */\n readyTimeout?: number;\n /** Poll interval in ms for waitUntilReady (default: 250) */\n readyInterval?: number;\n /** Additional environment variables for the app process */\n env?: Record<string, string>;\n /** Default timeout in ms for all wait operations (default: 5000) */\n defaultTimeout?: number;\n}\n\n/** Transport-level response wrapper from the Tauri automation server. */\nexport interface TransportResult<T = unknown> {\n ok: boolean;\n value?: T;\n error?: string;\n}\n\n/**\n * Reason types the harness reports in `FailureContext.reason`. Duplicated\n * here so consumers don't need to depend on the `phyto` package just to\n * narrow `PhytoCommandError.context.reason`.\n */\nexport type FailureReason =\n | \"not-found\"\n | \"not-visible\"\n | \"disabled\"\n | \"timeout\"\n | \"multiple-matches\"\n | \"unknown\";\n\n/**\n * Structured failure context attached by the in-page harness. Surfaced\n * on every `PhytoCommandError` thrown by `command()` so callers can\n * inspect element state, the locator, and the visible-testid breadcrumb\n * without parsing the error message.\n */\nexport interface PhytoFailureContext {\n reason: FailureReason;\n locator?: unknown;\n elementState?: {\n exists: boolean;\n visible: boolean;\n disabled: boolean;\n tagName?: string;\n textContent?: string;\n visibleTestIds?: string[];\n };\n componentTree?: unknown;\n}\n\n/**\n * Error thrown by `command()` (and downstream action methods) when the\n * harness returns a structured `{ ok: false }`. Carries the full\n * `FailureContext` envelope as `.context`, plus the name of the adapter\n * that produced the failure as `.handledBy`. The base `.message`\n * includes a short human-readable render of the context — reason,\n * locator, element-state highlights, and the first few visible testids\n * — so even a bare `console.error(err)` shows enough to triage.\n *\n * Before this change the driver threw a bare `Error(result.error)` and\n * silently discarded the context. The new shape is backwards-compatible\n * for code that only reads `.message`, but `instanceof PhytoCommandError`\n * lets richer callers branch on `.context.reason` directly.\n */\nexport class PhytoCommandError extends Error {\n override readonly name = \"PhytoCommandError\";\n readonly context?: PhytoFailureContext;\n readonly handledBy?: string;\n /**\n * The bare harness-level error string. Preserved so callers that\n * historically pattern-matched on `err.message` substrings have a\n * stable, unprefixed surface to grep against.\n */\n readonly rawError: string;\n\n constructor(\n rawError: string,\n context: PhytoFailureContext | undefined,\n handledBy: string | undefined\n ) {\n super(renderCommandErrorMessage(rawError, context, handledBy));\n this.rawError = rawError;\n this.context = context;\n this.handledBy = handledBy;\n // Restore the standard prototype chain after `super` so\n // `instanceof PhytoCommandError` works under both modern and\n // downlevelled output.\n Object.setPrototypeOf(this, PhytoCommandError.prototype);\n }\n}\n\n/**\n * Render a one-block diagnostic message from the structured failure\n * context. Format is intentionally compact and grep-friendly:\n *\n * <rawError>\n * reason: <reason>\n * locator: <stringified locator>\n * element: <tagName>, visible=<bool>, disabled=<bool>, text=\"...\"\n * visibleTestIds: a, b, c, …(N more)\n * handledBy: <adapter>\n *\n * Lines are omitted when the corresponding field is absent. Keeps the\n * message single-line-able for unstructured log sinks (every line is\n * prefixed with two spaces — no embedded carriage returns).\n */\nfunction renderCommandErrorMessage(\n rawError: string,\n context: PhytoFailureContext | undefined,\n handledBy: string | undefined\n): string {\n const lines: string[] = [rawError];\n if (context?.reason) lines.push(` reason: ${context.reason}`);\n if (context?.locator !== undefined) {\n lines.push(` locator: ${stringifyLocator(context.locator)}`);\n }\n const state = context?.elementState;\n if (state) {\n const parts: string[] = [];\n if (state.tagName) parts.push(`tag=${state.tagName}`);\n parts.push(`exists=${state.exists}`);\n parts.push(`visible=${state.visible}`);\n parts.push(`disabled=${state.disabled}`);\n if (state.textContent) {\n parts.push(`text=${JSON.stringify(truncate(state.textContent, 80))}`);\n }\n lines.push(` element: ${parts.join(\", \")}`);\n if (state.visibleTestIds && state.visibleTestIds.length > 0) {\n const shown = state.visibleTestIds.slice(0, 10);\n const more = state.visibleTestIds.length - shown.length;\n const tail = more > 0 ? `, …(+${more} more)` : \"\";\n lines.push(` visibleTestIds: ${shown.join(\", \")}${tail}`);\n }\n }\n if (handledBy) lines.push(` handledBy: ${handledBy}`);\n return lines.join(\"\\n\");\n}\n\nfunction stringifyLocator(locator: unknown): string {\n try {\n return JSON.stringify(locator);\n } catch {\n return String(locator);\n }\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n)}…` : s;\n}\n\nexport interface WaitOptions {\n /** Timeout in ms (default: driver's defaultTimeout) */\n timeout?: number;\n /** Poll interval in ms (default: 100) */\n interval?: number;\n}\n\nexport interface InteractionOptions extends WaitOptions {\n /** If true, also wait for the element to not be disabled (default: false) */\n enabled?: boolean;\n}\n\n// ─── Driver ─────────────────────────────────────────────────────────\n\nexport class TauriDriver {\n private process: ChildProcess | null = null;\n private readonly binary: string;\n private readonly port: number;\n private readonly readyTimeout: number;\n private readonly readyInterval: number;\n private readonly env: Record<string, string>;\n private readonly baseUrl: string;\n private readonly defaultTimeout: number;\n\n constructor(options: TauriDriverOptions) {\n this.binary = options.binary;\n this.port = options.port ?? 9876;\n this.readyTimeout = options.readyTimeout ?? 120_000;\n this.readyInterval = options.readyInterval ?? 250;\n this.env = options.env ?? {};\n this.baseUrl = `http://localhost:${this.port}`;\n this.defaultTimeout = options.defaultTimeout ?? 5000;\n }\n\n /**\n * Launch the Tauri app and wait until the automation server is ready.\n */\n async launch(): Promise<void> {\n this.process = spawn(this.binary, [], {\n stdio: \"pipe\",\n env: { ...process.env, ...this.env },\n });\n\n // Forward stderr for debugging\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n const line = data.toString().trim();\n if (line) {\n process.stderr.write(`[app] ${line}\\n`);\n }\n });\n\n this.process.on(\"error\", (err) => {\n throw new Error(`Failed to launch app binary: ${err.message}`);\n });\n\n await this.waitUntilReady();\n }\n\n /**\n * Poll the /health endpoint until the automation server is responsive,\n * then verify wire-protocol compatibility via /info.\n *\n * Fails fast (without retry) on a version mismatch — re-polling won't\n * fix a stale plugin binary.\n */\n async waitUntilReady(): Promise<void> {\n const deadline = Date.now() + this.readyTimeout;\n\n while (Date.now() < deadline) {\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: AbortSignal.timeout(2000),\n });\n if (res.ok) {\n await this.assertPluginCompatible();\n return;\n }\n } catch {\n // Server not ready yet — keep polling\n }\n await sleep(this.readyInterval);\n }\n\n throw new Error(\n `Automation server did not become ready within ${this.readyTimeout}ms`\n );\n }\n\n /**\n * Fetch plugin metadata (wire protocol + plugin version) from /info.\n * Used by `waitUntilReady` to assert the plugin's protocol version\n * matches this driver's `PROTOCOL_VERSION`.\n */\n async fetchInfo(): Promise<{\n protocol_version: number;\n plugin_version: string;\n plugin: string;\n }> {\n const res = await fetch(`${this.baseUrl}/info`, {\n signal: AbortSignal.timeout(2000),\n });\n if (!res.ok) {\n throw new Error(\n `GET /info failed with status ${res.status}. Is tauri-plugin-phyto installed and registered?`\n );\n }\n return (await res.json()) as {\n protocol_version: number;\n plugin_version: string;\n plugin: string;\n };\n }\n\n /**\n * Verify the running plugin's wire protocol matches this driver's.\n * Throws a clear error on mismatch — both versions named so the user\n * knows which side is stale.\n */\n private async assertPluginCompatible(): Promise<void> {\n const info = await this.fetchInfo();\n if (info.protocol_version !== PROTOCOL_VERSION) {\n throw new Error(\n `Phyto protocol version mismatch:\\n` +\n ` - @phyto/driver-tauri: wire protocol v${PROTOCOL_VERSION}\\n` +\n ` - tauri-plugin-phyto (v${info.plugin_version}): wire protocol v${info.protocol_version}\\n` +\n `\\n` +\n `Re-build the Tauri app against a matching version of tauri-plugin-phyto, ` +\n `or update the npm packages to match the plugin.`\n );\n }\n }\n\n /**\n * Shut down the app process.\n */\n async shutdown(): Promise<void> {\n if (this.process) {\n this.process.kill(\"SIGTERM\");\n\n // Give it a moment to exit gracefully, then force kill\n await Promise.race([\n new Promise<void>((resolve) => {\n this.process?.on(\"exit\", () => resolve());\n }),\n sleep(5000).then(() => {\n this.process?.kill(\"SIGKILL\");\n }),\n ]);\n\n this.process = null;\n }\n }\n\n /**\n * Evaluate arbitrary JavaScript in the webview and return the result.\n * This is an escape hatch for operations not covered by the command\n * protocol (e.g. custom app-specific scripts).\n */\n async evaluate<T = unknown>(script: string): Promise<T> {\n return this.command<T>({\n action: \"eval\",\n script,\n });\n }\n\n /**\n * Send a declarative command to the in-page harness via POST /command.\n * Returns the structured result from the harness.\n *\n * On a structured harness failure (`{ ok: false }` returned from the\n * adapter chain) this throws a `PhytoCommandError` carrying the full\n * `FailureContext` envelope — element state, locator, and the visible-\n * testid breadcrumb — rather than a bare `Error(result.error)`. The\n * old behaviour silently discarded the context that every adapter\n * goes to the trouble of building, leaving callers with nothing but a\n * short, decontextualised string (\"Element not found\").\n *\n * Callers that only inspect `err.message` still see a richer message\n * (rendered from the context); callers that want to branch can\n * `instanceof PhytoCommandError` and read `.context.reason` directly.\n */\n async command<T = unknown>(payload: Record<string, unknown>): Promise<T> {\n const res = await fetch(`${this.baseUrl}/command`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n const result: TransportResult<{\n ok: boolean;\n value?: T;\n error?: string;\n context?: PhytoFailureContext;\n handledBy?: string;\n }> = await res.json();\n\n // The /command endpoint wraps the harness result in a TransportResult envelope.\n // result.ok = true means the eval succeeded (harness didn't throw).\n // result.value contains the CommandResult from the harness.\n if (!result.ok) {\n throw new Error(result.error ?? \"Command transport failed\");\n }\n\n const commandResult = result.value;\n if (!commandResult || !commandResult.ok) {\n const rawError =\n commandResult?.error ?? \"Command failed with unknown error\";\n throw new PhytoCommandError(\n rawError,\n commandResult?.context,\n commandResult?.handledBy\n );\n }\n\n return commandResult.value as T;\n }\n\n /**\n * Send a command and return the full `CommandResult` envelope (so\n * callers can inspect routing — `handledBy` — and structured\n * failure context).\n *\n * Unlike `command()`, this does *not* throw on `{ ok: false }`. It\n * throws only when the transport itself fails.\n */\n async commandResult<T = unknown>(\n payload: Record<string, unknown>\n ): Promise<{\n ok: boolean;\n value?: T;\n error?: string;\n handledBy?: string;\n context?: PhytoFailureContext;\n }> {\n const res = await fetch(`${this.baseUrl}/command`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n const result: TransportResult<{\n ok: boolean;\n value?: T;\n error?: string;\n handledBy?: string;\n context?: PhytoFailureContext;\n }> = await res.json();\n if (!result.ok || !result.value) {\n throw new Error(result.error ?? \"Command transport failed\");\n }\n return result.value;\n }\n\n /**\n * Perform the protocol version handshake with the in-page harness.\n * Returns true if the harness is available and compatible.\n */\n async handshake(): Promise<void> {\n await this.command({\n action: \"handshake\",\n protocolVersion: PROTOCOL_VERSION,\n });\n }\n\n // ─── Public API ─────────────────────────────────────────────────\n\n /**\n * Click an element matching the given target. Accepts a CSS selector\n * string or any `Locator` object (e.g. `{ role: \"button\", name: \"Next\" }`\n * for text-based button matching, or `{ testId: \"...\" }`).\n * Auto-waits for the element to be visible before clicking.\n * With `{ enabled: true }`, also waits for `!el.disabled` — useful for\n * buttons gated by form validation.\n */\n async click(target: LocatorInput, options?: InteractionOptions): Promise<void> {\n await this.command({\n action: \"click\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n enabled: options?.enabled,\n },\n });\n }\n\n /**\n * Type text into an input/textarea matching the given target.\n * Auto-waits for the element to be visible before typing.\n * Finds the actual `<input>` or `<textarea>` element (even inside wrapper\n * components — useful for Mantine, which puts `data-testid` on the\n * wrapper `<div>`), sets the value using the React-compatible native\n * setter, and dispatches input/change events so React's synthetic\n * event system fires onChange handlers.\n */\n async type(\n target: LocatorInput,\n text: string,\n options?: InteractionOptions\n ): Promise<void> {\n await this.command({\n action: \"type\",\n locator: toLocator(target),\n text,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Get the text content of an element matching the given target.\n * Auto-waits for the element to be visible before reading.\n */\n async textContent(\n target: LocatorInput,\n options?: InteractionOptions\n ): Promise<string> {\n return this.command<string>({\n action: \"text-content\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until an element matching the target is in the DOM and visible.\n */\n async waitFor(target: LocatorInput, options?: WaitOptions): Promise<void> {\n await this.command({\n action: \"wait-for\",\n locator: toLocator(target),\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until an element matching the target contains the specified text.\n */\n async waitForText(\n target: LocatorInput,\n text: string,\n options?: WaitOptions\n ): Promise<void> {\n await this.command({\n action: \"wait-for-text\",\n locator: toLocator(target),\n text,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n }\n\n /**\n * Wait until any of the given targets matches a visible element.\n * Returns the target that matched, so the caller can branch.\n */\n async waitForAny<T extends LocatorInput>(\n targets: T[],\n options?: WaitOptions\n ): Promise<T> {\n const locators = targets.map(toLocator);\n const matchedIndex = await this.command<number>({\n action: \"wait-for-any\",\n locators,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n return targets[matchedIndex];\n }\n\n /**\n * Invoke a Tauri command from the webview.\n * Calls the Tauri backend directly via window.__TAURI_INTERNALS__.invoke().\n */\n async invoke<T = unknown>(\n command: string,\n args?: Record<string, unknown>\n ): Promise<T> {\n return this.command<T>({\n action: \"invoke\",\n command,\n args: args ?? {},\n });\n }\n\n /**\n * Generic \"select an option in this dropdown\" command.\n *\n * `target` identifies the *select control* — the input the user\n * clicks to open the dropdown — using the same `Locator` vocabulary\n * as every other driver method:\n *\n * await app.select({ label: \"Country\" }, \"Canada\");\n * await app.select({ testId: \"country-select\" }, \"Canada\");\n * await app.select({ role: \"combobox\", name: \"Country\" }, \"Canada\");\n * await app.select('[data-testid=\"country-select\"]', \"Canada\");\n *\n * `value` is the *option content* — the visible text of the option\n * to choose (falls back to the option's underlying `value` attribute\n * for native `<select>`).\n *\n * Dispatched via the harness's probe-based adapter chain: the\n * Mantine adapter claims it when the locator resolves to a Mantine\n * combobox; the DOM adapter claims it when the locator resolves to\n * a native `<select>`. Either way the same call shape works.\n *\n * Returns the structured envelope including `handledBy` (the adapter\n * that handled the call) so tests can verify routing.\n */\n async select(\n target: LocatorInput,\n value: string,\n options?: WaitOptions\n ): Promise<SelectCommandResult> {\n const locator = toLocator(target);\n const res = await this.commandResult<{\n component: string;\n selectedValue: string;\n }>({\n action: \"select\",\n locator,\n value,\n options: {\n timeout: options?.timeout ?? this.defaultTimeout,\n },\n });\n if (!res.ok) {\n throw new PhytoCommandError(\n res.error ?? `select() failed with unknown error`,\n res.context,\n res.handledBy\n );\n }\n return {\n handledBy: res.handledBy ?? \"unknown\",\n component: res.value?.component ?? \"Select\",\n selectedValue: res.value?.selectedValue ?? value,\n };\n }\n}\n\n/** Structured return shape for `driver.select(...)`. */\nexport interface SelectCommandResult {\n /** Adapter that handled the command (e.g. `\"mantine\"`, `\"dom\"`). */\n handledBy: string;\n /** Component category reported by the adapter (e.g. `\"Select\"`). */\n component: string;\n /** The text or value actually selected (post-confirmation). */\n selectedValue: string;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phyto/driver-tauri",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Tauri driver for Phyto — controls a running Tauri app via the tauri-plugin-phyto automation socket.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Coniferous <hello@coniferous.dev>",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"type": "module",
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"main": "./dist/index.js",
|