prowl-tools 0.1.8 → 0.1.10
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/README.md +103 -14
- package/dist/{chunk-CWLRDV5P.js → chunk-SRWAF2RI.js} +180 -19
- package/dist/chunk-SRWAF2RI.js.map +1 -0
- package/dist/{chunk-JFJQNJSJ.js → chunk-TTZ2IL3P.js} +27 -1
- package/dist/chunk-TTZ2IL3P.js.map +1 -0
- package/dist/index.cjs +843 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +622 -43
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +204 -17
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +72 -1
- package/dist/lib.d.ts +72 -1
- package/dist/lib.js +2 -2
- package/dist/{loader-JTHA4BYG.js → loader-CAE4FPBK.js} +2 -2
- package/examples/hunts/form.yml +53 -0
- package/examples/hunts/macos-hello.yml +49 -0
- package/package.json +4 -2
- package/dist/chunk-CWLRDV5P.js.map +0 -1
- package/dist/chunk-JFJQNJSJ.js.map +0 -1
- /package/dist/{loader-JTHA4BYG.js.map → loader-CAE4FPBK.js.map} +0 -0
package/dist/lib.d.cts
CHANGED
|
@@ -115,6 +115,12 @@ type HistoryEntry = {
|
|
|
115
115
|
durationMs: number;
|
|
116
116
|
startedAt: string;
|
|
117
117
|
runDir?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Number of retry attempts beyond the first for this run (PROWL-033). Absent or
|
|
120
|
+
* `0` when the run did not retry — kept optional so older `history.json` files and
|
|
121
|
+
* runs of hunts without a `retry` block keep parsing unchanged.
|
|
122
|
+
*/
|
|
123
|
+
retries?: number;
|
|
118
124
|
};
|
|
119
125
|
type HistoryFile = {
|
|
120
126
|
entries: HistoryEntry[];
|
|
@@ -177,6 +183,13 @@ type WaitForNetworkIdleStep = {
|
|
|
177
183
|
timeout?: number;
|
|
178
184
|
};
|
|
179
185
|
};
|
|
186
|
+
type WaitForResponseStep = {
|
|
187
|
+
waitForResponse: {
|
|
188
|
+
url: string;
|
|
189
|
+
status?: number;
|
|
190
|
+
timeout?: number;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
180
193
|
type SelectOptionStep = {
|
|
181
194
|
selectOption: {
|
|
182
195
|
selector: string;
|
|
@@ -299,7 +312,7 @@ type AssertScreenshotStep = {
|
|
|
299
312
|
type AssertWithAiStep = {
|
|
300
313
|
assertWithAI: string;
|
|
301
314
|
};
|
|
302
|
-
type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
|
|
315
|
+
type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | WaitForResponseStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
|
|
303
316
|
type Assertion = {
|
|
304
317
|
selectorExists: string;
|
|
305
318
|
} | {
|
|
@@ -344,6 +357,25 @@ type TraceCorrelation = {
|
|
|
344
357
|
traceId: string;
|
|
345
358
|
header: string;
|
|
346
359
|
};
|
|
360
|
+
/**
|
|
361
|
+
* One attempt of a retried hunt (PROWL-033). Recorded per attempt so a passed-on-retry
|
|
362
|
+
* run keeps the reason each earlier attempt failed, helping tell a flaky test apart from
|
|
363
|
+
* a slow environment or a real regression.
|
|
364
|
+
*/
|
|
365
|
+
type RetryAttempt = {
|
|
366
|
+
/** 1-based attempt number (attempt 1 is the initial run). */
|
|
367
|
+
attempt: number;
|
|
368
|
+
status: "pass" | "fail";
|
|
369
|
+
durationMs: number;
|
|
370
|
+
/** The first failing step on this attempt; absent when the attempt passed. */
|
|
371
|
+
failedStep?: {
|
|
372
|
+
/** 0-based index into the hunt's steps. */
|
|
373
|
+
index: number;
|
|
374
|
+
type: string;
|
|
375
|
+
};
|
|
376
|
+
/** Failure reason for this attempt; absent when it passed. */
|
|
377
|
+
error?: string;
|
|
378
|
+
};
|
|
347
379
|
type RunResult = {
|
|
348
380
|
status: "pass" | "fail";
|
|
349
381
|
exitCode: 0 | 1;
|
|
@@ -355,6 +387,17 @@ type RunResult = {
|
|
|
355
387
|
assertions: AssertionResult[];
|
|
356
388
|
artifacts: RunArtifacts;
|
|
357
389
|
traceCorrelations?: TraceCorrelation[];
|
|
390
|
+
/**
|
|
391
|
+
* Per-attempt diagnostics (PROWL-033), present only when the hunt used `retry` and
|
|
392
|
+
* more than one attempt ran. Absent when no retry occurred so existing consumers and
|
|
393
|
+
* older run artifacts keep parsing. The last entry always matches the final result.
|
|
394
|
+
*/
|
|
395
|
+
retryHistory?: RetryAttempt[];
|
|
396
|
+
/**
|
|
397
|
+
* Human-readable one-line retry outcome (PROWL-033), e.g. "Passed on attempt 2 of 3
|
|
398
|
+
* — first failure: navigate (timeout)". Present only alongside `retryHistory`.
|
|
399
|
+
*/
|
|
400
|
+
retrySummary?: string;
|
|
358
401
|
};
|
|
359
402
|
type CiHuntResult = {
|
|
360
403
|
hunt: string;
|
|
@@ -477,6 +520,10 @@ interface SessionDriver {
|
|
|
477
520
|
waitForNetworkIdle(options?: {
|
|
478
521
|
timeout?: number;
|
|
479
522
|
}): Promise<void>;
|
|
523
|
+
/** Resolve once a network response satisfies `predicate`, or reject on timeout. */
|
|
524
|
+
waitForResponse(predicate: (response: DriverResponse) => boolean, options?: {
|
|
525
|
+
timeout?: number;
|
|
526
|
+
}): Promise<void>;
|
|
480
527
|
evaluate<R = unknown, A = unknown>(pageFunction: string | ((arg: A) => R | Promise<R>), arg?: A): Promise<R>;
|
|
481
528
|
screenshot(options: {
|
|
482
529
|
path: string;
|
|
@@ -605,15 +652,38 @@ type ResolveHelperOptions = {
|
|
|
605
652
|
declare function resolveHelperBinary(env?: NodeJS.ProcessEnv, options?: ResolveHelperOptions): string;
|
|
606
653
|
/** Default per-request deadline for the helper transport. */
|
|
607
654
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
655
|
+
/**
|
|
656
|
+
* A server-initiated event line from the helper (ARCH-008): a JSON object with
|
|
657
|
+
* an `event` discriminant and **no** `id`, so it is unambiguously distinct from
|
|
658
|
+
* an id-matched command response. Emitted, for example, when an AXObserver
|
|
659
|
+
* notification fires during a `waitFor`/`openMenu` wait.
|
|
660
|
+
*/
|
|
661
|
+
type MacHelperEvent = Record<string, unknown> & {
|
|
662
|
+
event: string;
|
|
663
|
+
};
|
|
608
664
|
type SpawnMacHelperOptions = {
|
|
609
665
|
/** Per-request deadline; a request that gets no response by then rejects. */
|
|
610
666
|
requestTimeoutMs?: number;
|
|
667
|
+
/**
|
|
668
|
+
* Optional sink for server-initiated event lines. Events are informational —
|
|
669
|
+
* waits are resolved helper-side by the id-matched response — so they are
|
|
670
|
+
* forwarded here (if provided) and otherwise dropped, never touching the
|
|
671
|
+
* pending-request map.
|
|
672
|
+
*/
|
|
673
|
+
onEvent?: (event: MacHelperEvent) => void;
|
|
674
|
+
/**
|
|
675
|
+
* Optional diagnostic sink for event handler failures. Defaults to stderr so a
|
|
676
|
+
* bad sink is visible without allowing it to break helper transport.
|
|
677
|
+
*/
|
|
678
|
+
onEventError?: (message: string) => void;
|
|
611
679
|
};
|
|
612
680
|
/** A {@link MacHelperClient} backed by a spawned `prowl-macdriver serve` process. */
|
|
613
681
|
declare class SpawnMacHelperClient implements MacHelperClient {
|
|
614
682
|
private readonly child;
|
|
615
683
|
private readonly pending;
|
|
616
684
|
private readonly requestTimeoutMs;
|
|
685
|
+
private readonly onEvent?;
|
|
686
|
+
private readonly onEventError;
|
|
617
687
|
private stdoutBuffer;
|
|
618
688
|
private stderrBuffer;
|
|
619
689
|
private nextId;
|
|
@@ -622,6 +692,7 @@ declare class SpawnMacHelperClient implements MacHelperClient {
|
|
|
622
692
|
constructor(binaryPath: string, options?: SpawnMacHelperOptions);
|
|
623
693
|
private onStdout;
|
|
624
694
|
private dispatch;
|
|
695
|
+
private handleEvent;
|
|
625
696
|
private failAll;
|
|
626
697
|
private recordTerminalFailure;
|
|
627
698
|
/** Number of in-flight requests awaiting a response (for teardown/tests). */
|
package/dist/lib.d.ts
CHANGED
|
@@ -115,6 +115,12 @@ type HistoryEntry = {
|
|
|
115
115
|
durationMs: number;
|
|
116
116
|
startedAt: string;
|
|
117
117
|
runDir?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Number of retry attempts beyond the first for this run (PROWL-033). Absent or
|
|
120
|
+
* `0` when the run did not retry — kept optional so older `history.json` files and
|
|
121
|
+
* runs of hunts without a `retry` block keep parsing unchanged.
|
|
122
|
+
*/
|
|
123
|
+
retries?: number;
|
|
118
124
|
};
|
|
119
125
|
type HistoryFile = {
|
|
120
126
|
entries: HistoryEntry[];
|
|
@@ -177,6 +183,13 @@ type WaitForNetworkIdleStep = {
|
|
|
177
183
|
timeout?: number;
|
|
178
184
|
};
|
|
179
185
|
};
|
|
186
|
+
type WaitForResponseStep = {
|
|
187
|
+
waitForResponse: {
|
|
188
|
+
url: string;
|
|
189
|
+
status?: number;
|
|
190
|
+
timeout?: number;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
180
193
|
type SelectOptionStep = {
|
|
181
194
|
selectOption: {
|
|
182
195
|
selector: string;
|
|
@@ -299,7 +312,7 @@ type AssertScreenshotStep = {
|
|
|
299
312
|
type AssertWithAiStep = {
|
|
300
313
|
assertWithAI: string;
|
|
301
314
|
};
|
|
302
|
-
type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
|
|
315
|
+
type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | WaitForResponseStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
|
|
303
316
|
type Assertion = {
|
|
304
317
|
selectorExists: string;
|
|
305
318
|
} | {
|
|
@@ -344,6 +357,25 @@ type TraceCorrelation = {
|
|
|
344
357
|
traceId: string;
|
|
345
358
|
header: string;
|
|
346
359
|
};
|
|
360
|
+
/**
|
|
361
|
+
* One attempt of a retried hunt (PROWL-033). Recorded per attempt so a passed-on-retry
|
|
362
|
+
* run keeps the reason each earlier attempt failed, helping tell a flaky test apart from
|
|
363
|
+
* a slow environment or a real regression.
|
|
364
|
+
*/
|
|
365
|
+
type RetryAttempt = {
|
|
366
|
+
/** 1-based attempt number (attempt 1 is the initial run). */
|
|
367
|
+
attempt: number;
|
|
368
|
+
status: "pass" | "fail";
|
|
369
|
+
durationMs: number;
|
|
370
|
+
/** The first failing step on this attempt; absent when the attempt passed. */
|
|
371
|
+
failedStep?: {
|
|
372
|
+
/** 0-based index into the hunt's steps. */
|
|
373
|
+
index: number;
|
|
374
|
+
type: string;
|
|
375
|
+
};
|
|
376
|
+
/** Failure reason for this attempt; absent when it passed. */
|
|
377
|
+
error?: string;
|
|
378
|
+
};
|
|
347
379
|
type RunResult = {
|
|
348
380
|
status: "pass" | "fail";
|
|
349
381
|
exitCode: 0 | 1;
|
|
@@ -355,6 +387,17 @@ type RunResult = {
|
|
|
355
387
|
assertions: AssertionResult[];
|
|
356
388
|
artifacts: RunArtifacts;
|
|
357
389
|
traceCorrelations?: TraceCorrelation[];
|
|
390
|
+
/**
|
|
391
|
+
* Per-attempt diagnostics (PROWL-033), present only when the hunt used `retry` and
|
|
392
|
+
* more than one attempt ran. Absent when no retry occurred so existing consumers and
|
|
393
|
+
* older run artifacts keep parsing. The last entry always matches the final result.
|
|
394
|
+
*/
|
|
395
|
+
retryHistory?: RetryAttempt[];
|
|
396
|
+
/**
|
|
397
|
+
* Human-readable one-line retry outcome (PROWL-033), e.g. "Passed on attempt 2 of 3
|
|
398
|
+
* — first failure: navigate (timeout)". Present only alongside `retryHistory`.
|
|
399
|
+
*/
|
|
400
|
+
retrySummary?: string;
|
|
358
401
|
};
|
|
359
402
|
type CiHuntResult = {
|
|
360
403
|
hunt: string;
|
|
@@ -477,6 +520,10 @@ interface SessionDriver {
|
|
|
477
520
|
waitForNetworkIdle(options?: {
|
|
478
521
|
timeout?: number;
|
|
479
522
|
}): Promise<void>;
|
|
523
|
+
/** Resolve once a network response satisfies `predicate`, or reject on timeout. */
|
|
524
|
+
waitForResponse(predicate: (response: DriverResponse) => boolean, options?: {
|
|
525
|
+
timeout?: number;
|
|
526
|
+
}): Promise<void>;
|
|
480
527
|
evaluate<R = unknown, A = unknown>(pageFunction: string | ((arg: A) => R | Promise<R>), arg?: A): Promise<R>;
|
|
481
528
|
screenshot(options: {
|
|
482
529
|
path: string;
|
|
@@ -605,15 +652,38 @@ type ResolveHelperOptions = {
|
|
|
605
652
|
declare function resolveHelperBinary(env?: NodeJS.ProcessEnv, options?: ResolveHelperOptions): string;
|
|
606
653
|
/** Default per-request deadline for the helper transport. */
|
|
607
654
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
655
|
+
/**
|
|
656
|
+
* A server-initiated event line from the helper (ARCH-008): a JSON object with
|
|
657
|
+
* an `event` discriminant and **no** `id`, so it is unambiguously distinct from
|
|
658
|
+
* an id-matched command response. Emitted, for example, when an AXObserver
|
|
659
|
+
* notification fires during a `waitFor`/`openMenu` wait.
|
|
660
|
+
*/
|
|
661
|
+
type MacHelperEvent = Record<string, unknown> & {
|
|
662
|
+
event: string;
|
|
663
|
+
};
|
|
608
664
|
type SpawnMacHelperOptions = {
|
|
609
665
|
/** Per-request deadline; a request that gets no response by then rejects. */
|
|
610
666
|
requestTimeoutMs?: number;
|
|
667
|
+
/**
|
|
668
|
+
* Optional sink for server-initiated event lines. Events are informational —
|
|
669
|
+
* waits are resolved helper-side by the id-matched response — so they are
|
|
670
|
+
* forwarded here (if provided) and otherwise dropped, never touching the
|
|
671
|
+
* pending-request map.
|
|
672
|
+
*/
|
|
673
|
+
onEvent?: (event: MacHelperEvent) => void;
|
|
674
|
+
/**
|
|
675
|
+
* Optional diagnostic sink for event handler failures. Defaults to stderr so a
|
|
676
|
+
* bad sink is visible without allowing it to break helper transport.
|
|
677
|
+
*/
|
|
678
|
+
onEventError?: (message: string) => void;
|
|
611
679
|
};
|
|
612
680
|
/** A {@link MacHelperClient} backed by a spawned `prowl-macdriver serve` process. */
|
|
613
681
|
declare class SpawnMacHelperClient implements MacHelperClient {
|
|
614
682
|
private readonly child;
|
|
615
683
|
private readonly pending;
|
|
616
684
|
private readonly requestTimeoutMs;
|
|
685
|
+
private readonly onEvent?;
|
|
686
|
+
private readonly onEventError;
|
|
617
687
|
private stdoutBuffer;
|
|
618
688
|
private stderrBuffer;
|
|
619
689
|
private nextId;
|
|
@@ -622,6 +692,7 @@ declare class SpawnMacHelperClient implements MacHelperClient {
|
|
|
622
692
|
constructor(binaryPath: string, options?: SpawnMacHelperOptions);
|
|
623
693
|
private onStdout;
|
|
624
694
|
private dispatch;
|
|
695
|
+
private handleEvent;
|
|
625
696
|
private failAll;
|
|
626
697
|
private recordTerminalFailure;
|
|
627
698
|
/** Number of in-flight requests awaiting a response (for teardown/tests). */
|
package/dist/lib.js
CHANGED
|
@@ -149,7 +149,7 @@ import {
|
|
|
149
149
|
wdaTestRunArgs,
|
|
150
150
|
webOnlyReason,
|
|
151
151
|
zipinfoArchiveLister
|
|
152
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-SRWAF2RI.js";
|
|
153
153
|
import {
|
|
154
154
|
configSchema,
|
|
155
155
|
huntSchema,
|
|
@@ -160,7 +160,7 @@ import {
|
|
|
160
160
|
loadHuntMeta,
|
|
161
161
|
loadHuntTags,
|
|
162
162
|
stepSchema
|
|
163
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-TTZ2IL3P.js";
|
|
164
164
|
export {
|
|
165
165
|
ANDROID_INTERACTIVE_CLASSES,
|
|
166
166
|
ANDROID_KEYCODES,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadHuntTags,
|
|
11
11
|
resolveViewport,
|
|
12
12
|
warnLegacyConfigDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TTZ2IL3P.js";
|
|
14
14
|
export {
|
|
15
15
|
CONFIG_DIR,
|
|
16
16
|
LEGACY_CONFIG_DIR,
|
|
@@ -24,4 +24,4 @@ export {
|
|
|
24
24
|
resolveViewport,
|
|
25
25
|
warnLegacyConfigDir
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=loader-
|
|
27
|
+
//# sourceMappingURL=loader-CAE4FPBK.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Form Submission
|
|
2
|
+
# ---
|
|
3
|
+
# Pattern: Forms — fill text fields, pick a dropdown option, submit, verify result.
|
|
4
|
+
# What it tests: Complete a form and confirm the success state renders.
|
|
5
|
+
# Customize:
|
|
6
|
+
# - Point `navigate` at your form's path
|
|
7
|
+
# - Update the field labels, the dropdown label/option, and the button text
|
|
8
|
+
# to match your form
|
|
9
|
+
#
|
|
10
|
+
# Tip: Prefer stable selectors — an accessible label or a data-testid — over
|
|
11
|
+
# brittle CSS. Run `prowl analyze` to dump ranked selector candidates for a page.
|
|
12
|
+
|
|
13
|
+
name: form
|
|
14
|
+
description: Fill and submit a form, verify the success message
|
|
15
|
+
|
|
16
|
+
tags:
|
|
17
|
+
- forms
|
|
18
|
+
- input
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- navigate: "/signup"
|
|
22
|
+
|
|
23
|
+
# Shorthand fill — Prowl finds the input by its label or placeholder text.
|
|
24
|
+
# Equivalent explicit form:
|
|
25
|
+
# fill:
|
|
26
|
+
# selector: "input[name='name']"
|
|
27
|
+
# value: "Ada Lovelace"
|
|
28
|
+
- fill:
|
|
29
|
+
"Full name": "Ada Lovelace"
|
|
30
|
+
|
|
31
|
+
- fill:
|
|
32
|
+
"Email": "ada@example.com"
|
|
33
|
+
|
|
34
|
+
# Shorthand select — finds the <select> by its label and picks the option by
|
|
35
|
+
# its visible text. Equivalent explicit form:
|
|
36
|
+
# selectOption:
|
|
37
|
+
# selector: "select[name='plan']"
|
|
38
|
+
# value: "Pro"
|
|
39
|
+
- select:
|
|
40
|
+
"Plan": "Pro"
|
|
41
|
+
|
|
42
|
+
# Shorthand click — Prowl finds a checkbox or button by its text content.
|
|
43
|
+
- click: "I agree to the terms"
|
|
44
|
+
|
|
45
|
+
- click: "Create account"
|
|
46
|
+
|
|
47
|
+
# Mid-flow assertion — verify the success state rendered.
|
|
48
|
+
- assert:
|
|
49
|
+
visible: "Welcome, Ada"
|
|
50
|
+
|
|
51
|
+
assertions:
|
|
52
|
+
- urlIncludes: "/welcome"
|
|
53
|
+
- noConsoleErrors: true
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# macOS Hello — Your First Desktop Hunt (Experimental)
|
|
2
|
+
# ---
|
|
3
|
+
# Prowl drives native macOS apps through Apple's Accessibility API, from the same
|
|
4
|
+
# YAML as web hunts. This starter targets TextEdit — present on every Mac — so you
|
|
5
|
+
# can try the desktop target without wiring up your own app first.
|
|
6
|
+
#
|
|
7
|
+
# ── Before this hunt will run ───────────────────────────────────────────────
|
|
8
|
+
# 1. Install the helper: prowl macdriver install
|
|
9
|
+
# Until the first signed release ships, `install` returns a 404 — build from
|
|
10
|
+
# source instead (needs the Swift toolchain / Xcode CLT):
|
|
11
|
+
# cd macdriver && swift build -c release
|
|
12
|
+
# 2. Grant Accessibility permission to the app hosting your terminal (Terminal,
|
|
13
|
+
# iTerm, VS Code, …): System Settings → Privacy & Security → Accessibility.
|
|
14
|
+
# `prowl macdriver status` prints the resolved binary and this guidance.
|
|
15
|
+
# 3. Point .prowl/config.yml at a macOS target — init's default config targets
|
|
16
|
+
# the web. Replace its `target:` block, and scope the app under guardrails:
|
|
17
|
+
#
|
|
18
|
+
# target:
|
|
19
|
+
# type: macos
|
|
20
|
+
# app: "com.apple.TextEdit" # bundle id, or an absolute /path/to/App.app
|
|
21
|
+
# guardrails:
|
|
22
|
+
# allowedApps:
|
|
23
|
+
# - "com.apple.TextEdit"
|
|
24
|
+
#
|
|
25
|
+
# The macOS target is EXPERIMENTAL — the selector dialect and step coverage may
|
|
26
|
+
# still change. `navigate`, `waitForUrl`, and other web-only steps are rejected
|
|
27
|
+
# on it; the portable steps below (`type`, `assert: visible`) run on both targets.
|
|
28
|
+
#
|
|
29
|
+
# ── Finding selectors ───────────────────────────────────────────────────────
|
|
30
|
+
# Don't guess selectors — dump them: prowl analyze --app com.apple.TextEdit
|
|
31
|
+
# It walks the Accessibility tree and prints every element with ranked selector
|
|
32
|
+
# candidates (prefer `id=` — the native analog of data-testid). TextEdit's
|
|
33
|
+
# controls aren't ours, so treat the steps below as a starting point and adjust
|
|
34
|
+
# to what `analyze` reports on your macOS version.
|
|
35
|
+
|
|
36
|
+
name: macos-hello
|
|
37
|
+
description: Type into TextEdit and verify the text appears (experimental macOS target)
|
|
38
|
+
|
|
39
|
+
tags:
|
|
40
|
+
- macos
|
|
41
|
+
- smoke
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
# `type` sends keystrokes to the focused element — a fresh TextEdit document.
|
|
45
|
+
- type: "Hello from Prowl!"
|
|
46
|
+
|
|
47
|
+
# Portable assertion — the document's text should now contain what we typed.
|
|
48
|
+
- assert:
|
|
49
|
+
visible: "Hello from Prowl!"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prowl-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "E2E testing for native macOS apps and web apps from declarative YAML hunts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
"build": "tsup",
|
|
60
60
|
"lint": "eslint .",
|
|
61
61
|
"test": "vitest run",
|
|
62
|
-
"test:watch": "vitest"
|
|
62
|
+
"test:watch": "vitest",
|
|
63
|
+
"audit:licenses": "node scripts/audit-licenses.mjs"
|
|
63
64
|
},
|
|
64
65
|
"dependencies": {
|
|
65
66
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
84
85
|
"@typescript-eslint/parser": "^7.18.0",
|
|
85
86
|
"eslint": "^8.57.1",
|
|
87
|
+
"license-checker-rseidelsohn": "^4.4.2",
|
|
86
88
|
"tsup": "^8.3.5",
|
|
87
89
|
"typescript": "^5.7.3",
|
|
88
90
|
"vitest": "^2.1.8"
|