peachsim 0.1.527
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 +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- package/src/worklets-babel.ts +250 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rnx-test
|
|
3
|
+
description: Run automated tests against your React Native app under rnx with Maestro YAML or Detox/Jest
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rnx tests
|
|
7
|
+
|
|
8
|
+
rnx has two test runners. Maestro owns every YAML test, whether it was
|
|
9
|
+
hand-authored, generated from a goal, or recorded from live CLI actions.
|
|
10
|
+
Detox runs existing imperative Jest suites through the rnx driver.
|
|
11
|
+
|
|
12
|
+
> needs a connected, pinned sim. if `rnx describe` errors, load
|
|
13
|
+
> `/rnx-setup` first.
|
|
14
|
+
|
|
15
|
+
Set `RNX_NO_OPEN=1` before starting an app dev server for a Detox,
|
|
16
|
+
Maestro, or iOS-only pass. Apps can opt into a named Electron window through
|
|
17
|
+
the official Vite or Metro plugin, and test-only work must not open that window.
|
|
18
|
+
|
|
19
|
+
## route in (decision tree)
|
|
20
|
+
|
|
21
|
+
- have an existing `.maestro/` directory? → [Maestro](#branch--maestro)
|
|
22
|
+
- have existing Detox specs? → [detox drop-in](#branch--detox-drop-in)
|
|
23
|
+
- writing tests fresh, want plain-language generation, or want live authoring? → [Maestro](#branch--maestro)
|
|
24
|
+
- need one short, timing-sensitive sequence from the shell? → [inline chains](#one-off-inline-chains)
|
|
25
|
+
|
|
26
|
+
Do not invent a third runner for how a Maestro flow was authored.
|
|
27
|
+
|
|
28
|
+
## anti-patterns (apply to both runners)
|
|
29
|
+
|
|
30
|
+
- **`sleep` is almost always a bug.** the underlying CLI auto-settles
|
|
31
|
+
before reads and polls layout hash after writes. if a flow fails
|
|
32
|
+
without a `sleep`, the fix is `waitFor`, `waitForAnimationToEnd`, or
|
|
33
|
+
`wait idle` — not a blind timer. the one legitimate case: a
|
|
34
|
+
user-controlled debounce ("press and hold for 2s").
|
|
35
|
+
- **prefer testID (`tapOn: { id: ... }`, `by.id(testID)`) over text.** text breaks
|
|
36
|
+
under i18n, copy churn, and duplicate strings. add `testID` during
|
|
37
|
+
implementation and author flows against it.
|
|
38
|
+
- **pin a sim before running a suite.** parallel CI jobs colliding
|
|
39
|
+
on the default bridge port is the #1 flake source. pass `--port` in
|
|
40
|
+
CI and `rnx use <id>` locally.
|
|
41
|
+
- **every flow ends with an assert, not a screenshot.** a screenshot
|
|
42
|
+
passes as long as the flow didn't throw. `assertVisible` (or
|
|
43
|
+
`expect(...).toBeVisible()`) actually verifies the happy path.
|
|
44
|
+
- **don't run a 40-step "smoke everything" flow.** when it fails, you
|
|
45
|
+
can't tell what regressed. split into `login.yaml`, `compose.yaml`,
|
|
46
|
+
`settings.yaml`, chain with `runFlow`.
|
|
47
|
+
- **inside an `rnx do` chain, use `sleep` only for gesture timing.** after the
|
|
48
|
+
chain, use `rnx wait` for app state so automation advances on a real condition.
|
|
49
|
+
|
|
50
|
+
## you're done when
|
|
51
|
+
|
|
52
|
+
- the suite passes locally and in CI
|
|
53
|
+
- every flake is either fixed or marked `xfail` with a written reason
|
|
54
|
+
and a removal date
|
|
55
|
+
- the green run took noticeably less wall-clock than the previous
|
|
56
|
+
baseline (a suite that grows slower without growing wider is a smell)
|
|
57
|
+
|
|
58
|
+
## one-off inline chains
|
|
59
|
+
|
|
60
|
+
repeat `--then` before each additional `do` action to run one ordered engine
|
|
61
|
+
batch. use a chain for a short repro, form fill, or timing-sensitive gesture
|
|
62
|
+
that does not belong in a saved test:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
rnx do tap email --then type user@example.com --then tap submit
|
|
66
|
+
rnx wait selector dashboard
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
the batch stops on the first failure. add `--json` for per-action results.
|
|
70
|
+
`tap <id>` and `tap-id <id>` are exact; use `tap-text <text>` for visible text.
|
|
71
|
+
punctuation in action data remains data. the exact `--then` token is reserved
|
|
72
|
+
as the action boundary. use Maestro when the sequence should be reviewed,
|
|
73
|
+
reused, or run in CI; an inline chain is not a third test runner and does not
|
|
74
|
+
read flow files.
|
|
75
|
+
|
|
76
|
+
## branch — Maestro
|
|
77
|
+
|
|
78
|
+
Write declarative Maestro YAML. The same command runs existing suites,
|
|
79
|
+
generates tests, records live drafts, profiles, and uploads replayable results.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
rnx maestro test .maestro/login.yaml # run one flow
|
|
83
|
+
rnx maestro test .maestro/login.yaml --record # record video
|
|
84
|
+
rnx maestro test .maestro/login.yaml --profile # include perf stats
|
|
85
|
+
rnx maestro test .maestro/ # run the suite
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Author from a live interaction:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
rnx maestro start # begin a draft
|
|
92
|
+
rnx do tap-id loginButton # drive the app via the CLI
|
|
93
|
+
rnx maestro keep # save the step
|
|
94
|
+
rnx do type "user@example.com"
|
|
95
|
+
rnx maestro keep
|
|
96
|
+
rnx do tap-id submit
|
|
97
|
+
rnx maestro keep
|
|
98
|
+
rnx maestro end --output .maestro/login.yaml --validate
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
the recorder emits minimal YAML — no coordinate dumps, no `sleep` noise —
|
|
102
|
+
because the underlying CLI already settled between every action.
|
|
103
|
+
|
|
104
|
+
exemplar flow:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
# .maestro/login.yaml
|
|
108
|
+
- launchApp: {}
|
|
109
|
+
|
|
110
|
+
- waitFor:
|
|
111
|
+
text: 'Sign in'
|
|
112
|
+
timeout: 10000
|
|
113
|
+
|
|
114
|
+
- tapOn:
|
|
115
|
+
id: emailInput
|
|
116
|
+
- inputText: '${USERNAME}'
|
|
117
|
+
|
|
118
|
+
- tapOn:
|
|
119
|
+
id: passwordInput
|
|
120
|
+
- inputText: '${PASSWORD}'
|
|
121
|
+
|
|
122
|
+
- tapOn:
|
|
123
|
+
id: signInButton
|
|
124
|
+
- waitForAnimationToEnd: true
|
|
125
|
+
|
|
126
|
+
- assertVisible: 'Welcome back'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
run with env interpolation:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
rnx maestro --env USERNAME=alice --env PASSWORD=hunter2 \
|
|
133
|
+
test .maestro/login.yaml \
|
|
134
|
+
--record --profile
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
automatic settling inside flows: every tap, type, scroll, swipe, and
|
|
138
|
+
drag is backed by the same primitives that auto-wait for transitions on
|
|
139
|
+
the way in and layout stability on the way out. you rarely need
|
|
140
|
+
explicit waits; when you do, `waitForAnimationToEnd: true` maps to the
|
|
141
|
+
CLI's `wait idle` with a generous budget. use it before any
|
|
142
|
+
`assertVisible` that depends on a transition longer than ~400 ms.
|
|
143
|
+
|
|
144
|
+
Generate and immediately run a Maestro flow from a plain-language goal:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
rnx maestro generate "log in with the demo account and verify the dashboard"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Generation writes the same deterministic Maestro artifact, uploads its replay,
|
|
151
|
+
and registers it as a Maestro run. The dashboard does not distinguish how it
|
|
152
|
+
was authored.
|
|
153
|
+
|
|
154
|
+
### existing Maestro suites
|
|
155
|
+
|
|
156
|
+
if you already have a `.maestro/` directory, the migration is one
|
|
157
|
+
command change:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
# before
|
|
161
|
+
maestro test .maestro/login.yaml
|
|
162
|
+
|
|
163
|
+
# after
|
|
164
|
+
rnx maestro test .maestro/login.yaml
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
no test rewrites, no detox config, no simulator. the runtime auto-launches
|
|
168
|
+
an rnx shell if one isn't running.
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
rnx maestro # auto-discover .maestro/ or maestro/
|
|
172
|
+
rnx maestro test .maestro/login.yaml
|
|
173
|
+
rnx maestro test .maestro/ # every flow in a directory
|
|
174
|
+
rnx maestro init # scaffold a starter flow
|
|
175
|
+
rnx maestro --list-compat # supported / partial / unsupported
|
|
176
|
+
rnx maestro --env USERNAME=alice test .maestro/login.yaml
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### supported verbs
|
|
180
|
+
|
|
181
|
+
`launchApp` (including JSON `arguments`), `stopApp`, `clearState`, `tapOn`, `tapAtCoords`, `longPressOn`, `inputText`,
|
|
182
|
+
`pressKey`, `dispatchKey`, `hideKeyboard`, `eraseText`, `assertVisible`,
|
|
183
|
+
`assertNotVisible`, `assertTreeContains`, `waitFor`, `extendedWaitUntil`,
|
|
184
|
+
`waitForAnimationToEnd`, `scroll`, `scrollUntilVisible`, `scrollTo`,
|
|
185
|
+
`swipe`, `pinch`, `takeScreenshot`, `dumpTree`, `back`, `repeat.times`,
|
|
186
|
+
`runFlow`, `when` (`visible` / `notVisible` / `platform` / `true`),
|
|
187
|
+
`optional: true`, `onFlowStart` / `onFlowComplete`, `copyTextFrom` +
|
|
188
|
+
`${maestroCopiedText}`, `evalScript`, `openLink`, env var `${NAME}`
|
|
189
|
+
interpolation.
|
|
190
|
+
|
|
191
|
+
### partial
|
|
192
|
+
|
|
193
|
+
- `clearKeychain` — no keychain in rnx; logs a warning and continues.
|
|
194
|
+
if a test depends on keychain state being reset, add an explicit
|
|
195
|
+
in-app "sign out" step instead.
|
|
196
|
+
- `when.platform` — rnx emulates iOS; android branches are skipped.
|
|
197
|
+
- `openLink`: app routing works; there is no OS-level browser fallback.
|
|
198
|
+
|
|
199
|
+
`stopApp` keeps the guest runtime stopped. The next `launchApp` starts a fresh
|
|
200
|
+
guest with its replacement arguments. Every `openLink` replaces the guest and
|
|
201
|
+
delivers the URL through React Native Linking after the replacement starts.
|
|
202
|
+
|
|
203
|
+
### not yet implemented
|
|
204
|
+
|
|
205
|
+
these throw `unsupported maestro verb: X`:
|
|
206
|
+
|
|
207
|
+
- `travel`, `setLocation`, `setAirplaneMode`, `killApp`
|
|
208
|
+
- `addMedia`
|
|
209
|
+
- `repeat.while` (use `repeat.times`)
|
|
210
|
+
|
|
211
|
+
### troubleshooting
|
|
212
|
+
|
|
213
|
+
- **"no maestro flows found"** — pass an explicit path
|
|
214
|
+
(`rnx maestro test path/to/flow.yaml`) or run `rnx maestro
|
|
215
|
+
init` to scaffold one.
|
|
216
|
+
- **"missing environment variable for flow placeholder: FOO"** — pass
|
|
217
|
+
`--env FOO=bar` or export it. pick one mechanism per repo and document
|
|
218
|
+
it in the flow directory's README.
|
|
219
|
+
- **flow can't find an element** — rnx's matcher uses testID + visible
|
|
220
|
+
text. UIKit-only labels that real maestro scrapes from the host OS won't
|
|
221
|
+
resolve here; switch to testID.
|
|
222
|
+
- **flow runs but nothing renders** — confirm the bundler your flow
|
|
223
|
+
targets is reachable and pass `--url <port>` or add `app: <port>`
|
|
224
|
+
frontmatter at the top of the YAML.
|
|
225
|
+
|
|
226
|
+
## branch — detox drop-in
|
|
227
|
+
|
|
228
|
+
an existing detox suite runs against rnx with **one line** of jest
|
|
229
|
+
config and zero test changes.
|
|
230
|
+
|
|
231
|
+
`import { by, device, element, expect, waitFor } from 'detox'` is
|
|
232
|
+
rewritten by jest to an rnx-backed driver that drives the real shell
|
|
233
|
+
over playwright. matchers, assertions, and the `waitFor` API match the
|
|
234
|
+
upstream detox surface.
|
|
235
|
+
|
|
236
|
+
### one-line install
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
// rnx-detox.config.cjs
|
|
240
|
+
module.exports = {
|
|
241
|
+
preset: 'rnxsim/detox',
|
|
242
|
+
rootDir: __dirname,
|
|
243
|
+
testMatch: ['<rootDir>/e2e/**/*.test.ts'],
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
or, if you already have a jest config, pull in just the module mapper:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
moduleNameMapper: {
|
|
251
|
+
'^detox$': require.resolve('rnxsim/detox'),
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
keep the **preset**, not just the mapper — the preset wires up the
|
|
256
|
+
global setup/teardown that tears the shell down between suites. mapper-only
|
|
257
|
+
works until your first hung shell.
|
|
258
|
+
|
|
259
|
+
### commands
|
|
260
|
+
|
|
261
|
+
```sh
|
|
262
|
+
rnx detox # run e2e/, test/e2e/, or detox/
|
|
263
|
+
rnx detox --config my.cjs # use a specific jest config
|
|
264
|
+
rnx detox --watch # jest watch mode
|
|
265
|
+
rnx detox -t "login" # --testNamePattern
|
|
266
|
+
rnx detox --headed # keep the shell window visible
|
|
267
|
+
rnx detox --no-launch # don't auto-launch a shell
|
|
268
|
+
rnx detox --port 5173 # explicit bridge port for CI
|
|
269
|
+
rnx detox init # scaffold config + e2e/example.test.ts
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### compat surface
|
|
273
|
+
|
|
274
|
+
| detox API | rnx |
|
|
275
|
+
| ---------------------------------- | ------- |
|
|
276
|
+
| `element(by.id(...))` | supported |
|
|
277
|
+
| `element(by.text(...))` | supported — but prefer `by.id` |
|
|
278
|
+
| `element(by.label(...))` | supported |
|
|
279
|
+
| `element(by.traits([...]))` | supported for the iOS trait set |
|
|
280
|
+
| `.toBeVisible()` / `.not.toBeVisible()` | supported |
|
|
281
|
+
| `.toHaveText(...)` / `.toHaveLabel(...)` | supported |
|
|
282
|
+
| `.tap()` / `.longPress()` | supported |
|
|
283
|
+
| `.typeText(...)` / `.replaceText(...)` | supported |
|
|
284
|
+
| `.scroll(...)` / `.scrollTo(...)` | supported |
|
|
285
|
+
| `.swipe(...)` | supported |
|
|
286
|
+
| `.setColumnToValue(...)` | supported for uniquely named picker rows |
|
|
287
|
+
| `waitFor(...).toBeVisible().withTimeout(...)` | supported |
|
|
288
|
+
| `device.launchApp(...)` / `device.reloadReactNative()` | supported |
|
|
289
|
+
| `device.shake()` | partial — emits the JS event, no haptic |
|
|
290
|
+
| `device.sendUserNotification(...)` | not yet |
|
|
291
|
+
|
|
292
|
+
### troubleshooting
|
|
293
|
+
|
|
294
|
+
- **"no rnx shell reachable"** — start one yourself
|
|
295
|
+
(`rnx open <port>`) or pass `--port <n>`.
|
|
296
|
+
- **"jest cannot find module 'detox'"** — the preset isn't wired. confirm
|
|
297
|
+
`preset: 'rnxsim/detox'` is in the jest config jest
|
|
298
|
+
actually loaded (`--config` overrides package.json).
|
|
299
|
+
- **matchers fail on elements that exist** — canvas has no DOM. use
|
|
300
|
+
`by.id` (testID); `rnx describe` shows what the shell sees.
|
|
301
|
+
- **passes locally, flakes in CI** — the shell boots asynchronously.
|
|
302
|
+
bump jest `testTimeout` to 10–15 s and add a `waitFor(element(...))
|
|
303
|
+
.toBeVisible().withTimeout(10000)` before the first assertion.
|
|
304
|
+
- **port conflicts under parallel CI** — pass `--port` explicitly per
|
|
305
|
+
job so workers don't collide on the default bridge port.
|
|
306
|
+
|
|
307
|
+
## recovery — common failure modes (both runners)
|
|
308
|
+
|
|
309
|
+
- **"no flows found"** — confirm the working directory or pass an explicit
|
|
310
|
+
`.maestro/` path.
|
|
311
|
+
- **"missing env variable"** — set with `--env` (or jest env config) and
|
|
312
|
+
document the variable list in the flow directory's README so new
|
|
313
|
+
contributors aren't guessing.
|
|
314
|
+
- **CI flakes but local passes** — bump test timeouts to 10–15 s before
|
|
315
|
+
the first assertion, pin the bridge port, and run with `--video` so
|
|
316
|
+
failing Maestro runs land a recording next to the output.
|
|
317
|
+
- **a flake reproduces but resists fixing** — don't paper over with
|
|
318
|
+
`sleep`. open `/rnx-debug` against the same sim, capture
|
|
319
|
+
the timeline (`rnx timeline start` → reproduce → `rnx
|
|
320
|
+
what-happened`), and look for the missing animation-completion or
|
|
321
|
+
the unresolved fetch that's racing the assertion.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rnx-visual
|
|
3
|
+
description: Pixel-level rendering comparison between rnx and native iOS — fonts, gradients, shadows, image fidelity
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rnx visual fidelity
|
|
7
|
+
|
|
8
|
+
pixel-level rendering fidelity: does the rnx canvas match native iOS
|
|
9
|
+
for text, gradients, shadows, blur, and image rendering? the `rnx`
|
|
10
|
+
CLI gives you two primitives — **canvas capture** (`rnx screenshot`)
|
|
11
|
+
and **structural diff** (`rnx debug snapshot`/`diff`). pixel
|
|
12
|
+
comparison itself is "capture both sides, diff the PNGs with a standard
|
|
13
|
+
image tool" — there is no single oracle-diff command, and that's fine:
|
|
14
|
+
the capture step is the hard part and it's fully reproducible.
|
|
15
|
+
|
|
16
|
+
> needs a connected, pinned sim. if `rnx describe` errors,
|
|
17
|
+
> load `/rnx-setup` first.
|
|
18
|
+
|
|
19
|
+
## route in
|
|
20
|
+
|
|
21
|
+
- structural change ("which nodes / which props changed?") → load
|
|
22
|
+
`/rnx-debug`, debugging branch — `debug snapshot` + `debug
|
|
23
|
+
diff` is faster, deterministic, and tells you *why*.
|
|
24
|
+
- visual change (color, gradient, shadow, font metric, blur, image)? →
|
|
25
|
+
continue here.
|
|
26
|
+
- comparing rnx to your iOS simulator for fidelity? → continue.
|
|
27
|
+
- regressing your own app against itself across commits? → continue.
|
|
28
|
+
|
|
29
|
+
## anti-patterns (read first)
|
|
30
|
+
|
|
31
|
+
- **run structural diff before pixel work.** `rnx debug snapshot` /
|
|
32
|
+
`debug diff` (in `/rnx-debug`) is cheaper, deterministic, and
|
|
33
|
+
points at the responsible nodes. only when it's empty (or the change is
|
|
34
|
+
pure rendering) does a pixel comparison add information.
|
|
35
|
+
- **don't single-screenshot animated surfaces.** a gradient
|
|
36
|
+
mid-animation never matches. gate the capture behind `rnx wait
|
|
37
|
+
idle` first.
|
|
38
|
+
- **a > 5% pixel threshold hides regressions.** ~1% is the right
|
|
39
|
+
ballpark for catching real font / shadow / gradient drift without
|
|
40
|
+
flagging anti-alias noise. go higher only with a written reason.
|
|
41
|
+
- **pin device and theme.** capture both sides on the same simulated
|
|
42
|
+
device and appearance. capture each appearance separately with
|
|
43
|
+
`rnx screenshot` after setting it on both simulators.
|
|
44
|
+
- **commit baselines alongside the code change that produced them.**
|
|
45
|
+
baseline churn without code churn means someone captured noise.
|
|
46
|
+
|
|
47
|
+
## capturing — `rnx screenshot`
|
|
48
|
+
|
|
49
|
+
fast, no extra setup, works on any loaded app:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
rnx screenshot --output app.png # full canvas
|
|
53
|
+
rnx screenshot --area 0,200,393,400 # crop to a logical rect
|
|
54
|
+
rnx screenshot --id loginButton -o btn.png # crop to a node by testID
|
|
55
|
+
rnx screenshot --text "Sign in" # crop to a node by text
|
|
56
|
+
rnx screenshot --no-shell -o tenant.png # tenant surface only, no iOS chrome
|
|
57
|
+
rnx screenshot --shell-only # only the simulated iOS chrome
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`rnx screenshot` captures the current screen as a PNG. navigate to each
|
|
61
|
+
screen you want to inspect and capture it with a distinct output filename.
|
|
62
|
+
|
|
63
|
+
## comparing pixels
|
|
64
|
+
|
|
65
|
+
capture both sides to PNGs, then diff with any standard image tool.
|
|
66
|
+
|
|
67
|
+
**rnx vs rnx (regression across your own commits):**
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
rnx screenshot -o before.png # baseline, before your change
|
|
71
|
+
# … make the rendering change, let the sim hot-reload …
|
|
72
|
+
rnx screenshot -o after.png # after your change
|
|
73
|
+
npx pixelmatch before.png after.png diff.png 0.1 # writes diff.png
|
|
74
|
+
# or: magick compare before.png after.png diff.png
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**rnx vs native iOS (fidelity to the real thing):**
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
# native side: lifecycle and UI use xcodebuildmcp with one claimed simulator
|
|
81
|
+
# lossless capture names that simulator explicitly; never use the booted alias
|
|
82
|
+
xcrun simctl io <udid> screenshot --type=png native.png
|
|
83
|
+
# rnx side: same screen, same appearance
|
|
84
|
+
rnx screenshot -o rnx.png
|
|
85
|
+
npx pixelmatch native.png rnx.png diff.png 0.1
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`xcodebuildmcp simulator screenshot` may return an optimized JPEG even when the
|
|
89
|
+
proof contract requires a lossless source. Use it only when JPEG is acceptable.
|
|
90
|
+
For parity proof, use the explicit-UDID PNG command above, then verify the
|
|
91
|
+
decoded dimensions and file type before admitting the capture.
|
|
92
|
+
|
|
93
|
+
crop both to the same region (`--area` / `--id`) when you only care
|
|
94
|
+
about one element — a full-screen diff buries a 3px font-baseline shift
|
|
95
|
+
under unrelated chrome.
|
|
96
|
+
|
|
97
|
+
## typical workflow
|
|
98
|
+
|
|
99
|
+
1. structural diff first — confirm the change is genuinely visual.
|
|
100
|
+
2. capture the affected screen with `rnx screenshot` (crop to the
|
|
101
|
+
element if you can).
|
|
102
|
+
3. diff against the baseline (previous commit, or a native capture).
|
|
103
|
+
4. iterate the rendering until the diff is under threshold.
|
|
104
|
+
5. navigate to each affected screen, capture it, and inspect the PNG
|
|
105
|
+
for unexpected rendering changes.
|
|
106
|
+
|
|
107
|
+
## you're done when
|
|
108
|
+
|
|
109
|
+
- the diff is under threshold on every affected case, **or**
|
|
110
|
+
- the differences are explicitly accepted, baselines re-captured, and
|
|
111
|
+
both halves (code + baselines) committed together.
|
|
112
|
+
- the captures show no unexpected changes across all affected screens.
|
|
113
|
+
|
|
114
|
+
## recovery — common failure modes
|
|
115
|
+
|
|
116
|
+
- **a single icon/glyph diffs** — font-load race or image not yet
|
|
117
|
+
cached. recapture after `rnx wait idle`, or navigate away and back
|
|
118
|
+
to seed the cache.
|
|
119
|
+
- **everything diffs by a constant offset** — you captured the two sides
|
|
120
|
+
on different devices or appearances. pin both, recapture.
|
|
121
|
+
- **baselines churn without a code change** — someone captured noise (a
|
|
122
|
+
lingering badge, half-open keyboard, a drifting clock). revert and
|
|
123
|
+
recapture from a clean, idle state.
|
|
124
|
+
- **persistent delta on the same font / shadow / gradient across
|
|
125
|
+
unrelated screens** — that's an engine-level rendering fidelity issue,
|
|
126
|
+
not your app. report it with the affected captures attached.
|