zig-mobile-runner 0.1.0
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/CHANGELOG.md +484 -0
- package/CONTRIBUTING.md +42 -0
- package/FEATURES.md +112 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/SECURITY.md +34 -0
- package/build.zig +38 -0
- package/build.zig.zon +7 -0
- package/clients/README.md +144 -0
- package/clients/go/README.md +24 -0
- package/clients/go/examples/fake-session/main.go +93 -0
- package/clients/go/go.mod +3 -0
- package/clients/go/zmr/client.go +432 -0
- package/clients/kotlin/README.md +35 -0
- package/clients/kotlin/build.gradle.kts +35 -0
- package/clients/kotlin/settings.gradle.kts +15 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/FakeSession.kt +86 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +67 -0
- package/clients/python/README.md +29 -0
- package/clients/python/examples/fake_session.py +48 -0
- package/clients/python/pyproject.toml +13 -0
- package/clients/python/zmr_client.py +202 -0
- package/clients/rust/Cargo.lock +107 -0
- package/clients/rust/Cargo.toml +10 -0
- package/clients/rust/README.md +19 -0
- package/clients/rust/examples/fake_session.rs +70 -0
- package/clients/rust/src/lib.rs +461 -0
- package/clients/swift/Package.swift +16 -0
- package/clients/swift/README.md +36 -0
- package/clients/swift/Sources/ZMRClient/ZMRClient.swift +114 -0
- package/clients/swift/Sources/ZMRFakeSession/main.swift +86 -0
- package/clients/typescript/README.md +34 -0
- package/clients/typescript/examples/fake-session.mjs +36 -0
- package/clients/typescript/index.d.ts +144 -0
- package/clients/typescript/index.mjs +192 -0
- package/clients/typescript/package.json +8 -0
- package/docs/adr/0001-agent-native-runner-boundary.md +31 -0
- package/docs/adr/0002-app-local-zmr-contract.md +39 -0
- package/docs/adr/0003-ios-simulator-xctest-shim.md +41 -0
- package/docs/adr/0004-benchmark-claims-and-baseline-collection.md +37 -0
- package/docs/adr/README.md +12 -0
- package/docs/ai-agents.md +156 -0
- package/docs/app-integration.md +316 -0
- package/docs/benchmarking.md +275 -0
- package/docs/client-installation.md +141 -0
- package/docs/clients.md +98 -0
- package/docs/config.md +175 -0
- package/docs/demo.md +259 -0
- package/docs/dsl.md +57 -0
- package/docs/install.md +233 -0
- package/docs/market-positioning.md +70 -0
- package/docs/npm.md +359 -0
- package/docs/protocol-fixtures/README.md +8 -0
- package/docs/protocol-fixtures/core-session.requests.jsonl +8 -0
- package/docs/protocol-fixtures/core-session.responses.jsonl +8 -0
- package/docs/protocol-versioning.md +65 -0
- package/docs/protocol.md +560 -0
- package/docs/publication.md +77 -0
- package/docs/release-audit.md +99 -0
- package/docs/release-candidate.md +111 -0
- package/docs/release-evidence.md +188 -0
- package/docs/release-notes-template.md +58 -0
- package/docs/roadmap.md +334 -0
- package/docs/scenario-authoring.md +88 -0
- package/docs/shipping.md +170 -0
- package/docs/trace-privacy.md +88 -0
- package/docs/troubleshooting.md +256 -0
- package/examples/android-app-auth-probe.json +89 -0
- package/examples/android-app-error-state.json +13 -0
- package/examples/android-app-login-smoke.json +192 -0
- package/examples/android-app-onboarding.json +12 -0
- package/examples/android-app-referral-deep-link.json +12 -0
- package/examples/android-shim-smoke.json +19 -0
- package/examples/demo-failure.json +12 -0
- package/examples/demo-fake.json +14 -0
- package/examples/ios-dev-client-open-link.json +26 -0
- package/examples/ios-dev-client-route-snapshot.json +24 -0
- package/examples/ios-shim-smoke.json +23 -0
- package/examples/ios-smoke.json +9 -0
- package/go.work +3 -0
- package/npm/agents.mjs +183 -0
- package/npm/app-config.mjs +95 -0
- package/npm/build-zmr.mjs +21 -0
- package/npm/commands.mjs +104 -0
- package/npm/generated-files.mjs +50 -0
- package/npm/index.mjs +75 -0
- package/npm/init-app.mjs +80 -0
- package/npm/package-scripts.mjs +72 -0
- package/npm/postinstall.mjs +21 -0
- package/npm/scaffold.mjs +179 -0
- package/npm/scenarios.mjs +93 -0
- package/npm/setup.mjs +69 -0
- package/npm/wizard.mjs +117 -0
- package/npm/zmr.mjs +23 -0
- package/package.json +114 -0
- package/prebuilds/darwin-arm64/zmr +0 -0
- package/prebuilds/darwin-x64/zmr +0 -0
- package/prebuilds/linux-arm64/zmr +0 -0
- package/prebuilds/linux-x64/zmr +0 -0
- package/schemas/README.md +26 -0
- package/schemas/action-result.schema.json +27 -0
- package/schemas/capabilities-output.schema.json +98 -0
- package/schemas/devices-output.schema.json +25 -0
- package/schemas/doctor-output.schema.json +51 -0
- package/schemas/explain-output.schema.json +51 -0
- package/schemas/import-output.schema.json +23 -0
- package/schemas/init-output.schema.json +71 -0
- package/schemas/json-rpc.schema.json +55 -0
- package/schemas/release-manifest.schema.json +43 -0
- package/schemas/release-readiness-output.schema.json +127 -0
- package/schemas/run-output.schema.json +43 -0
- package/schemas/scenario.schema.json +128 -0
- package/schemas/schemas-output.schema.json +26 -0
- package/schemas/semantic-snapshot.schema.json +116 -0
- package/schemas/snapshot.schema.json +60 -0
- package/schemas/trace-event.schema.json +14 -0
- package/schemas/trace-manifest.schema.json +59 -0
- package/schemas/validate-output.schema.json +42 -0
- package/schemas/version-output.schema.json +23 -0
- package/schemas/zmr-config.schema.json +75 -0
- package/scripts/android-emulator.sh +126 -0
- package/scripts/assert-ios-physical-ready.sh +213 -0
- package/scripts/benchmark-command.sh +307 -0
- package/scripts/benchmark.sh +359 -0
- package/scripts/benchmark_gate.py +117 -0
- package/scripts/benchmark_result_row.py +88 -0
- package/scripts/compare-benchmarks.py +288 -0
- package/scripts/create-android-demo-app.sh +342 -0
- package/scripts/create-ios-demo-app.sh +261 -0
- package/scripts/demo-android-real.sh +232 -0
- package/scripts/demo-ios-real.sh +270 -0
- package/scripts/demo.sh +464 -0
- package/scripts/device-matrix.sh +338 -0
- package/scripts/ensure-ios-shim-target.rb +237 -0
- package/scripts/install-android-shim.sh +281 -0
- package/scripts/install-ios-shim.sh +589 -0
- package/scripts/pilot-gate.sh +560 -0
- package/scripts/release-readiness.py +838 -0
- package/scripts/release-readiness.sh +91 -0
- package/scripts/run-android-pilot.sh +561 -0
- package/scripts/run-ios-pilot.sh +509 -0
- package/shims/android/README.md +21 -0
- package/shims/android/ZMRShimInstrumentedTest.java +152 -0
- package/shims/android/protocol.md +18 -0
- package/shims/ios/README.md +50 -0
- package/shims/ios/ZMRShim.swift +110 -0
- package/shims/ios/ZMRShimUITestCase.swift +475 -0
- package/shims/ios/protocol.md +74 -0
- package/skills/zmr-mobile-testing/SKILL.md +127 -0
- package/src/android.zig +344 -0
- package/src/android_device_info.zig +99 -0
- package/src/android_emulator.zig +154 -0
- package/src/android_screen_recording.zig +112 -0
- package/src/android_shell.zig +112 -0
- package/src/bundle.zig +124 -0
- package/src/bundle_redaction.zig +272 -0
- package/src/bundle_tar.zig +123 -0
- package/src/cli_devices.zig +97 -0
- package/src/cli_doctor.zig +114 -0
- package/src/cli_import.zig +70 -0
- package/src/cli_info.zig +39 -0
- package/src/cli_init.zig +72 -0
- package/src/cli_output.zig +467 -0
- package/src/cli_run.zig +259 -0
- package/src/cli_serve.zig +287 -0
- package/src/cli_trace.zig +111 -0
- package/src/cli_validate.zig +41 -0
- package/src/command.zig +211 -0
- package/src/config.zig +305 -0
- package/src/config_diagnostics.zig +212 -0
- package/src/config_paths.zig +49 -0
- package/src/device_registry.zig +37 -0
- package/src/doctor.zig +412 -0
- package/src/doctor_hints.zig +52 -0
- package/src/errors.zig +55 -0
- package/src/fake_device.zig +163 -0
- package/src/health.zig +28 -0
- package/src/importer.zig +343 -0
- package/src/importer_json.zig +100 -0
- package/src/importer_model.zig +103 -0
- package/src/ios.zig +399 -0
- package/src/ios_devices.zig +219 -0
- package/src/ios_lifecycle.zig +72 -0
- package/src/ios_shim.zig +242 -0
- package/src/ios_snapshot.zig +20 -0
- package/src/json_fields.zig +80 -0
- package/src/json_rpc.zig +150 -0
- package/src/json_rpc_methods.zig +318 -0
- package/src/json_rpc_observation.zig +31 -0
- package/src/json_rpc_params.zig +52 -0
- package/src/json_rpc_protocol.zig +110 -0
- package/src/json_rpc_trace.zig +73 -0
- package/src/main.zig +135 -0
- package/src/mcp.zig +234 -0
- package/src/mcp_protocol.zig +64 -0
- package/src/mcp_trace.zig +83 -0
- package/src/report.zig +346 -0
- package/src/report_html.zig +63 -0
- package/src/report_values.zig +27 -0
- package/src/run_options.zig +152 -0
- package/src/runner.zig +280 -0
- package/src/runner_actions.zig +109 -0
- package/src/runner_config.zig +6 -0
- package/src/runner_diagnostics.zig +268 -0
- package/src/runner_events.zig +170 -0
- package/src/runner_native.zig +88 -0
- package/src/runner_waits.zig +300 -0
- package/src/scaffold.zig +472 -0
- package/src/scenario.zig +346 -0
- package/src/scenario_fields.zig +50 -0
- package/src/schema_registry.zig +53 -0
- package/src/selector.zig +84 -0
- package/src/semantic.zig +171 -0
- package/src/trace.zig +315 -0
- package/src/trace_json.zig +340 -0
- package/src/trace_summary.zig +218 -0
- package/src/trace_summary_diagnostic.zig +202 -0
- package/src/types.zig +120 -0
- package/src/uiautomator.zig +164 -0
- package/src/validation.zig +187 -0
- package/src/version.zig +22 -0
- package/viewer/app.js +373 -0
- package/viewer/index.html +126 -0
- package/viewer/parser.js +233 -0
- package/viewer/styles.css +585 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Start with structured diagnostics instead of reading terminal output by hand:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
zmr doctor --json
|
|
7
|
+
zmr doctor --strict --json
|
|
8
|
+
zmr validate --json .zmr/android-smoke.json
|
|
9
|
+
zmr explain traces/zmr-android
|
|
10
|
+
./scripts/release-gate.sh --dry-run
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`zmr doctor --json` is the first command to run when setup is unclear. It
|
|
14
|
+
reports Zig, ADB, Android device count, `xcrun`, iOS simulator state, physical
|
|
15
|
+
iOS device state, and configured Android/iOS shim command paths in a
|
|
16
|
+
machine-readable shape that scripts and agents can inspect. Device readiness
|
|
17
|
+
checks report `warning` when ADB sees zero devices, `xcrun` sees zero booted
|
|
18
|
+
iOS simulators, `devicectl` sees zero paired physical iOS devices, or all
|
|
19
|
+
listed physical devices are disconnected/unavailable, with stable
|
|
20
|
+
`setup.android.no_devices`, `setup.ios.no_booted_simulators`,
|
|
21
|
+
`setup.ios.no_physical_devices`, and
|
|
22
|
+
`setup.ios.no_ready_physical_devices` error codes.
|
|
23
|
+
Missing tool and shim checks also include stable setup codes such as
|
|
24
|
+
`setup.adb.not_found` and `setup.android_shim.not_found`.
|
|
25
|
+
By default `doctor` exits zero after printing diagnostics so interactive setup
|
|
26
|
+
can keep going; add `--strict` when CI or install scripts should exit non-zero
|
|
27
|
+
for any warning or missing check.
|
|
28
|
+
When run with `--config .zmr/config.json`, it
|
|
29
|
+
first reports whether the config file itself loaded, then validates configured
|
|
30
|
+
smoke scenario files from `android.smokeScenario` and
|
|
31
|
+
`ios.smokeScenario` so app-local setup mistakes fail before device orchestration
|
|
32
|
+
starts. Config files with wrong types, such as string values for boolean
|
|
33
|
+
artifact controls, unknown fields, such as misspelled `smokeScenario`, or empty strings
|
|
34
|
+
where paths/app ids/script commands are required are reported as `config` warnings.
|
|
35
|
+
Those warnings include `fieldPath` in JSON mode when ZMR can identify the
|
|
36
|
+
invalid `.zmr/config.json` key, plus stable `errorCode` values for setup
|
|
37
|
+
automation.
|
|
38
|
+
Missing scenario files are reported as `missing`; malformed scenario files are
|
|
39
|
+
reported as `warning` with a hint to run `zmr validate` on the configured file.
|
|
40
|
+
Non-`ok` checks include a `"hint"` field with the next concrete remediation
|
|
41
|
+
step. The JSON contract is published at
|
|
42
|
+
`schemas/doctor-output.schema.json`.
|
|
43
|
+
|
|
44
|
+
Top-level CLI failures use stable public error codes instead of Zig stack
|
|
45
|
+
traces. For example, `error[device.command_failed]: device command failed`
|
|
46
|
+
means a platform command such as ADB or `xcrun` failed before ZMR could collect
|
|
47
|
+
a device response; run `zmr doctor --json` next for setup details. An
|
|
48
|
+
`error[cli.unknown_command]: unknown command` response means the command name
|
|
49
|
+
was not recognized; run `zmr help` to inspect the supported CLI surface.
|
|
50
|
+
|
|
51
|
+
The real Android and iOS pilot wrappers run setup preflights before expensive
|
|
52
|
+
install or benchmark work. `scripts/run-android-pilot.sh --skip-emulator`
|
|
53
|
+
reports `no Android device found: <serial>` plus `setup.android.no_devices`
|
|
54
|
+
when the requested serial is not attached. `scripts/run-ios-pilot.sh` reports
|
|
55
|
+
`no booted iOS simulator found` plus `setup.ios.no_booted_simulators` when
|
|
56
|
+
`--device booted` has no target. Physical iOS pilot runs report
|
|
57
|
+
`setup.ios.physical_device_required`, `setup.ios.physical_device_not_found`, or
|
|
58
|
+
`setup.ios.physical_device_not_ready` for invalid or disconnected physical
|
|
59
|
+
device identifiers. Use the `serial` value from `zmr devices --json --platform
|
|
60
|
+
ios --ios-device-type physical`. The not-ready preflight also prints the
|
|
61
|
+
matched device state, such as
|
|
62
|
+
`state: disconnected`, while `zmr doctor --json` reports the broader
|
|
63
|
+
`ios-physical-devices` check with
|
|
64
|
+
`setup.ios.no_physical_devices` when no physical devices are listed, or
|
|
65
|
+
`setup.ios.no_ready_physical_devices` when only disconnected/unavailable
|
|
66
|
+
devices are listed. The no-ready detail includes a state breakdown such as
|
|
67
|
+
`disconnected=1, unavailable=1`. When at least one physical device is ready but
|
|
68
|
+
other devices are listed in unusable states, `doctor` keeps the check `ok` and
|
|
69
|
+
still includes the broader breakdown, for example
|
|
70
|
+
`1 ready physical iOS device(s); 3 listed (disconnected=1, unavailable=1)`.
|
|
71
|
+
In both cases the wrapper also prints the matching `zmr doctor --json` output
|
|
72
|
+
so CI logs contain the next remediation.
|
|
73
|
+
The JSON output also includes numeric `count` and `readyCount` fields on
|
|
74
|
+
device checks, so agents and scripts can branch without parsing the human
|
|
75
|
+
`detail` string.
|
|
76
|
+
|
|
77
|
+
## Install Or Binary Issues
|
|
78
|
+
|
|
79
|
+
If `zmr` is not found from an app repo, check the npm wrapper resolution order:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
node -e 'import("zig-mobile-runner").then(m => console.log(m.resolveBinary()))'
|
|
83
|
+
npx zmr version
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The npm wrapper resolves `ZMR_BIN`, then bundled prebuilds, then
|
|
87
|
+
`zig-out/bin/zmr`. If none exist, install Zig and run:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm run build:zmr
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For source checkouts on this macOS host, use the explicit macOS 15 target shown
|
|
94
|
+
in the README:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
zig test src/main.zig -target aarch64-macos.15.0
|
|
98
|
+
zig build-exe src/main.zig -target aarch64-macos.15.0 -O Debug -femit-bin=zig-out/bin/zmr
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Scenario Issues
|
|
102
|
+
|
|
103
|
+
Validate scenarios before touching a device:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
zmr validate --json .zmr/android-smoke.json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The JSON output includes `errorCode`, `fieldPath`, `line`, and `column` when ZMR
|
|
110
|
+
can locate the source. Fix schema and selector mistakes there first; device
|
|
111
|
+
state debugging is slower and less reliable when the scenario itself is invalid.
|
|
112
|
+
|
|
113
|
+
## Android Device Issues
|
|
114
|
+
|
|
115
|
+
For a real emulator run, confirm that ADB sees exactly the device you intend to
|
|
116
|
+
use:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
adb devices
|
|
120
|
+
zmr doctor --adb adb
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If repeated local runs need a known state, prefer ZMR's emulator lifecycle flags
|
|
124
|
+
or the Android pilot wrapper:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
zmr run .zmr/android-smoke.json \
|
|
128
|
+
--android-avd Small_Phone \
|
|
129
|
+
--create-avd-if-missing \
|
|
130
|
+
--avd-system-image 'system-images;android-35;google_apis;arm64-v8a' \
|
|
131
|
+
--avd-device pixel_6 \
|
|
132
|
+
--restore-snapshot zmr-clean \
|
|
133
|
+
--wait-emulator \
|
|
134
|
+
--device emulator-5554
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If selector actions are slow or flaky through shell/UI Automator, install the
|
|
138
|
+
Android shim in the app repo:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
npx zmr-install-android-shim \
|
|
142
|
+
--app-root . \
|
|
143
|
+
--test-package com.example.mobiletest.test \
|
|
144
|
+
--android-module android/app \
|
|
145
|
+
--gradle-file android/app/build.gradle
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Then run with `--android-shim ./.zmr/android-shim` or set
|
|
149
|
+
`tools.androidShimPath` in `.zmr/config.json`.
|
|
150
|
+
|
|
151
|
+
## iOS Simulator Issues
|
|
152
|
+
|
|
153
|
+
Confirm that `xcrun` sees a booted simulator:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
xcrun simctl list devices booted
|
|
157
|
+
zmr doctor --xcrun xcrun
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The simulator `.app` must be built and installed before launch/open-link flows.
|
|
161
|
+
A device `.ipa` is not simulator-compatible; `scripts/run-ios-pilot.sh` rejects
|
|
162
|
+
that mismatch with `setup.ios.simulator_app_required`. Build an
|
|
163
|
+
`iphonesimulator` `.app` for simulator pilots, or pass `--ios-device-type
|
|
164
|
+
physical` with a signed device artifact and a real physical device identifier.
|
|
165
|
+
|
|
166
|
+
On iOS, `clearState` is best-effort uninstall by bundle id; if the app is
|
|
167
|
+
already missing, ZMR treats the simulator as clean and continues. Simulator
|
|
168
|
+
`launch` is also idempotent when an XCTest shim is configured: if `simctl
|
|
169
|
+
launch` reports an error but `{"cmd":"appState"}` shows the app is already
|
|
170
|
+
running, ZMR treats the app as usable instead of failing the scenario.
|
|
171
|
+
|
|
172
|
+
Selector-grade iOS actions require an XCTest/XCUIAutomation shim command:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx zmr-install-ios-shim \
|
|
176
|
+
--app-root . \
|
|
177
|
+
--scheme SampleUITests \
|
|
178
|
+
--workspace ios/Sample.xcworkspace \
|
|
179
|
+
--app-target SampleApp \
|
|
180
|
+
--bundle-id com.example.mobiletest \
|
|
181
|
+
--patch-xcodeproj
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The generated `.zmr/ensure-ios-shim-target.sh` helper resolves the referenced
|
|
185
|
+
`.xcodeproj` from the workspace when there is one project, or when exactly one
|
|
186
|
+
project contains `--app-target`, or when `--bundle-id` disambiguates matching
|
|
187
|
+
app targets. Pass `--project` explicitly for still-ambiguous multi-project
|
|
188
|
+
workspaces. Run with `--ios-shim ./.zmr/ios-shim` or set
|
|
189
|
+
`tools.iosShimPath` in `.zmr/config.json`.
|
|
190
|
+
|
|
191
|
+
If a real iOS run fails with CoreSimulator or Xcode cache errors such as
|
|
192
|
+
`Operation not permitted`, `CoreSimulatorService connection became invalid`, or
|
|
193
|
+
an unexpected workspace/build database error, rerun from a normal terminal or CI
|
|
194
|
+
worker that has access to the user's Xcode, simulator, and DerivedData paths.
|
|
195
|
+
These errors usually mean the host process is sandboxed away from Apple's local
|
|
196
|
+
developer services, not that the scenario JSON is malformed.
|
|
197
|
+
For the generated public iOS demo, `scripts/demo-ios-real.sh --device booted`
|
|
198
|
+
tries available iOS simulators in order when no simulator is already booted.
|
|
199
|
+
This avoids failing the whole demo when one local simulator cannot start
|
|
200
|
+
`launchd_sim`, while still surfacing a setup error if every available simulator
|
|
201
|
+
fails to boot.
|
|
202
|
+
|
|
203
|
+
If a previous Xcode build was interrupted, remove only the app-local ZMR derived
|
|
204
|
+
data path configured for the shim, then rerun the shim once to prewarm it:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
rm -rf ios/build/ZMRDerivedData
|
|
208
|
+
printf '{"cmd":"appState"}\n' | ./.zmr/ios-shim
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Cold shim builds can take several minutes on large apps. Warm runs should use
|
|
212
|
+
the cached `build-for-testing` output and respond much faster.
|
|
213
|
+
`scripts/run-ios-pilot.sh` performs this prewarm automatically when
|
|
214
|
+
`--ios-shim` is set; pass `--skip-shim-prewarm` only when debugging cold-start
|
|
215
|
+
timing.
|
|
216
|
+
|
|
217
|
+
If a freshly booted simulator reports `iOS shim server exited before it became
|
|
218
|
+
ready`, `Early unexpected exit`, or `operation never finished bootstrapping`,
|
|
219
|
+
ZMR retries the shim command once because XCTest can miss its first server
|
|
220
|
+
bootstrap immediately after CoreSimulator startup. Persistent failures after
|
|
221
|
+
that retry should be treated as setup failures: run `xcrun simctl bootstatus
|
|
222
|
+
booted -b`, inspect `.zmr/ios-shim-state/xcodebuild.log`, and prewarm the shim
|
|
223
|
+
with the `appState` command above.
|
|
224
|
+
|
|
225
|
+
## Trace And Failure Issues
|
|
226
|
+
|
|
227
|
+
When a run fails, do not rerun blindly. Inspect the recorded failure:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
zmr explain traces/zmr-android
|
|
231
|
+
zmr report traces/zmr-android --out traces/zmr-android/report.html
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Timeout diagnostics include the active package/activity, visible text, hidden or
|
|
235
|
+
disabled exact selector candidates, offscreen candidates, and nearest text
|
|
236
|
+
matches from the last snapshot. Redacted bundles replace PNG screenshots with
|
|
237
|
+
placeholder frames and omit screen recordings, so use them for sharing:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
zmr export traces/zmr-android --out traces/zmr-android-redacted.zmrtrace --redact
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Release Gate Issues
|
|
244
|
+
|
|
245
|
+
To see exactly what the local release gate will run:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
./scripts/release-gate.sh --dry-run
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The gate intentionally prints real Android and iOS pilot commands at the end
|
|
252
|
+
instead of running them by default. Those pilots need app builds and devices, so
|
|
253
|
+
run them explicitly before publishing reliability or performance claims. Use
|
|
254
|
+
`zmr-pilot-gate --dry-run` with the same app path flags from an app repo or
|
|
255
|
+
release machine to inspect the combined Android+iOS external gate command
|
|
256
|
+
before starting the real runs.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Sample App auth probe",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"action": "stop"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"action": "openLink",
|
|
10
|
+
"url": "exampleapp:///e2e-auth?probe=1"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"action": "waitAny",
|
|
14
|
+
"selectors": [
|
|
15
|
+
{
|
|
16
|
+
"text": "E2E auth probe"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"text": "Deep link received:"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"text": "Development servers"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"timeoutMs": 60000
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"action": "whenVisible",
|
|
29
|
+
"selector": {
|
|
30
|
+
"text": "Deep link received:"
|
|
31
|
+
},
|
|
32
|
+
"steps": [
|
|
33
|
+
{
|
|
34
|
+
"action": "tap",
|
|
35
|
+
"selector": {
|
|
36
|
+
"contentDescContains": "Sample App Test on"
|
|
37
|
+
},
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"action": "tap",
|
|
42
|
+
"selector": {
|
|
43
|
+
"contentDesc": "Sample App Test"
|
|
44
|
+
},
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"action": "whenVisible",
|
|
51
|
+
"selector": {
|
|
52
|
+
"text": "Development servers"
|
|
53
|
+
},
|
|
54
|
+
"steps": [
|
|
55
|
+
{
|
|
56
|
+
"action": "tap",
|
|
57
|
+
"selector": {
|
|
58
|
+
"contentDescContains": "Sample App Test on"
|
|
59
|
+
},
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"action": "tap",
|
|
64
|
+
"selector": {
|
|
65
|
+
"contentDesc": "Sample App Test, http://10.0.2.2:8081"
|
|
66
|
+
},
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"action": "tap",
|
|
71
|
+
"selector": {
|
|
72
|
+
"text": "http://10.0.2.2:8081"
|
|
73
|
+
},
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"action": "waitVisible",
|
|
80
|
+
"selector": {
|
|
81
|
+
"text": "E2E auth probe"
|
|
82
|
+
},
|
|
83
|
+
"timeoutMs": 120000
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"action": "snapshot"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Android app error-state smoke",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "launch" },
|
|
6
|
+
{ "action": "openLink", "url": "exampleapp://error-demo?mode=offline" },
|
|
7
|
+
{ "action": "waitAny", "selectors": [{ "textContains": "Try again" }, { "textContains": "offline" }, { "textContains": "Something went wrong" }], "timeoutMs": 15000 },
|
|
8
|
+
{ "action": "snapshot" },
|
|
9
|
+
{ "action": "tap", "selector": { "textContains": "Try again" }, "optional": true },
|
|
10
|
+
{ "action": "waitNotVisible", "selector": { "textContains": "Loading" }, "timeoutMs": 10000 },
|
|
11
|
+
{ "action": "assertHealthy" }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Sample App email login and referral smoke",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"action": "stop"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"action": "clearState"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"action": "launch"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"action": "waitAny",
|
|
16
|
+
"selectors": [
|
|
17
|
+
{ "textContains": "Development servers" },
|
|
18
|
+
{ "textContains": "Downloading 100%" },
|
|
19
|
+
{ "text": "Or sign up via email here" },
|
|
20
|
+
{ "text": "Sign in with email" },
|
|
21
|
+
{ "textContains": "Dashboard" },
|
|
22
|
+
{ "textContains": "Request a feature" },
|
|
23
|
+
{ "textContains": "Earn points" },
|
|
24
|
+
{ "textContains": "Spend points" },
|
|
25
|
+
{ "textContains": "points" }
|
|
26
|
+
],
|
|
27
|
+
"timeoutMs": 60000
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"action": "whenVisible",
|
|
31
|
+
"selector": { "textContains": "Development servers" },
|
|
32
|
+
"steps": [
|
|
33
|
+
{
|
|
34
|
+
"action": "tap",
|
|
35
|
+
"selector": { "contentDescContains": "Sample App Test on" },
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"action": "tap",
|
|
40
|
+
"selector": { "contentDesc": "Sample App Test, http://10.0.2.2:8081" },
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"action": "tap",
|
|
45
|
+
"selector": { "text": "http://10.0.2.2:8081" },
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"action": "waitNotVisible",
|
|
50
|
+
"selector": { "textContains": "Development servers" },
|
|
51
|
+
"timeoutMs": 60000,
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"action": "whenVisible",
|
|
58
|
+
"selector": { "text": "Continue" },
|
|
59
|
+
"timeoutMs": 120000,
|
|
60
|
+
"steps": [
|
|
61
|
+
{
|
|
62
|
+
"action": "tap",
|
|
63
|
+
"selector": { "text": "Continue" }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"action": "whenVisible",
|
|
67
|
+
"selector": { "textContains": "Connected to:" },
|
|
68
|
+
"steps": [
|
|
69
|
+
{
|
|
70
|
+
"action": "pressBack"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"action": "whenVisible",
|
|
78
|
+
"selector": { "textContains": "Connected to:" },
|
|
79
|
+
"timeoutMs": 10000,
|
|
80
|
+
"steps": [
|
|
81
|
+
{
|
|
82
|
+
"action": "pressBack"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"action": "waitAny",
|
|
88
|
+
"selectors": [
|
|
89
|
+
{ "id": "email-login-email-input" },
|
|
90
|
+
{ "contentDesc": "Open email sign up" },
|
|
91
|
+
{ "text": "Or sign up via email here" },
|
|
92
|
+
{ "textContains": "Welcome" },
|
|
93
|
+
{ "textContains": "Sample landing" },
|
|
94
|
+
{ "text": "Sign in with email" },
|
|
95
|
+
{ "textContains": "Dashboard" },
|
|
96
|
+
{ "textContains": "Request a feature" },
|
|
97
|
+
{ "textContains": "Earn points" },
|
|
98
|
+
{ "textContains": "Spend points" },
|
|
99
|
+
{ "textContains": "points" }
|
|
100
|
+
],
|
|
101
|
+
"timeoutMs": 60000
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"action": "whenVisible",
|
|
105
|
+
"selector": { "textContains": "Downloading 100%" },
|
|
106
|
+
"steps": [
|
|
107
|
+
{
|
|
108
|
+
"action": "waitNotVisible",
|
|
109
|
+
"selector": { "textContains": "Downloading 100%" },
|
|
110
|
+
"timeoutMs": 180000
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"action": "whenVisible",
|
|
116
|
+
"selector": { "contentDesc": "Use E2E sign in" },
|
|
117
|
+
"steps": [
|
|
118
|
+
{
|
|
119
|
+
"action": "tap",
|
|
120
|
+
"selector": { "contentDesc": "Use E2E sign in" }
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"action": "waitAny",
|
|
124
|
+
"selectors": [
|
|
125
|
+
{ "textContains": "Signing in to your account" },
|
|
126
|
+
{ "textContains": "Requesting test token" },
|
|
127
|
+
{ "textContains": "Bootstrapping session" },
|
|
128
|
+
{ "textContains": "Routing to app" },
|
|
129
|
+
{ "textContains": "Dashboard" },
|
|
130
|
+
{ "textContains": "Request a feature" },
|
|
131
|
+
{ "textContains": "Invite teammates" },
|
|
132
|
+
{ "textContains": "Earn points" },
|
|
133
|
+
{ "textContains": "Spend points" },
|
|
134
|
+
{ "textContains": "points" }
|
|
135
|
+
],
|
|
136
|
+
"timeoutMs": 45000
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"action": "waitAny",
|
|
142
|
+
"selectors": [
|
|
143
|
+
{ "textContains": "Dashboard" },
|
|
144
|
+
{ "textContains": "Request a feature" },
|
|
145
|
+
{ "textContains": "Invite teammates" },
|
|
146
|
+
{ "textContains": "Earn points" },
|
|
147
|
+
{ "textContains": "Spend points" },
|
|
148
|
+
{ "textContains": "points" }
|
|
149
|
+
],
|
|
150
|
+
"timeoutMs": 90000
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"action": "openLink",
|
|
154
|
+
"url": "exampleapp:///referrals/invite"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"action": "waitAny",
|
|
158
|
+
"selectors": [
|
|
159
|
+
{ "textContains": "Invite a teammate and both earn" },
|
|
160
|
+
{ "textContains": "Share my link" },
|
|
161
|
+
{ "textContains": "Your Referral Code" },
|
|
162
|
+
{ "textContains": "No referrals yet" },
|
|
163
|
+
{ "text": "Retry" },
|
|
164
|
+
{ "textContains": "Request timed out" }
|
|
165
|
+
],
|
|
166
|
+
"timeoutMs": 20000
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"action": "whenVisible",
|
|
170
|
+
"selector": { "text": "Retry" },
|
|
171
|
+
"steps": [
|
|
172
|
+
{
|
|
173
|
+
"action": "tap",
|
|
174
|
+
"selector": { "text": "Retry" }
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"action": "waitAny",
|
|
178
|
+
"selectors": [
|
|
179
|
+
{ "textContains": "Invite a teammate and both earn" },
|
|
180
|
+
{ "textContains": "Share my link" },
|
|
181
|
+
{ "textContains": "Your Referral Code" },
|
|
182
|
+
{ "textContains": "No referrals yet" }
|
|
183
|
+
],
|
|
184
|
+
"timeoutMs": 60000
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"action": "snapshot"
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Android app onboarding smoke",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "clearState" },
|
|
6
|
+
{ "action": "launch" },
|
|
7
|
+
{ "action": "waitVisible", "selector": { "textContains": "Welcome" }, "timeoutMs": 15000 },
|
|
8
|
+
{ "action": "tap", "selector": { "textContains": "Get started" } },
|
|
9
|
+
{ "action": "waitAny", "selectors": [{ "textContains": "Home" }, { "textContains": "Sign in" }], "timeoutMs": 15000 },
|
|
10
|
+
{ "action": "snapshot" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Android referral deep link smoke",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "clearState" },
|
|
6
|
+
{ "action": "openLink", "url": "exampleapp://invite?code=DEMO-CODE" },
|
|
7
|
+
{ "action": "waitVisible", "selector": { "textContains": "Invite" }, "timeoutMs": 15000 },
|
|
8
|
+
{ "action": "scrollUntilVisible", "selector": { "id": "invite-card" }, "direction": "down", "timeoutMs": 10000 },
|
|
9
|
+
{ "action": "assertVisible", "selector": { "id": "invite-card" } },
|
|
10
|
+
{ "action": "snapshot" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ZMR Android shim selector demo",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "launch" },
|
|
6
|
+
{
|
|
7
|
+
"action": "waitVisible",
|
|
8
|
+
"selector": { "text": "Continue" },
|
|
9
|
+
"timeoutMs": 1000
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"action": "tap",
|
|
13
|
+
"selector": { "resourceId": "continue_button" }
|
|
14
|
+
},
|
|
15
|
+
{ "action": "typeText", "text": "hello" },
|
|
16
|
+
{ "action": "hideKeyboard" },
|
|
17
|
+
{ "action": "snapshot" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ZMR fake Android auth probe demo",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "launch" },
|
|
6
|
+
{ "action": "openLink", "url": "exampleapp://e2e-auth?probe=1" },
|
|
7
|
+
{
|
|
8
|
+
"action": "waitVisible",
|
|
9
|
+
"selector": { "text": "E2E auth probe" },
|
|
10
|
+
"timeoutMs": 1000
|
|
11
|
+
},
|
|
12
|
+
{ "action": "snapshot" }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iOS Expo dev-client open-link smoke",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "stop" },
|
|
6
|
+
{
|
|
7
|
+
"action": "openLink",
|
|
8
|
+
"url": "exp+mobiletest://expo-development-client/?url=http%3A%2F%2F127.0.0.1%3A8081"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"action": "waitAny",
|
|
12
|
+
"selectors": [
|
|
13
|
+
{ "textContains": "Downloading" },
|
|
14
|
+
{ "textContains": "Connected to:" },
|
|
15
|
+
{ "textContains": "Reload" },
|
|
16
|
+
{ "textContains": "Continue" },
|
|
17
|
+
{ "textContains": "Sign in" },
|
|
18
|
+
{ "textContains": "Home" },
|
|
19
|
+
{ "textContains": "Unable to load" }
|
|
20
|
+
],
|
|
21
|
+
"timeoutMs": 120000
|
|
22
|
+
},
|
|
23
|
+
{ "action": "assertHealthy" },
|
|
24
|
+
{ "action": "snapshot" }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iOS Expo dev-client route snapshot",
|
|
3
|
+
"appId": "com.example.mobiletest",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "action": "stop" },
|
|
6
|
+
{
|
|
7
|
+
"action": "openLink",
|
|
8
|
+
"url": "exampleapp:///settings?mode=manage&source=zmr"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"action": "waitAny",
|
|
12
|
+
"selectors": [
|
|
13
|
+
{ "textContains": "Settings" },
|
|
14
|
+
{ "textContains": "Manage" },
|
|
15
|
+
{ "textContains": "Home" },
|
|
16
|
+
{ "textContains": "Sign in" },
|
|
17
|
+
{ "textContains": "Unable to load" }
|
|
18
|
+
],
|
|
19
|
+
"timeoutMs": 60000
|
|
20
|
+
},
|
|
21
|
+
{ "action": "assertHealthy" },
|
|
22
|
+
{ "action": "snapshot" }
|
|
23
|
+
]
|
|
24
|
+
}
|