prowl-tools 0.1.3 → 0.1.5
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/LICENSE +1 -1
- package/NOTICE +11 -1
- package/README.md +390 -3
- package/dist/chunk-2KD2XCTH.js +6202 -0
- package/dist/chunk-2KD2XCTH.js.map +1 -0
- package/dist/{chunk-NXXGJOBG.js → chunk-ITOSUJCN.js} +74 -13
- package/dist/chunk-ITOSUJCN.js.map +1 -0
- package/dist/index.cjs +4249 -1116
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +184 -78
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +3988 -816
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +1052 -17
- package/dist/lib.d.ts +1052 -17
- package/dist/lib.js +152 -4
- package/dist/{loader-5RDNTJHH.js → loader-FCXPARP7.js} +2 -2
- package/package.json +6 -2
- package/dist/chunk-NXXGJOBG.js.map +0 -1
- package/dist/chunk-T7YLXF6X.js +0 -3158
- package/dist/chunk-T7YLXF6X.js.map +0 -1
- /package/dist/{loader-5RDNTJHH.js.map → loader-FCXPARP7.js.map} +0 -0
package/LICENSE
CHANGED
package/NOTICE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Prowl
|
|
2
|
-
Copyright (c) 2026
|
|
2
|
+
Copyright (c) 2026 Genkei Labs
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
5
5
|
this file except in compliance with the License. You may obtain a copy of the
|
|
@@ -12,6 +12,16 @@ attributions are provided for the benefit of downstream users.
|
|
|
12
12
|
Direct Runtime Dependencies
|
|
13
13
|
-------------------------------------------------------------------------------
|
|
14
14
|
|
|
15
|
+
appium-uiautomator2-server
|
|
16
|
+
License: Apache-2.0
|
|
17
|
+
Copyright (c) Appium Contributors
|
|
18
|
+
https://github.com/appium/appium-uiautomator2-server
|
|
19
|
+
|
|
20
|
+
appium-webdriveragent
|
|
21
|
+
License: Apache-2.0
|
|
22
|
+
Copyright (c) Appium Contributors
|
|
23
|
+
https://github.com/appium/WebDriverAgent
|
|
24
|
+
|
|
15
25
|
chalk
|
|
16
26
|
License: MIT
|
|
17
27
|
https://github.com/chalk/chalk
|
package/README.md
CHANGED
|
@@ -55,6 +55,29 @@ Prowl uses Playwright under the hood. Install the browser:
|
|
|
55
55
|
npx playwright install chromium
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
The on-device agents for the experimental Android/iOS targets
|
|
59
|
+
(`appium-uiautomator2-server`, `appium-webdriveragent`) are **optional
|
|
60
|
+
dependencies** — installed by default so mobile testing works out of the box,
|
|
61
|
+
but skippable for a leaner web-only install:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install -g prowl-tools --omit=optional
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If a mobile target later needs an omitted agent, it fails with exact recovery
|
|
68
|
+
commands for global and local installs. For a global npm install, restore the
|
|
69
|
+
agents with:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install -g appium-uiautomator2-server@10.6.2 appium-webdriveragent@16.4.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For a local project install, omit `-g`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install appium-uiautomator2-server@10.6.2 appium-webdriveragent@16.4.0
|
|
79
|
+
```
|
|
80
|
+
|
|
58
81
|
### 2. Initialize
|
|
59
82
|
|
|
60
83
|
```bash
|
|
@@ -422,15 +445,29 @@ assertions:
|
|
|
422
445
|
Config lives at `.prowl/config.yml`. All options with defaults:
|
|
423
446
|
|
|
424
447
|
```yaml
|
|
425
|
-
#
|
|
448
|
+
# Execution target. Defaults to the web target; existing configs work unchanged.
|
|
426
449
|
target:
|
|
427
|
-
|
|
450
|
+
type: "web" # "web" (default), "macos", "android", or "ios" (experimental)
|
|
451
|
+
url: "http://localhost:3000" # Required for web targets
|
|
452
|
+
# For the experimental macOS target instead:
|
|
453
|
+
# type: "macos"
|
|
454
|
+
# app: "com.example.App" # bundle id or /path/to/App.app (see "macOS Target")
|
|
455
|
+
# For the experimental Android target instead:
|
|
456
|
+
# type: "android"
|
|
457
|
+
# app: "com.example.app" # package name or /path/to/app.apk (see "Android Target")
|
|
458
|
+
# deviceSerial: "emulator-5554" # optional; required only with several devices attached
|
|
459
|
+
# coldStart: false # optional; pm clear before launch
|
|
460
|
+
# For the experimental iOS simulator target instead:
|
|
461
|
+
# type: "ios"
|
|
462
|
+
# app: "com.example.App" # bundle id or /path/to/App.app (see "iOS Target")
|
|
463
|
+
# udid: "..." # optional; required only with several booted simulators
|
|
464
|
+
# coldStart: false # optional; uninstall+reinstall before launch (needs a .app)
|
|
428
465
|
|
|
429
466
|
# Browser settings
|
|
430
467
|
browser:
|
|
431
468
|
headless: true # false = show the browser window
|
|
432
469
|
slowMo: 0 # ms delay between actions (debugging)
|
|
433
|
-
timeout: 30000 # default page operation timeout
|
|
470
|
+
timeout: 30000 # default page operation timeout; macOS app launch timeout
|
|
434
471
|
|
|
435
472
|
# What gets saved per run
|
|
436
473
|
artifacts:
|
|
@@ -454,6 +491,7 @@ guardrails:
|
|
|
454
491
|
forbiddenSelectors: # selectors that steps cannot use
|
|
455
492
|
- "[data-danger]"
|
|
456
493
|
- ".delete-btn"
|
|
494
|
+
allowedApps: [] # native targets only: macOS bundle IDs/names/.app paths, Android package IDs/canonical APK paths, or iOS bundle IDs/.app paths
|
|
457
495
|
|
|
458
496
|
# Auth state from `prowl login`
|
|
459
497
|
auth:
|
|
@@ -897,6 +935,355 @@ Templates cover auth flows (OAuth, 2FA), e-commerce (Stripe), admin panels, SaaS
|
|
|
897
935
|
|
|
898
936
|
---
|
|
899
937
|
|
|
938
|
+
## macOS Target (Experimental)
|
|
939
|
+
|
|
940
|
+
> **Experimental (PROWL-048).** Prowl can drive **native macOS apps** — including
|
|
941
|
+
> menu bar extras (`NSStatusItem` + `NSMenu`) — through Apple's Accessibility API,
|
|
942
|
+
> in addition to the web. The API, selector dialect, and step coverage may change.
|
|
943
|
+
> **Distribution is deferred:** the required helper binary is **not** shipped in the
|
|
944
|
+
> npm package; you build it locally (below).
|
|
945
|
+
|
|
946
|
+
### Enabling it
|
|
947
|
+
|
|
948
|
+
1. **Build the helper** (one time; requires the Swift toolchain / Xcode CLT):
|
|
949
|
+
|
|
950
|
+
```bash
|
|
951
|
+
cd macdriver
|
|
952
|
+
swift build -c release
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
Prowl finds the binary at `macdriver/.build/release/prowl-macdriver`, or at
|
|
956
|
+
`$PROWL_MACDRIVER_BIN` if set. If it is missing, Prowl fails with a clear
|
|
957
|
+
"build the helper" message rather than crashing.
|
|
958
|
+
|
|
959
|
+
2. **Point your config at a macOS target:**
|
|
960
|
+
|
|
961
|
+
```yaml
|
|
962
|
+
target:
|
|
963
|
+
type: macos
|
|
964
|
+
app: "com.example.App" # bundle id, or an absolute /path/to/App.app
|
|
965
|
+
guardrails:
|
|
966
|
+
allowedApps: # optional scope; empty = allow the target app
|
|
967
|
+
- "com.example.App"
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
When `target.app` is an app path, `allowedApps` may list the exact `.app`
|
|
971
|
+
path, the app bundle name (`Example` for `Example.app`), or the bundle id
|
|
972
|
+
from `Contents/Info.plist` when that file is readable.
|
|
973
|
+
|
|
974
|
+
3. **Grant Accessibility permission** (see below), then run a hunt as usual:
|
|
975
|
+
`prowl run my-macos-hunt`.
|
|
976
|
+
|
|
977
|
+
### Accessibility & Screen Recording permission
|
|
978
|
+
|
|
979
|
+
The **process that hosts** Prowl (your terminal — Terminal, iTerm, VS Code, or a CI
|
|
980
|
+
agent) must be granted **Accessibility** permission: **System Settings → Privacy &
|
|
981
|
+
Security → Accessibility**, then enable that app. macOS attributes the grant to the
|
|
982
|
+
hosting app, not to `prowl-macdriver`. Preflight from the helper:
|
|
983
|
+
|
|
984
|
+
```bash
|
|
985
|
+
macdriver/.build/release/prowl-macdriver check # prints {"trusted": <bool>}; prompts on first run
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
The `screenshot`/`assertScreenshot` steps additionally need **Screen Recording**
|
|
989
|
+
permission for the hosting app.
|
|
990
|
+
|
|
991
|
+
**CI notes (macOS runners):** headless CI cannot click "Allow" in a dialog, so grant
|
|
992
|
+
the permissions non-interactively before the run. On a self-hosted runner you can
|
|
993
|
+
pre-authorize the agent's host app with a TCC profile via MDM, or (on ephemeral
|
|
994
|
+
runners where it's acceptable) seed the TCC database, e.g.:
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \
|
|
998
|
+
"INSERT OR REPLACE INTO access VALUES('kTCCServiceAccessibility','<runner-app-bundle-id>',0,2,2,1,NULL,NULL,NULL,'UNUSED',NULL,0,1,NULL,NULL,NULL);"
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
GitHub-hosted macOS runners do not grant Accessibility, so the macOS target is aimed
|
|
1002
|
+
at self-hosted / MDM-managed runners for now.
|
|
1003
|
+
|
|
1004
|
+
### Selector dialect (macOS)
|
|
1005
|
+
|
|
1006
|
+
Native selectors address accessibility identifiers, roles, and labels:
|
|
1007
|
+
|
|
1008
|
+
| Selector | Matches |
|
|
1009
|
+
|---|---|
|
|
1010
|
+
| `id=openSettings` | element whose `AXIdentifier` equals `openSettings` |
|
|
1011
|
+
| `role=button[name="Save"]` | an `AXButton` whose title/description/value contains `Save` |
|
|
1012
|
+
| `label="Email"` | element whose accessibility label equals `Email` |
|
|
1013
|
+
| `text="Save"` or bare `Save` | element whose title/description/value contains the text |
|
|
1014
|
+
| `statusItem` | opens the app's menu bar status-item menu |
|
|
1015
|
+
| `menu=Preferences…` | opens the status-item menu and clicks that item |
|
|
1016
|
+
|
|
1017
|
+
`forbiddenSelectors` still applies (text patterns match via the same substring
|
|
1018
|
+
semantics as the web target). Prefer `id=` (accessibility identifiers) — the native
|
|
1019
|
+
analog of `data-testid`.
|
|
1020
|
+
|
|
1021
|
+
### Finding selectors
|
|
1022
|
+
|
|
1023
|
+
Don't guess selectors — dump them. `prowl analyze` works on the macOS target the
|
|
1024
|
+
same way it does on the web: it launches/attaches to the app, walks the
|
|
1025
|
+
Accessibility tree, and prints every interactive element with **ranked selector
|
|
1026
|
+
candidates** (best first) plus the app's windows and status-item menu contents.
|
|
1027
|
+
It is read-only (the only interaction is opening and closing the status menu),
|
|
1028
|
+
honors `guardrails.allowedApps`, and leaves the app running when done.
|
|
1029
|
+
|
|
1030
|
+
```bash
|
|
1031
|
+
# Uses the macOS target from .prowl/config.yml:
|
|
1032
|
+
prowl analyze
|
|
1033
|
+
|
|
1034
|
+
# …or point it at any app without a config:
|
|
1035
|
+
prowl analyze --app com.example.App
|
|
1036
|
+
prowl analyze --app "/Applications/Example.app"
|
|
1037
|
+
|
|
1038
|
+
# Machine-readable output for agents:
|
|
1039
|
+
prowl analyze --app com.example.App --json
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
Example (human-readable) output:
|
|
1043
|
+
|
|
1044
|
+
```text
|
|
1045
|
+
App Analysis: com.example.App
|
|
1046
|
+
|
|
1047
|
+
Windows:
|
|
1048
|
+
"Main Window" id=mainWindow
|
|
1049
|
+
|
|
1050
|
+
Interactive Elements:
|
|
1051
|
+
AXButton id=saveButton "Save"
|
|
1052
|
+
AXTextField label="Email" "Email"
|
|
1053
|
+
AXCheckBox label="Remember me" "Remember me" (disabled)
|
|
1054
|
+
|
|
1055
|
+
Menu Bar:
|
|
1056
|
+
AXMenuItem id=preferences "Preferences…"
|
|
1057
|
+
AXMenuItem label="Quit" "Quit"
|
|
1058
|
+
|
|
1059
|
+
3 elements, 1 windows, 2 menu items
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
Selectors are ranked `id=` > `label=` > `role=…[name="…"]` > `text=` — copy the
|
|
1063
|
+
first (most durable) candidate into your hunt. Status-item menu identifiers
|
|
1064
|
+
(`id=preferences` above) are especially valuable, since menu titles often carry
|
|
1065
|
+
ellipses or localized text that are awkward to match by substring.
|
|
1066
|
+
|
|
1067
|
+
### Step compatibility
|
|
1068
|
+
|
|
1069
|
+
Portable steps run on **both** targets; web-only steps are rejected up front on the
|
|
1070
|
+
macOS target (with a clear error), and `prowl login` / URL guardrails do not apply.
|
|
1071
|
+
|
|
1072
|
+
| Portable (web + macOS) | Web-only (rejected on macOS) |
|
|
1073
|
+
|---|---|
|
|
1074
|
+
| `click`, `fill`, `type`, `press` | `navigate`, `waitForUrl`, `waitForNetworkIdle` |
|
|
1075
|
+
| `wait`, `waitForSelector` | `mockRoute` / `unmockRoute` |
|
|
1076
|
+
| `assert: visible` / `notVisible` | `evalScript`, `runScript` |
|
|
1077
|
+
| `screenshot`, `assertScreenshot` | `onDialog`, `select` / `selectOption` |
|
|
1078
|
+
| `hover`, `scrollTo` | `setInputFiles`, `waitForDownload` |
|
|
1079
|
+
| `repeat`, `if`, `runHunt`, `copyText` | `scroll` (directional), `assert: urlIncludes` / `urlEquals` |
|
|
1080
|
+
|
|
1081
|
+
Notes: `press` maps Enter/Return/Space onto the element's activate action (other keys
|
|
1082
|
+
are unsupported); `type` fills the focused control; app teardown quits the target app
|
|
1083
|
+
after the run.
|
|
1084
|
+
|
|
1085
|
+
> Docs follow-up: the customer-facing docs site (`prowl-docs`) should gain a "macOS
|
|
1086
|
+
> target" page mirroring this section (target type + step-compatibility matrix +
|
|
1087
|
+
> permission setup); tracked separately from this repo.
|
|
1088
|
+
|
|
1089
|
+
---
|
|
1090
|
+
|
|
1091
|
+
## Android Target (Experimental)
|
|
1092
|
+
|
|
1093
|
+
> **Experimental (PROWL-058).** Prowl can drive **native Android apps** on an
|
|
1094
|
+
> emulator or a USB-connected device, in addition to the web and macOS targets.
|
|
1095
|
+
> It follows the same "external agent + JSON protocol" shape as the macOS target:
|
|
1096
|
+
> `adb` handles device lifecycle and the on-device
|
|
1097
|
+
> [`appium-uiautomator2-server`](https://github.com/appium/appium-uiautomator2-driver)
|
|
1098
|
+
> (Apache-2.0) handles UI interaction over a plain HTTP/JSON API driven with raw
|
|
1099
|
+
> `fetch`. The API, selector dialect, and step coverage may change.
|
|
1100
|
+
|
|
1101
|
+
### Requirements
|
|
1102
|
+
|
|
1103
|
+
- **`adb`** on your `PATH` (from the Android SDK platform-tools), plus at least
|
|
1104
|
+
one **booted emulator or device** (`adb devices -l` should list it as `device`).
|
|
1105
|
+
- The two prebuilt agent APKs ship inside the `appium-uiautomator2-server` npm
|
|
1106
|
+
dependency and are installed onto the device automatically — **nothing to build**.
|
|
1107
|
+
No Appium server, no JVM, no gRPC.
|
|
1108
|
+
- If `target.app` is an **`.apk`**, Android build-tools **`aapt`/`aapt2`** must be
|
|
1109
|
+
on `PATH` so Prowl can read the package name (or set `target.app` to the package
|
|
1110
|
+
name directly and install the APK yourself).
|
|
1111
|
+
|
|
1112
|
+
### Enabling it
|
|
1113
|
+
|
|
1114
|
+
Point your config at an Android target:
|
|
1115
|
+
|
|
1116
|
+
```yaml
|
|
1117
|
+
target:
|
|
1118
|
+
type: android
|
|
1119
|
+
app: "com.example.app" # a package name, or a path to an .apk to install
|
|
1120
|
+
# deviceSerial: "emulator-5554" # required only when several devices are attached
|
|
1121
|
+
# coldStart: true # `pm clear` before launch for a deterministic start (default off)
|
|
1122
|
+
guardrails:
|
|
1123
|
+
allowedApps: # optional scope; empty = allow the target app
|
|
1124
|
+
- "com.example.app"
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
Then run a hunt as usual: `prowl run my-android-hunt`.
|
|
1128
|
+
|
|
1129
|
+
On launch Prowl selects the device (failing with an actionable error, listing
|
|
1130
|
+
serials, if several are attached and no `deviceSerial` is set), installs the app
|
|
1131
|
+
(when given an `.apk`) and the agent, starts the agent via `am instrument`,
|
|
1132
|
+
port-forwards it on a **dynamically allocated** local port (so parallel sessions /
|
|
1133
|
+
CI jobs don't collide), and waits for the agent to report ready. Everything is
|
|
1134
|
+
torn down (agent session, instrumentation, port forward, `am force-stop`) after
|
|
1135
|
+
the run. `guardrails.allowedApps` accepts Android package IDs or canonical full
|
|
1136
|
+
`.apk` paths; when `target.app` is an APK, Prowl resolves its package ID and
|
|
1137
|
+
validates it before installing.
|
|
1138
|
+
|
|
1139
|
+
### Selector dialect (Android)
|
|
1140
|
+
|
|
1141
|
+
Native selectors address `resource-id`, `content-desc`, visible text, and widget
|
|
1142
|
+
class. Semantics match the macOS target so a selector means the same thing on both
|
|
1143
|
+
native targets:
|
|
1144
|
+
|
|
1145
|
+
| Selector | Matches |
|
|
1146
|
+
|---|---|
|
|
1147
|
+
| `id=save` | element whose `resource-id` is `save` — a bare name is auto-qualified with the target app's package (`com.pkg:id/save`); ids in other namespaces need the full form (e.g. `id=android:id/title`) |
|
|
1148
|
+
| `label="Submit"` | element whose `content-desc` equals `Submit` (exact) |
|
|
1149
|
+
| `role=android.widget.Button` | element of that widget class |
|
|
1150
|
+
| `role=android.widget.Button[name="Save"]` | that widget class whose visible text contains `Save` |
|
|
1151
|
+
| `text="Save"` or bare `Save` | element whose visible text contains the text (substring) |
|
|
1152
|
+
|
|
1153
|
+
Prefer `id=` (the native analog of `data-testid`). **Jetpack Compose caveat:**
|
|
1154
|
+
Compose nodes only expose a `resource-id` when the app sets
|
|
1155
|
+
`Modifier.testTag(...)` **and** enables `testTagsAsResourceId = true`; otherwise
|
|
1156
|
+
match Compose UI with `text=` or `label=` (from `Modifier.semantics { contentDescription = ... }`).
|
|
1157
|
+
`forbiddenSelectors` still applies (text patterns use the same substring semantics
|
|
1158
|
+
as the other targets).
|
|
1159
|
+
|
|
1160
|
+
### Step compatibility
|
|
1161
|
+
|
|
1162
|
+
Portable steps run on the Android target; web-only steps are rejected up front
|
|
1163
|
+
(with a clear error), and `prowl login` / URL guardrails do not apply.
|
|
1164
|
+
|
|
1165
|
+
| Portable (Android) | Not supported on Android |
|
|
1166
|
+
|---|---|
|
|
1167
|
+
| `click`, `fill`, `type`, `press` | `navigate`, `waitForUrl`, `waitForNetworkIdle` |
|
|
1168
|
+
| `wait`, `waitForSelector` | `mockRoute` / `unmockRoute`, `evalScript`, `runScript` |
|
|
1169
|
+
| `assert: visible` / `notVisible` | `onDialog`, `select` / `selectOption`, `setInputFiles` |
|
|
1170
|
+
| `screenshot`, `assertScreenshot` | `waitForDownload`, `scroll`, `assert: urlIncludes` / `urlEquals` |
|
|
1171
|
+
| `repeat`, `if`, `runHunt`, `copyText` | `hover`, `scrollTo` (no touch equivalent yet — see below) |
|
|
1172
|
+
|
|
1173
|
+
Notes: `type` and `fill` set text on the focused / matched field **unicode-safely**
|
|
1174
|
+
(via the agent's `element/value`, not `adb shell input text`); `press` maps key
|
|
1175
|
+
names (`Enter`, `Tab`, `Backspace`, `Back`, `Home`, arrow keys, …) onto Android key
|
|
1176
|
+
codes and dispatches them to the focused view. `hover` and `scrollTo` have no touch
|
|
1177
|
+
equivalent yet and are rejected with a clear message; scroll-gesture support is a
|
|
1178
|
+
follow-up. A degraded pure-`adb` fallback (`uiautomator dump` + `input tap`) is a
|
|
1179
|
+
possible future diagnostic mode, not the primary path.
|
|
1180
|
+
|
|
1181
|
+
> Out of scope for PROWL-058 (tracked separately): `prowl analyze` for Android and
|
|
1182
|
+
> CI recipes (PROWL-061), the unified native selector engine (PROWL-060), and real
|
|
1183
|
+
> iOS devices (PROWL-062). The iOS **simulator** target ships below.
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
|
|
1187
|
+
## iOS Target (Experimental)
|
|
1188
|
+
|
|
1189
|
+
> **Experimental (PROWL-059).** Prowl can drive **native iOS apps** on a **booted
|
|
1190
|
+
> iOS Simulator**, in addition to the web, macOS, and Android targets. It follows
|
|
1191
|
+
> the same "external agent + JSON protocol" shape: `xcrun simctl` handles simulator
|
|
1192
|
+
> lifecycle and screenshots, and the on-simulator
|
|
1193
|
+
> [WebDriverAgent](https://github.com/appium/WebDriverAgent) (Apache-2.0) handles UI
|
|
1194
|
+
> interaction over its W3C-shaped HTTP/JSON API driven with raw `fetch`. The API,
|
|
1195
|
+
> selector dialect, and step coverage may change. **Real devices are out of scope**
|
|
1196
|
+
> (PROWL-062) — simulators only.
|
|
1197
|
+
|
|
1198
|
+
### Requirements
|
|
1199
|
+
|
|
1200
|
+
- **macOS with a full Xcode** (not just the command-line tools) installed and
|
|
1201
|
+
selected (`xcode-select -p`), so `xcrun simctl` and `xcodebuild` are available.
|
|
1202
|
+
- At least one **booted simulator** (`xcrun simctl list devices | grep Booted`, or
|
|
1203
|
+
boot one with `xcrun simctl boot <udid>` / from Xcode).
|
|
1204
|
+
- The **WebDriverAgent** runner is built **once** from the `appium-webdriveragent`
|
|
1205
|
+
npm dependency (`xcodebuild build-for-testing`) and cached under
|
|
1206
|
+
`~/.prowl/wda/<wda-version>-xcode<xcode-version>/`; the first run prints a one-time
|
|
1207
|
+
"building WebDriverAgent…" notice and can take a few minutes. Simulators need **no
|
|
1208
|
+
code signing**. Set `PROWL_WDA_RUNNER` to a prebuilt `WebDriverAgentRunner-Runner.app`
|
|
1209
|
+
to skip the build (e.g. in CI with a cached runner).
|
|
1210
|
+
|
|
1211
|
+
### Enabling it
|
|
1212
|
+
|
|
1213
|
+
Point your config at an iOS target:
|
|
1214
|
+
|
|
1215
|
+
```yaml
|
|
1216
|
+
target:
|
|
1217
|
+
type: ios
|
|
1218
|
+
app: "com.example.App" # a bundle id, or a path to a built .app to install
|
|
1219
|
+
# udid: "ABCD-1234" # required only when several simulators are booted
|
|
1220
|
+
# coldStart: true # uninstall+reinstall before launch (requires a .app path)
|
|
1221
|
+
guardrails:
|
|
1222
|
+
allowedApps: # optional scope; empty = allow the target app
|
|
1223
|
+
- "com.example.App"
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
Then run a hunt as usual: `prowl run my-ios-hunt`.
|
|
1227
|
+
|
|
1228
|
+
On launch Prowl selects the booted simulator (failing with an actionable error,
|
|
1229
|
+
listing candidates, if several are booted and no `udid` is set), installs the app
|
|
1230
|
+
(when given a `.app`, reading its bundle id from the bundle's **root** `Info.plist`),
|
|
1231
|
+
builds/caches and installs the WebDriverAgent runner, launches it on a
|
|
1232
|
+
**dynamically allocated** port (passed via `SIMCTL_CHILD_USE_PORT` so parallel
|
|
1233
|
+
sessions / CI jobs don't collide), launches the target app, and waits for WDA to
|
|
1234
|
+
report ready. Everything is torn down (WDA session, `simctl terminate` of the runner
|
|
1235
|
+
and the app) after the run. `guardrails.allowedApps` accepts iOS bundle ids or
|
|
1236
|
+
`.app` paths; a `.app` path is authorized by its path, bundle name, or the bundle id
|
|
1237
|
+
read from its root `Info.plist`. Note: a bare `target.app` ending in `.app` is
|
|
1238
|
+
treated as a **bundle id** unless a directory of that name exists, so bundle ids like
|
|
1239
|
+
`com.company.app` are not mistaken for paths.
|
|
1240
|
+
|
|
1241
|
+
### Selector dialect (iOS)
|
|
1242
|
+
|
|
1243
|
+
Native selectors address accessibility ids, labels, visible text, and element type.
|
|
1244
|
+
Semantics match the macOS/Android targets so a selector means the same thing across
|
|
1245
|
+
native targets:
|
|
1246
|
+
|
|
1247
|
+
| Selector | Matches |
|
|
1248
|
+
|---|---|
|
|
1249
|
+
| `id=save` | element whose accessibility id (`accessibilityIdentifier` / name) is `save` |
|
|
1250
|
+
| `label="Submit"` | element whose `accessibilityLabel` equals `Submit` (exact) |
|
|
1251
|
+
| `role=XCUIElementTypeButton` | element of that type (shorthand `role=Button` works too) |
|
|
1252
|
+
| `role=Button[name="Save"]` | that type whose visible `label`/`value` contains `Save` |
|
|
1253
|
+
| `text="Save"` or bare `Save` | element whose `label` or `value` contains the text (substring) |
|
|
1254
|
+
| `:focus` | the element with keyboard focus (`hasKeyboardFocus == 1`) |
|
|
1255
|
+
|
|
1256
|
+
Prefer `id=` (set `accessibilityIdentifier` in your app — the native analog of
|
|
1257
|
+
`data-testid`). Text/label/role+name selectors compile to WDA NSPredicate strings
|
|
1258
|
+
(quotes and backslashes are escaped). `forbiddenSelectors` still applies.
|
|
1259
|
+
|
|
1260
|
+
### Step compatibility
|
|
1261
|
+
|
|
1262
|
+
Portable steps run on the iOS target; web-only steps are rejected up front (with a
|
|
1263
|
+
clear error), and URL guardrails do not apply.
|
|
1264
|
+
|
|
1265
|
+
| Portable (iOS) | Not supported on iOS |
|
|
1266
|
+
|---|---|
|
|
1267
|
+
| `click`, `fill`, `type`, `press` | `navigate`, `waitForUrl`, `waitForNetworkIdle` |
|
|
1268
|
+
| `wait`, `waitForSelector` | `mockRoute` / `unmockRoute`, `evalScript`, `runScript` |
|
|
1269
|
+
| `assert: visible` / `notVisible` | `onDialog`, `select` / `selectOption`, `setInputFiles` |
|
|
1270
|
+
| `screenshot`, `assertScreenshot` | `waitForDownload`, `scroll`, `assert: urlIncludes` / `urlEquals` |
|
|
1271
|
+
| `repeat`, `if`, `runHunt`, `copyText` | `hover`, `scrollTo` (no touch equivalent yet) |
|
|
1272
|
+
|
|
1273
|
+
Notes: `type` and `fill` set text on the focused / matched field via WDA's
|
|
1274
|
+
`element/value`; `press` supports a small honest key set — `enter`/`return` and
|
|
1275
|
+
`delete`/`backspace` (sent through WDA's key endpoint to the focused element) and
|
|
1276
|
+
`home` (returns to the springboard) — and rejects other keys with the supported-keys
|
|
1277
|
+
message. Screenshots are captured with `simctl` (not WDA), so artifacts still work
|
|
1278
|
+
even if the agent wedges. `hover` and `scrollTo` have no touch equivalent yet and are
|
|
1279
|
+
rejected with a clear message; scroll-gesture support is a follow-up.
|
|
1280
|
+
|
|
1281
|
+
> Out of scope for PROWL-059 (tracked separately): `prowl analyze` for iOS and CI
|
|
1282
|
+
> recipes (PROWL-061), the unified native selector engine (PROWL-060), and real iOS
|
|
1283
|
+
> devices (PROWL-062).
|
|
1284
|
+
|
|
1285
|
+
---
|
|
1286
|
+
|
|
900
1287
|
## Troubleshooting
|
|
901
1288
|
|
|
902
1289
|
### "Could not find .prowl/config.yml"
|