prowl-tools 0.1.4 → 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/NOTICE CHANGED
@@ -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
@@ -424,11 +447,21 @@ Config lives at `.prowl/config.yml`. All options with defaults:
424
447
  ```yaml
425
448
  # Execution target. Defaults to the web target; existing configs work unchanged.
426
449
  target:
427
- type: "web" # "web" (default) or "macos" (experimental)
450
+ type: "web" # "web" (default), "macos", "android", or "ios" (experimental)
428
451
  url: "http://localhost:3000" # Required for web targets
429
452
  # For the experimental macOS target instead:
430
453
  # type: "macos"
431
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)
432
465
 
433
466
  # Browser settings
434
467
  browser:
@@ -458,7 +491,7 @@ guardrails:
458
491
  forbiddenSelectors: # selectors that steps cannot use
459
492
  - "[data-danger]"
460
493
  - ".delete-btn"
461
- allowedApps: [] # macOS target only: bundle IDs, bundle names, or .app paths
494
+ allowedApps: [] # native targets only: macOS bundle IDs/names/.app paths, Android package IDs/canonical APK paths, or iOS bundle IDs/.app paths
462
495
 
463
496
  # Auth state from `prowl login`
464
497
  auth:
@@ -985,6 +1018,52 @@ Native selectors address accessibility identifiers, roles, and labels:
985
1018
  semantics as the web target). Prefer `id=` (accessibility identifiers) — the native
986
1019
  analog of `data-testid`.
987
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
+
988
1067
  ### Step compatibility
989
1068
 
990
1069
  Portable steps run on **both** targets; web-only steps are rejected up front on the
@@ -1009,6 +1088,202 @@ after the run.
1009
1088
 
1010
1089
  ---
1011
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
+
1012
1287
  ## Troubleshooting
1013
1288
 
1014
1289
  ### "Could not find .prowl/config.yml"