prowl-tools 0.1.5 → 0.1.6
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 +251 -9
- package/dist/{chunk-2KD2XCTH.js → chunk-5KQR3IR3.js} +1182 -234
- package/dist/chunk-5KQR3IR3.js.map +1 -0
- package/dist/{chunk-ITOSUJCN.js → chunk-WHAMB4TY.js} +5 -1
- package/dist/chunk-WHAMB4TY.js.map +1 -0
- package/dist/index.cjs +1152 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +173 -10
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +1220 -232
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +540 -53
- package/dist/lib.d.ts +540 -53
- package/dist/lib.js +74 -6
- package/dist/{loader-FCXPARP7.js → loader-PBBYV3U7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2KD2XCTH.js.map +0 -1
- package/dist/chunk-ITOSUJCN.js.map +0 -1
- /package/dist/{loader-FCXPARP7.js.map → loader-PBBYV3U7.js.map} +0 -0
package/README.md
CHANGED
|
@@ -935,6 +935,43 @@ Templates cover auth flows (OAuth, 2FA), e-commerce (Stripe), admin panels, SaaS
|
|
|
935
935
|
|
|
936
936
|
---
|
|
937
937
|
|
|
938
|
+
## Native Selector Dialect (compatibility matrix)
|
|
939
|
+
|
|
940
|
+
Android and iOS now consume one shared selector dialect implementation, so `id=`
|
|
941
|
+
/ `label=` / `text=` / `role=` mean the same *shape* of thing on both mobile
|
|
942
|
+
targets. That shared grammar, per-platform attribute mapping, ranking order, and
|
|
943
|
+
host-side matching live in `src/selector/native.ts`. macOS remains on its existing
|
|
944
|
+
driver/analyzer implementation for now, with migration deferred, but follows the
|
|
945
|
+
same documented selector shape. The web target speaks Playwright's own selector
|
|
946
|
+
engines and is shown for contrast.
|
|
947
|
+
|
|
948
|
+
| Kind | Web (Playwright) | macOS (AX) | Android (uiautomator2) | iOS (WebDriverAgent) |
|
|
949
|
+
|---|---|---|---|---|
|
|
950
|
+
| `id=` | use CSS `#id` / `[data-testid]` | `AXIdentifier`, exact | `resource-id`, exact (bare names are package-qualified: `save` → `<pkg>:id/save`) | accessibility id (the `name` attribute), exact |
|
|
951
|
+
| `label=` | *(no native kind; analyzer surfaces the associated `<label>` text)* | `title`/`description`, **exact** | `content-desc`, **exact** | `accessibilityLabel`, **exact** |
|
|
952
|
+
| `text=` (or bare) | text engine, substring, case-insensitive | `title`/`description`/`value`, substring | visible `text`, substring | `label` **or** `value`, substring |
|
|
953
|
+
| `role=` | ARIA role engine | AX role (e.g. `AXButton`) | widget class (e.g. `android.widget.Button`) | element type (`XCUIElementType…`; shorthand `Button` accepted) |
|
|
954
|
+
| `role=X[name="Y"]` | role + accessible name (substring) | role + name (substring) | class + visible-text (substring) | type + (`label` or `value`) substring |
|
|
955
|
+
| `:focus` | *(n/a)* | focused element | `UiSelector().focused(true)` | `hasKeyboardFocus == 1` |
|
|
956
|
+
|
|
957
|
+
**The `label=`-in-assertions trap.** On every native target `label=` is an **exact**
|
|
958
|
+
match on the accessibility label — unlike `text=`, which is a substring match, and
|
|
959
|
+
unlike the web, where text matching is forgiving. So `assert: selectorExists:
|
|
960
|
+
label="Save"` will **not** match an element whose real label is "Save changes"; it
|
|
961
|
+
silently fails rather than partially matching. Use `text=` when you want substring
|
|
962
|
+
behavior in an assertion, and keep `label=` for the exact accessibility label. On
|
|
963
|
+
iOS there is a second trap: WDA's page source exposes a single `name` attribute that
|
|
964
|
+
is the `accessibilityIdentifier` when one is set and otherwise the label. Prowl's
|
|
965
|
+
analyzer only recommends `id=` when `name` differs from `label`, so it does not emit
|
|
966
|
+
label-shaped ids, but runtime and host-side matching still resolve `id=` against
|
|
967
|
+
WDA's `name`.
|
|
968
|
+
|
|
969
|
+
Prefer `id=` on every native target — the native analog of `data-testid`. Per-target
|
|
970
|
+
specifics (escaping, `statusItem`/`menu=` on macOS, the Compose `testTagsAsResourceId`
|
|
971
|
+
caveat on Android) follow in each target's own section below.
|
|
972
|
+
|
|
973
|
+
---
|
|
974
|
+
|
|
938
975
|
## macOS Target (Experimental)
|
|
939
976
|
|
|
940
977
|
> **Experimental (PROWL-048).** Prowl can drive **native macOS apps** — including
|
|
@@ -1003,6 +1040,8 @@ at self-hosted / MDM-managed runners for now.
|
|
|
1003
1040
|
|
|
1004
1041
|
### Selector dialect (macOS)
|
|
1005
1042
|
|
|
1043
|
+
See the [Native Selector Dialect matrix](#native-selector-dialect-compatibility-matrix)
|
|
1044
|
+
for how these compare across native targets (and the `label=` exact-match trap).
|
|
1006
1045
|
Native selectors address accessibility identifiers, roles, and labels:
|
|
1007
1046
|
|
|
1008
1047
|
| Selector | Matches |
|
|
@@ -1139,8 +1178,10 @@ validates it before installing.
|
|
|
1139
1178
|
### Selector dialect (Android)
|
|
1140
1179
|
|
|
1141
1180
|
Native selectors address `resource-id`, `content-desc`, visible text, and widget
|
|
1142
|
-
class. Semantics match the macOS
|
|
1143
|
-
native targets
|
|
1181
|
+
class. Semantics match the macOS and iOS targets so a selector means the same thing
|
|
1182
|
+
across native targets — see the
|
|
1183
|
+
[Native Selector Dialect matrix](#native-selector-dialect-compatibility-matrix)
|
|
1184
|
+
(and the `label=` exact-match trap):
|
|
1144
1185
|
|
|
1145
1186
|
| Selector | Matches |
|
|
1146
1187
|
|---|---|
|
|
@@ -1178,9 +1219,52 @@ equivalent yet and are rejected with a clear message; scroll-gesture support is
|
|
|
1178
1219
|
follow-up. A degraded pure-`adb` fallback (`uiautomator dump` + `input tap`) is a
|
|
1179
1220
|
possible future diagnostic mode, not the primary path.
|
|
1180
1221
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1222
|
+
### Finding selectors (Android)
|
|
1223
|
+
|
|
1224
|
+
Don't guess selectors — dump them. `prowl analyze` works on the Android target the
|
|
1225
|
+
same way it does on the web and macOS: it attaches to the running app, reads the
|
|
1226
|
+
uiautomator UI hierarchy, and prints every interactive element with **ranked
|
|
1227
|
+
selector candidates** (best first). It is read-only, honors
|
|
1228
|
+
`guardrails.allowedApps`, and leaves the app running when done. Point it at a
|
|
1229
|
+
booted emulator/device:
|
|
1230
|
+
|
|
1231
|
+
```bash
|
|
1232
|
+
# Uses the Android target from .prowl/config.yml:
|
|
1233
|
+
prowl analyze
|
|
1234
|
+
|
|
1235
|
+
# …or force the Android target explicitly:
|
|
1236
|
+
prowl analyze --app com.android.settings --platform android
|
|
1237
|
+
prowl analyze --app ./app-debug.apk # an .apk implies Android
|
|
1238
|
+
prowl analyze --app com.example.app --device emulator-5556 # pick a device
|
|
1239
|
+
|
|
1240
|
+
# Machine-readable output for agents:
|
|
1241
|
+
prowl analyze --app com.android.settings --platform android --json
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
Ranking (best → last resort): `id=` (the package-qualified `resource-id`, the
|
|
1245
|
+
native `data-testid`) > `label=` (content-desc) > `role=<class>[name="<text>"]` >
|
|
1246
|
+
`text=`. Example (human-readable) output:
|
|
1247
|
+
|
|
1248
|
+
```text
|
|
1249
|
+
App Analysis: com.android.settings
|
|
1250
|
+
|
|
1251
|
+
Interactive Elements:
|
|
1252
|
+
android.widget.EditText id=com.android.settings:id/search_src_text "Search settings"
|
|
1253
|
+
android.widget.LinearLayout text="Network & internet" "Network & internet"
|
|
1254
|
+
android.widget.Switch id=com.android.settings:id/switch_widget (disabled)
|
|
1255
|
+
|
|
1256
|
+
3 elements
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
> Platform selection for `--app`: an `.apk` implies Android; otherwise pass
|
|
1260
|
+
> `--platform android` (a bare bundle-id / package is ambiguous with the macOS and
|
|
1261
|
+
> iOS targets, which default to macOS unless a config `target.type` or `--platform`
|
|
1262
|
+
> says otherwise). With an Android `target.type` in `.prowl/config.yml`, a bare
|
|
1263
|
+
> `prowl analyze` needs no flag.
|
|
1264
|
+
>
|
|
1265
|
+
> Out of scope for PROWL-058 (tracked separately): the unified native selector
|
|
1266
|
+
> engine (PROWL-060) and real iOS devices (PROWL-062). `prowl analyze` for Android
|
|
1267
|
+
> and the CI recipes shipped in PROWL-061 (above, and see "Mobile targets in CI").
|
|
1184
1268
|
|
|
1185
1269
|
---
|
|
1186
1270
|
|
|
@@ -1242,7 +1326,9 @@ treated as a **bundle id** unless a directory of that name exists, so bundle ids
|
|
|
1242
1326
|
|
|
1243
1327
|
Native selectors address accessibility ids, labels, visible text, and element type.
|
|
1244
1328
|
Semantics match the macOS/Android targets so a selector means the same thing across
|
|
1245
|
-
native targets
|
|
1329
|
+
native targets — see the
|
|
1330
|
+
[Native Selector Dialect matrix](#native-selector-dialect-compatibility-matrix)
|
|
1331
|
+
(and the `label=` exact-match trap):
|
|
1246
1332
|
|
|
1247
1333
|
| Selector | Matches |
|
|
1248
1334
|
|---|---|
|
|
@@ -1278,9 +1364,165 @@ message. Screenshots are captured with `simctl` (not WDA), so artifacts still wo
|
|
|
1278
1364
|
even if the agent wedges. `hover` and `scrollTo` have no touch equivalent yet and are
|
|
1279
1365
|
rejected with a clear message; scroll-gesture support is a follow-up.
|
|
1280
1366
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1367
|
+
### Finding selectors (iOS)
|
|
1368
|
+
|
|
1369
|
+
Don't guess selectors — dump them. `prowl analyze` works on the iOS target the
|
|
1370
|
+
same way it does on the web, macOS, and Android: it attaches to the running app on
|
|
1371
|
+
a booted simulator, reads WebDriverAgent's UI hierarchy, and prints every
|
|
1372
|
+
interactive element (plus the app's windows) with **ranked selector candidates**
|
|
1373
|
+
(best first). It is read-only, honors `guardrails.allowedApps`, and leaves the app
|
|
1374
|
+
running when done:
|
|
1375
|
+
|
|
1376
|
+
```bash
|
|
1377
|
+
# Uses the iOS target from .prowl/config.yml:
|
|
1378
|
+
prowl analyze
|
|
1379
|
+
|
|
1380
|
+
# …or force the iOS target explicitly:
|
|
1381
|
+
prowl analyze --app com.apple.Preferences --platform ios
|
|
1382
|
+
prowl analyze --app com.example.App --platform ios --udid <SIM-UDID>
|
|
1383
|
+
|
|
1384
|
+
# Machine-readable output for agents:
|
|
1385
|
+
prowl analyze --app com.apple.Preferences --platform ios --json
|
|
1386
|
+
```
|
|
1387
|
+
|
|
1388
|
+
Ranking (best → last resort): `id=` (accessibility id) > `label=` > `role=<Type>
|
|
1389
|
+
[name="<text>"]` > `text=`. Because WDA's page source exposes only a single `name`
|
|
1390
|
+
attribute — the accessibility identifier when set, otherwise the label — `id=` is
|
|
1391
|
+
offered only when that `name` differs from the element's label. Example output:
|
|
1392
|
+
|
|
1393
|
+
```text
|
|
1394
|
+
App Analysis: com.apple.Preferences
|
|
1395
|
+
|
|
1396
|
+
Windows:
|
|
1397
|
+
(untitled) role=Window
|
|
1398
|
+
|
|
1399
|
+
Interactive Elements:
|
|
1400
|
+
XCUIElementTypeButton id=general_button "General"
|
|
1401
|
+
XCUIElementTypeCell label="Wi-Fi" "Wi-Fi"
|
|
1402
|
+
XCUIElementTypeSwitch label="Airplane Mode" "Airplane Mode" (disabled)
|
|
1403
|
+
|
|
1404
|
+
3 elements, 1 windows
|
|
1405
|
+
```
|
|
1406
|
+
|
|
1407
|
+
> A bare bundle-id `--app` is ambiguous with the macOS target (which is the
|
|
1408
|
+
> default), so pass `--platform ios`. With an iOS `target.type` in
|
|
1409
|
+
> `.prowl/config.yml`, a bare `prowl analyze` needs no flag.
|
|
1410
|
+
>
|
|
1411
|
+
> Out of scope for PROWL-059 (tracked separately): the unified native selector
|
|
1412
|
+
> engine (PROWL-060) and real iOS devices (PROWL-062). `prowl analyze` for iOS and
|
|
1413
|
+
> the CI recipes shipped in PROWL-061 (above, and see "Mobile targets in CI").
|
|
1414
|
+
|
|
1415
|
+
---
|
|
1416
|
+
|
|
1417
|
+
## Mobile targets in CI
|
|
1418
|
+
|
|
1419
|
+
The Android and iOS targets run in continuous integration, either on GitHub-hosted
|
|
1420
|
+
runners or on a self-hosted Mac. Every recipe runs the real `prowl` CLI against a
|
|
1421
|
+
booted emulator/simulator and uploads run artifacts (screenshots, JUnit, reports).
|
|
1422
|
+
|
|
1423
|
+
### Android on `ubuntu-latest` (GitHub-hosted)
|
|
1424
|
+
|
|
1425
|
+
GitHub's Linux runners support KVM, so a hardware-accelerated emulator boots in the
|
|
1426
|
+
job via [`reactivecircus/android-emulator-runner`](https://github.com/ReactiveCircus/android-emulator-runner).
|
|
1427
|
+
Prowl installs the uiautomator2 agent APKs from its optional dependency automatically.
|
|
1428
|
+
|
|
1429
|
+
```yaml
|
|
1430
|
+
name: Android E2E
|
|
1431
|
+
on: [push, pull_request]
|
|
1432
|
+
jobs:
|
|
1433
|
+
android:
|
|
1434
|
+
runs-on: ubuntu-latest
|
|
1435
|
+
steps:
|
|
1436
|
+
- uses: actions/checkout@v4
|
|
1437
|
+
- uses: actions/setup-node@v4
|
|
1438
|
+
with:
|
|
1439
|
+
node-version: 20
|
|
1440
|
+
- run: npm ci
|
|
1441
|
+
- run: npm run build
|
|
1442
|
+
|
|
1443
|
+
# KVM must be accessible for a fast emulator.
|
|
1444
|
+
- name: Enable KVM
|
|
1445
|
+
run: |
|
|
1446
|
+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
1447
|
+
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
1448
|
+
sudo udevadm control --reload-rules
|
|
1449
|
+
sudo udevadm trigger --name-match=kvm
|
|
1450
|
+
|
|
1451
|
+
- name: Run hunts against the emulator
|
|
1452
|
+
uses: reactivecircus/android-emulator-runner@v2
|
|
1453
|
+
with:
|
|
1454
|
+
api-level: 34
|
|
1455
|
+
arch: x86_64
|
|
1456
|
+
force-avd-creation: false
|
|
1457
|
+
emulator-options: -no-window -no-audio -no-boot-anim -no-snapshot -gpu swiftshader_indirect
|
|
1458
|
+
disable-animations: true
|
|
1459
|
+
# `prowl` is your installed CLI (e.g. `npx prowl` or a global install);
|
|
1460
|
+
# the emulator is booted and on adb by the time this runs.
|
|
1461
|
+
script: npx prowl ci --junit
|
|
1462
|
+
|
|
1463
|
+
- name: Upload artifacts
|
|
1464
|
+
if: always()
|
|
1465
|
+
uses: actions/upload-artifact@v4
|
|
1466
|
+
with:
|
|
1467
|
+
name: android-artifacts
|
|
1468
|
+
path: .prowl/runs/**
|
|
1469
|
+
if-no-files-found: ignore
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
### iOS simulators on `macos-*` (GitHub-hosted)
|
|
1473
|
+
|
|
1474
|
+
macOS runners ship Xcode and the iOS simulator runtimes. Boot a simulator with
|
|
1475
|
+
`xcrun simctl`, and cache the one-time WebDriverAgent build (`~/.prowl/wda/`, keyed
|
|
1476
|
+
on the WDA + Xcode versions) so subsequent runs skip the ~2-minute `xcodebuild`.
|
|
1477
|
+
|
|
1478
|
+
```yaml
|
|
1479
|
+
name: iOS E2E
|
|
1480
|
+
on: [push, pull_request]
|
|
1481
|
+
jobs:
|
|
1482
|
+
ios:
|
|
1483
|
+
runs-on: macos-15
|
|
1484
|
+
steps:
|
|
1485
|
+
- uses: actions/checkout@v4
|
|
1486
|
+
- uses: actions/setup-node@v4
|
|
1487
|
+
with:
|
|
1488
|
+
node-version: 20
|
|
1489
|
+
- run: npm ci
|
|
1490
|
+
- run: npm run build
|
|
1491
|
+
|
|
1492
|
+
# Cache the built WebDriverAgent runner across runs.
|
|
1493
|
+
- name: Cache WebDriverAgent
|
|
1494
|
+
uses: actions/cache@v4
|
|
1495
|
+
with:
|
|
1496
|
+
path: ~/.prowl/wda
|
|
1497
|
+
key: prowl-wda-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
|
1498
|
+
|
|
1499
|
+
- name: Boot a simulator
|
|
1500
|
+
run: |
|
|
1501
|
+
xcrun simctl boot "iPhone 16" || true
|
|
1502
|
+
xcrun simctl bootstatus "iPhone 16"
|
|
1503
|
+
|
|
1504
|
+
- name: Run hunts against the simulator
|
|
1505
|
+
run: npx prowl ci --junit
|
|
1506
|
+
|
|
1507
|
+
- name: Upload artifacts
|
|
1508
|
+
if: always()
|
|
1509
|
+
uses: actions/upload-artifact@v4
|
|
1510
|
+
with:
|
|
1511
|
+
name: ios-artifacts
|
|
1512
|
+
path: .prowl/runs/**
|
|
1513
|
+
if-no-files-found: ignore
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
### Self-hosted device-verification gate
|
|
1517
|
+
|
|
1518
|
+
This repo also ships `.github/workflows/mobile-e2e.yml`, a real end-to-end gate on
|
|
1519
|
+
the Prowl Tools self-hosted Mac (labels `self-hosted, macOS, prowl-mobile`) that
|
|
1520
|
+
boots both a headless emulator and a simulator and drives Settings on each through
|
|
1521
|
+
the real CLI. It runs on `workflow_dispatch` (the owner's post-merge verification)
|
|
1522
|
+
and on same-repo pull requests, skipping cleanly (green) for forks/outside PRs that
|
|
1523
|
+
can't reach the runner, and uses its own `concurrency` group so it never collides
|
|
1524
|
+
with other jobs on the shared box. It is the machine-run version of the manual
|
|
1525
|
+
smoke tests that caught the uiautomator2 wire-shape bug and the WDA readiness hang.
|
|
1284
1526
|
|
|
1285
1527
|
---
|
|
1286
1528
|
|