rnxsim 0.0.0 → 0.1.312
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 +21 -0
- package/README.md +512 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +39 -0
- package/detox/expectations.ts +537 -0
- package/detox/gestures.ts +480 -0
- package/detox/index.ts +2118 -0
- package/detox/jest-preset.cjs +51 -0
- package/detox/jest-setup-after-env.cjs +18 -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.ts +43 -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-cli/bin.js +15 -0
- package/dist-cli/chunks/agent-NXVDDCFG.js +48 -0
- package/dist-cli/chunks/agent-wrapper-DXJCFKL3.js +17 -0
- package/dist-cli/chunks/app-fonts-W5AJZQJF.js +4 -0
- package/dist-cli/chunks/assert-3LOJEUDT.js +49 -0
- package/dist-cli/chunks/auth-MFTYYPNX.js +12 -0
- package/dist-cli/chunks/beta-GT3NN67B.js +4 -0
- package/dist-cli/chunks/camera-6EAK7CUL.js +22 -0
- package/dist-cli/chunks/chunk-274UD5FD.js +4 -0
- package/dist-cli/chunks/chunk-2SA2VTGP.js +5 -0
- package/dist-cli/chunks/chunk-33FKQH54.js +5 -0
- package/dist-cli/chunks/chunk-37I5AIS7.js +3 -0
- package/dist-cli/chunks/chunk-3D57IBPC.js +4 -0
- package/dist-cli/chunks/chunk-4BXCZHAU.js +4 -0
- package/dist-cli/chunks/chunk-5OX7ANRA.js +5 -0
- package/dist-cli/chunks/chunk-66NF2KNG.js +9 -0
- package/dist-cli/chunks/chunk-6UKJB2VC.js +125 -0
- package/dist-cli/chunks/chunk-7INBNA3U.js +26 -0
- package/dist-cli/chunks/chunk-7VWOSTD5.js +5 -0
- package/dist-cli/chunks/chunk-AY47WKS5.js +23 -0
- package/dist-cli/chunks/chunk-B7KMNHX7.js +4 -0
- package/dist-cli/chunks/chunk-BKNOLP3J.js +4 -0
- package/dist-cli/chunks/chunk-BRIZOR4K.js +28 -0
- package/dist-cli/chunks/chunk-DM6KUAOZ.js +4 -0
- package/dist-cli/chunks/chunk-EUAVBTKU.js +4 -0
- package/dist-cli/chunks/chunk-FTC4M26Q.js +4 -0
- package/dist-cli/chunks/chunk-FWYYCUAZ.js +4 -0
- package/dist-cli/chunks/chunk-G74YIEVS.js +7 -0
- package/dist-cli/chunks/chunk-GZDHEONW.js +507 -0
- package/dist-cli/chunks/chunk-HG7ORJMT.js +5 -0
- package/dist-cli/chunks/chunk-HMDOLCKG.js +15 -0
- package/dist-cli/chunks/chunk-JG6S4RSH.js +4 -0
- package/dist-cli/chunks/chunk-KR5BGMBZ.js +4 -0
- package/dist-cli/chunks/chunk-KZW5WBGU.js +983 -0
- package/dist-cli/chunks/chunk-L24Z3CRS.js +30 -0
- package/dist-cli/chunks/chunk-LDQ7HVCW.js +4 -0
- package/dist-cli/chunks/chunk-MENV7VLD.js +4 -0
- package/dist-cli/chunks/chunk-MNGQ42QE.js +568 -0
- package/dist-cli/chunks/chunk-N6A3SXMR.js +4 -0
- package/dist-cli/chunks/chunk-NLEMQ4PY.js +6 -0
- package/dist-cli/chunks/chunk-NPVGMH2N.js +101 -0
- package/dist-cli/chunks/chunk-OR3XTXEB.js +107 -0
- package/dist-cli/chunks/chunk-PEPUGJVI.js +4 -0
- package/dist-cli/chunks/chunk-Q3RGLERJ.js +142 -0
- package/dist-cli/chunks/chunk-SI3BUTQR.js +4 -0
- package/dist-cli/chunks/chunk-T4FT6CM7.js +4 -0
- package/dist-cli/chunks/chunk-TBMVBI26.js +7 -0
- package/dist-cli/chunks/chunk-TCWQJILS.js +5 -0
- package/dist-cli/chunks/chunk-TGQNQ6XT.js +766 -0
- package/dist-cli/chunks/chunk-TYE3WBSR.js +5 -0
- package/dist-cli/chunks/chunk-U4PN6JOT.js +6 -0
- package/dist-cli/chunks/chunk-VUFRB347.js +4 -0
- package/dist-cli/chunks/chunk-VZBLFMYC.js +10 -0
- package/dist-cli/chunks/chunk-WBT32R6N.js +25 -0
- package/dist-cli/chunks/chunk-XRGEEPJ5.js +38 -0
- package/dist-cli/chunks/chunk-Y2FSZSO7.js +71 -0
- package/dist-cli/chunks/chunk-YRV7HR3P.js +74 -0
- package/dist-cli/chunks/chunk-YZRFF4YA.js +14 -0
- package/dist-cli/chunks/chunk-ZMZTHU7V.js +4 -0
- package/dist-cli/chunks/cleanup-H3GWQB63.js +25 -0
- package/dist-cli/chunks/cli-version-3QPAJ3LC.js +4 -0
- package/dist-cli/chunks/compat-5WG2XJV7.js +20 -0
- package/dist-cli/chunks/config-ANMBVVLL.js +13 -0
- package/dist-cli/chunks/control-NSGNQFJG.js +4 -0
- package/dist-cli/chunks/daemon-JHF4BJ3U.js +4 -0
- package/dist-cli/chunks/debug-CHV2O2KF.js +200 -0
- package/dist-cli/chunks/desktop-FIPIK4CH.js +31 -0
- package/dist-cli/chunks/detox-CVUJP7K6.js +52 -0
- package/dist-cli/chunks/device-6WCUM4W6.js +18 -0
- package/dist-cli/chunks/diagnose-I6EMWEII.js +43 -0
- package/dist-cli/chunks/disk-cleanup-P27CYFTN.js +4 -0
- package/dist-cli/chunks/drivers-HQ6AKZT6.js +4 -0
- package/dist-cli/chunks/film-UOQFFYNF.js +74 -0
- package/dist-cli/chunks/flow-N27MRRNL.js +4 -0
- package/dist-cli/chunks/help-SF6FEGEE.js +4 -0
- package/dist-cli/chunks/hidden-runtime-alias-NJYFSJKZ.js +4 -0
- package/dist-cli/chunks/home-paths-XOV44JEF.js +4 -0
- package/dist-cli/chunks/inspect-CWFRFQTY.js +4 -0
- package/dist-cli/chunks/install-desktop-KLQDNXZQ.js +4 -0
- package/dist-cli/chunks/login-BPFQENCX.js +28 -0
- package/dist-cli/chunks/logout-5A45ZGY7.js +4 -0
- package/dist-cli/chunks/maestro-E4CSDMLM.js +104 -0
- package/dist-cli/chunks/maestro-generate-CKMOJQX2.js +36 -0
- package/dist-cli/chunks/mode-SZYNMIQF.js +26 -0
- package/dist-cli/chunks/optional-demo-registry-FRH4SFVG.js +4 -0
- package/dist-cli/chunks/perf-VXU2XJAX.js +48 -0
- package/dist-cli/chunks/permissions-IRIAOVND.js +13 -0
- package/dist-cli/chunks/record-RY7FPLLH.js +72 -0
- package/dist-cli/chunks/report-issue-KNWXOE53.js +29 -0
- package/dist-cli/chunks/runtime-AR3GJTGK.js +4 -0
- package/dist-cli/chunks/screenshot-command-QLALR6GY.js +159 -0
- package/dist-cli/chunks/screenshot-layers-OQFMFVRL.js +20 -0
- package/dist-cli/chunks/screenshots-capture-6TSRJFGB.js +42 -0
- package/dist-cli/chunks/serve-F54J2NQO.js +44 -0
- package/dist-cli/chunks/setup-7V6UKX4U.js +32 -0
- package/dist-cli/chunks/skills-QQBOSETH.js +50 -0
- package/dist-cli/chunks/storage-TAPMKK5O.js +37 -0
- package/dist-cli/chunks/store-7NX3S43H.js +4 -0
- package/dist-cli/chunks/telemetry-E7BUK6RA.js +4 -0
- package/dist-cli/chunks/timeline-IAM564NA.js +24 -0
- package/dist-cli/chunks/upgrade-CD55QXIX.js +7 -0
- package/dist-cli/chunks/upload-GZ6LIMYM.js +4 -0
- package/dist-cli/chunks/version-3DYJ5AS3.js +6 -0
- package/dist-cli/chunks/web-6NW6YLPI.js +4 -0
- package/dist-cli/chunks/what-happened-CZZDOOVG.js +25 -0
- package/dist-lib/agent-daemon-client.cjs +468 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +725 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +195 -0
- package/dist-lib/backend-origin.cjs +70 -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/cli-constants.cjs +32 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +2863 -0
- package/dist-lib/detox/jest-preset.cjs +51 -0
- package/dist-lib/dev-bundle-resolution.cjs +223 -0
- package/dist-lib/home-paths.cjs +421 -0
- package/dist-lib/host/bridge-host.cjs +5600 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +373 -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/websocket-proxy.cjs +214 -0
- package/dist-lib/index.cjs +513 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/menu.cjs +620 -0
- package/dist-lib/menu.mjs +581 -0
- package/dist-lib/metro.cjs +297 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1034 -0
- package/dist-lib/sdk.cjs +2333 -0
- package/dist-lib/sdk.mjs +2240 -0
- package/dist-lib/skills.cjs +23911 -0
- package/dist-lib/vite.cjs +358 -0
- package/package.json +189 -18
- package/scripts/dev-server-scanner.ts +941 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/scripts/postinstall.cjs +3 -0
- package/scripts/preuninstall.cjs +118 -0
- package/skills/contrast/SKILL.md +171 -0
- package/skills/rnx-debug/SKILL.md +425 -0
- package/skills/rnx-perf/SKILL.md +295 -0
- package/skills/rnx-setup/SKILL.md +160 -0
- package/skills/rnx-test/SKILL.md +292 -0
- package/skills/rnx-visual/SKILL.md +122 -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 +573 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +204 -0
- package/src/backend-origin.ts +49 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +171 -0
- package/src/browser-cache.ts +16 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +32 -0
- package/src/codex-client.ts +215 -0
- package/src/config.ts +134 -0
- package/src/dev-bundle-resolution.ts +236 -0
- package/src/disk-cleanup.ts +466 -0
- package/src/home-paths.ts +552 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2626 -0
- package/src/host/fetch-proxy-handler.ts +397 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/websocket-proxy.ts +210 -0
- package/src/index.ts +12 -705
- package/src/jump-to-source-babel.ts +135 -0
- package/src/menu.ts +734 -0
- package/src/metro-plugin.ts +139 -0
- package/src/native-dev-bundle-url.ts +96 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +92 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/render-mode.ts +27 -0
- package/src/runtime-assets.ts +228 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +454 -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 +7 -0
- package/src/settings-bundle.ts +418 -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/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +190 -0
- package/src/vite-plugin.ts +1750 -0
- package/src/worklets-babel.ts +134 -0
|
@@ -0,0 +1,292 @@
|
|
|
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 `rnxsim describe` errors, load
|
|
13
|
+
> `/rnx-setup` first.
|
|
14
|
+
|
|
15
|
+
## route in (decision tree)
|
|
16
|
+
|
|
17
|
+
- have an existing `.maestro/` directory? → [Maestro](#branch--maestro)
|
|
18
|
+
- have existing Detox specs? → [detox drop-in](#branch--detox-drop-in)
|
|
19
|
+
- writing tests fresh, want plain-language generation, or want live authoring? → [Maestro](#branch--maestro)
|
|
20
|
+
|
|
21
|
+
Do not invent a third runner for how a Maestro flow was authored.
|
|
22
|
+
|
|
23
|
+
## anti-patterns (apply to both runners)
|
|
24
|
+
|
|
25
|
+
- **`sleep` is almost always a bug.** the underlying CLI auto-settles
|
|
26
|
+
before reads and polls layout hash after writes. if a flow fails
|
|
27
|
+
without a `sleep`, the fix is `waitFor`, `waitForAnimationToEnd`, or
|
|
28
|
+
`wait idle` — not a blind timer. the one legitimate case: a
|
|
29
|
+
user-controlled debounce ("press and hold for 2s").
|
|
30
|
+
- **prefer testID (`tapOn: { id: ... }`, `by.id(testID)`) over text.** text breaks
|
|
31
|
+
under i18n, copy churn, and duplicate strings. add `testID` during
|
|
32
|
+
implementation and author flows against it.
|
|
33
|
+
- **pin a sim before running a suite.** parallel CI jobs colliding
|
|
34
|
+
on the default bridge port is the #1 flake source. pass `--port` in
|
|
35
|
+
CI and `rnxsim use <id>` locally.
|
|
36
|
+
- **every flow ends with an assert, not a screenshot.** a screenshot
|
|
37
|
+
passes as long as the flow didn't throw. `assertVisible` (or
|
|
38
|
+
`expect(...).toBeVisible()`) actually verifies the happy path.
|
|
39
|
+
- **don't run a 40-step "smoke everything" flow.** when it fails, you
|
|
40
|
+
can't tell what regressed. split into `login.yaml`, `compose.yaml`,
|
|
41
|
+
`settings.yaml`, chain with `runFlow`.
|
|
42
|
+
|
|
43
|
+
## you're done when
|
|
44
|
+
|
|
45
|
+
- the suite passes locally and in CI
|
|
46
|
+
- every flake is either fixed or marked `xfail` with a written reason
|
|
47
|
+
and a removal date
|
|
48
|
+
- the green run took noticeably less wall-clock than the previous
|
|
49
|
+
baseline (a suite that grows slower without growing wider is a smell)
|
|
50
|
+
|
|
51
|
+
## branch — Maestro
|
|
52
|
+
|
|
53
|
+
Write declarative Maestro YAML. The same command runs existing suites,
|
|
54
|
+
generates tests, records live drafts, profiles, and uploads replayable results.
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
rnxsim maestro test .maestro/login.yaml # run one flow
|
|
58
|
+
rnxsim maestro test .maestro/login.yaml --record # record video
|
|
59
|
+
rnxsim maestro test .maestro/login.yaml --profile # include perf stats
|
|
60
|
+
rnxsim maestro test .maestro/ # run the suite
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Author from a live interaction:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
rnxsim maestro start # begin a draft
|
|
67
|
+
rnxsim do tap-id loginButton # drive the app via the CLI
|
|
68
|
+
rnxsim maestro keep # save the step
|
|
69
|
+
rnxsim do type "user@example.com"
|
|
70
|
+
rnxsim maestro keep
|
|
71
|
+
rnxsim do tap-id submit
|
|
72
|
+
rnxsim maestro keep
|
|
73
|
+
rnxsim maestro end --output .maestro/login.yaml --validate
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
the recorder emits minimal YAML — no coordinate dumps, no `sleep` noise —
|
|
77
|
+
because the underlying CLI already settled between every action.
|
|
78
|
+
|
|
79
|
+
exemplar flow:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
# .maestro/login.yaml
|
|
83
|
+
- launchApp: {}
|
|
84
|
+
|
|
85
|
+
- waitFor:
|
|
86
|
+
text: 'Sign in'
|
|
87
|
+
timeout: 10000
|
|
88
|
+
|
|
89
|
+
- tapOn:
|
|
90
|
+
id: emailInput
|
|
91
|
+
- inputText: '${USERNAME}'
|
|
92
|
+
|
|
93
|
+
- tapOn:
|
|
94
|
+
id: passwordInput
|
|
95
|
+
- inputText: '${PASSWORD}'
|
|
96
|
+
|
|
97
|
+
- tapOn:
|
|
98
|
+
id: signInButton
|
|
99
|
+
- waitForAnimationToEnd: true
|
|
100
|
+
|
|
101
|
+
- assertVisible: 'Welcome back'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
run with env interpolation:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
rnxsim maestro --env USERNAME=alice --env PASSWORD=hunter2 \
|
|
108
|
+
test .maestro/login.yaml \
|
|
109
|
+
--record --profile
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
automatic settling inside flows: every tap, type, scroll, swipe, and
|
|
113
|
+
drag is backed by the same primitives that auto-wait for transitions on
|
|
114
|
+
the way in and layout stability on the way out. you rarely need
|
|
115
|
+
explicit waits; when you do, `waitForAnimationToEnd: true` maps to the
|
|
116
|
+
CLI's `wait idle` with a generous budget. use it before any
|
|
117
|
+
`assertVisible` that depends on a transition longer than ~400 ms.
|
|
118
|
+
|
|
119
|
+
Generate and immediately run a Maestro flow from a plain-language goal:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
rnxsim maestro generate "log in with the demo account and verify the dashboard"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Generation writes the same deterministic Maestro artifact, uploads its replay,
|
|
126
|
+
and registers it as a Maestro run. The dashboard does not distinguish how it
|
|
127
|
+
was authored.
|
|
128
|
+
|
|
129
|
+
### existing Maestro suites
|
|
130
|
+
|
|
131
|
+
if you already have a `.maestro/` directory, the migration is one
|
|
132
|
+
command change:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
# before
|
|
136
|
+
maestro test .maestro/login.yaml
|
|
137
|
+
|
|
138
|
+
# after
|
|
139
|
+
rnxsim maestro test .maestro/login.yaml
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
no test rewrites, no detox config, no simulator. the runtime auto-launches
|
|
143
|
+
an rnx shell if one isn't running.
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
rnxsim maestro # auto-discover .maestro/ or maestro/
|
|
147
|
+
rnxsim maestro test .maestro/login.yaml
|
|
148
|
+
rnxsim maestro test .maestro/ # every flow in a directory
|
|
149
|
+
rnxsim maestro init # scaffold a starter flow
|
|
150
|
+
rnxsim maestro --list-compat # supported / partial / unsupported
|
|
151
|
+
rnxsim maestro --env USERNAME=alice test .maestro/login.yaml
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### supported verbs
|
|
155
|
+
|
|
156
|
+
`launchApp`, `stopApp`, `clearState`, `tapOn`, `tapAtCoords`, `longPressOn`, `inputText`,
|
|
157
|
+
`pressKey`, `dispatchKey`, `hideKeyboard`, `eraseText`, `assertVisible`,
|
|
158
|
+
`assertNotVisible`, `assertTreeContains`, `waitFor`, `extendedWaitUntil`,
|
|
159
|
+
`waitForAnimationToEnd`, `scroll`, `scrollUntilVisible`, `scrollTo`,
|
|
160
|
+
`swipe`, `pinch`, `takeScreenshot`, `dumpTree`, `back`, `repeat.times`,
|
|
161
|
+
`runFlow`, `when` (`visible` / `notVisible` / `platform` / `true`),
|
|
162
|
+
`optional: true`, `onFlowStart` / `onFlowComplete`, `copyTextFrom` +
|
|
163
|
+
`${maestroCopiedText}`, `evalScript`, `openLink`, env var `${NAME}`
|
|
164
|
+
interpolation.
|
|
165
|
+
|
|
166
|
+
### partial
|
|
167
|
+
|
|
168
|
+
- `clearKeychain` — no keychain in rnx; logs a warning and continues.
|
|
169
|
+
if a test depends on keychain state being reset, add an explicit
|
|
170
|
+
in-app "sign out" step instead.
|
|
171
|
+
- `when.platform` — rnx emulates iOS; android branches are skipped.
|
|
172
|
+
- `openLink` — navigates `window.location`; no OS-level app routing.
|
|
173
|
+
|
|
174
|
+
### not yet implemented
|
|
175
|
+
|
|
176
|
+
these throw `unsupported maestro verb: X`:
|
|
177
|
+
|
|
178
|
+
- `travel`, `setLocation`, `setAirplaneMode`, `killApp`
|
|
179
|
+
- `addMedia`
|
|
180
|
+
- `repeat.while` (use `repeat.times`)
|
|
181
|
+
|
|
182
|
+
### troubleshooting
|
|
183
|
+
|
|
184
|
+
- **"no maestro flows found"** — pass an explicit path
|
|
185
|
+
(`rnxsim maestro test path/to/flow.yaml`) or run `rnxsim maestro
|
|
186
|
+
init` to scaffold one.
|
|
187
|
+
- **"missing environment variable for flow placeholder: FOO"** — pass
|
|
188
|
+
`--env FOO=bar` or export it. pick one mechanism per repo and document
|
|
189
|
+
it in the flow directory's README.
|
|
190
|
+
- **flow can't find an element** — rnx's matcher uses testID + visible
|
|
191
|
+
text. UIKit-only labels that real maestro scrapes from the host OS won't
|
|
192
|
+
resolve here; switch to testID.
|
|
193
|
+
- **flow runs but nothing renders** — confirm the bundler your flow
|
|
194
|
+
targets is reachable and pass `--url <port>` or add `app: <port>`
|
|
195
|
+
frontmatter at the top of the YAML.
|
|
196
|
+
|
|
197
|
+
## branch — detox drop-in
|
|
198
|
+
|
|
199
|
+
an existing detox suite runs against rnx with **one line** of jest
|
|
200
|
+
config and zero test changes.
|
|
201
|
+
|
|
202
|
+
`import { by, device, element, expect, waitFor } from 'detox'` is
|
|
203
|
+
rewritten by jest to an rnx-backed driver that drives the real shell
|
|
204
|
+
over playwright. matchers, assertions, and the `waitFor` API match the
|
|
205
|
+
upstream detox surface.
|
|
206
|
+
|
|
207
|
+
### one-line install
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// sootsim-detox.config.cjs
|
|
211
|
+
module.exports = {
|
|
212
|
+
preset: 'rnxsim/detox/jest-preset',
|
|
213
|
+
rootDir: __dirname,
|
|
214
|
+
testMatch: ['<rootDir>/e2e/**/*.test.ts'],
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
or, if you already have a jest config, pull in just the module mapper:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
moduleNameMapper: {
|
|
222
|
+
'^detox$': require.resolve('rnxsim/detox'),
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
keep the **preset**, not just the mapper — the preset wires up the
|
|
227
|
+
global setup/teardown that tears the shell down between suites. mapper-only
|
|
228
|
+
works until your first hung shell.
|
|
229
|
+
|
|
230
|
+
### commands
|
|
231
|
+
|
|
232
|
+
```sh
|
|
233
|
+
rnxsim detox # run e2e/, test/e2e/, or detox/
|
|
234
|
+
rnxsim detox --config my.cjs # use a specific jest config
|
|
235
|
+
rnxsim detox --watch # jest watch mode
|
|
236
|
+
rnxsim detox -t "login" # --testNamePattern
|
|
237
|
+
rnxsim detox --headed # keep the shell window visible
|
|
238
|
+
rnxsim detox --no-launch # don't auto-launch a shell
|
|
239
|
+
rnxsim detox --port 5173 # explicit bridge port for CI
|
|
240
|
+
rnxsim detox init # scaffold config + e2e/example.test.ts
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### compat surface
|
|
244
|
+
|
|
245
|
+
| detox API | rnx |
|
|
246
|
+
| ---------------------------------- | ------- |
|
|
247
|
+
| `element(by.id(...))` | supported |
|
|
248
|
+
| `element(by.text(...))` | supported — but prefer `by.id` |
|
|
249
|
+
| `element(by.label(...))` | supported |
|
|
250
|
+
| `element(by.traits([...]))` | supported for the iOS trait set |
|
|
251
|
+
| `.toBeVisible()` / `.not.toBeVisible()` | supported |
|
|
252
|
+
| `.toHaveText(...)` / `.toHaveLabel(...)` | supported |
|
|
253
|
+
| `.tap()` / `.longPress()` | supported |
|
|
254
|
+
| `.typeText(...)` / `.replaceText(...)` | supported |
|
|
255
|
+
| `.scroll(...)` / `.scrollTo(...)` | supported |
|
|
256
|
+
| `.swipe(...)` | supported |
|
|
257
|
+
| `.setColumnToValue(...)` | supported for uniquely named picker rows |
|
|
258
|
+
| `waitFor(...).toBeVisible().withTimeout(...)` | supported |
|
|
259
|
+
| `device.launchApp(...)` / `device.reloadReactNative()` | supported |
|
|
260
|
+
| `device.shake()` | partial — emits the JS event, no haptic |
|
|
261
|
+
| `device.sendUserNotification(...)` | not yet |
|
|
262
|
+
|
|
263
|
+
### troubleshooting
|
|
264
|
+
|
|
265
|
+
- **"no rnx shell reachable"** — start one yourself
|
|
266
|
+
(`rnxsim open <port>`) or pass `--port <n>`.
|
|
267
|
+
- **"jest cannot find module 'detox'"** — the preset isn't wired. confirm
|
|
268
|
+
`preset: 'rnxsim/detox/jest-preset'` is in the jest config jest
|
|
269
|
+
actually loaded (`--config` overrides package.json).
|
|
270
|
+
- **matchers fail on elements that exist** — canvas has no DOM. use
|
|
271
|
+
`by.id` (testID); `rnxsim describe` shows what the shell sees.
|
|
272
|
+
- **passes locally, flakes in CI** — the shell boots asynchronously.
|
|
273
|
+
bump jest `testTimeout` to 10–15 s and add a `waitFor(element(...))
|
|
274
|
+
.toBeVisible().withTimeout(10000)` before the first assertion.
|
|
275
|
+
- **port conflicts under parallel CI** — pass `--port` explicitly per
|
|
276
|
+
job so workers don't collide on the default bridge port.
|
|
277
|
+
|
|
278
|
+
## recovery — common failure modes (both runners)
|
|
279
|
+
|
|
280
|
+
- **"no flows found"** — confirm the working directory or pass an explicit
|
|
281
|
+
`.maestro/` path.
|
|
282
|
+
- **"missing env variable"** — set with `--env` (or jest env config) and
|
|
283
|
+
document the variable list in the flow directory's README so new
|
|
284
|
+
contributors aren't guessing.
|
|
285
|
+
- **CI flakes but local passes** — bump test timeouts to 10–15 s before
|
|
286
|
+
the first assertion, pin the bridge port, and run with `--video` so
|
|
287
|
+
failing Maestro runs land a recording next to the output.
|
|
288
|
+
- **a flake reproduces but resists fixing** — don't paper over with
|
|
289
|
+
`sleep`. open `/rnx-debug` against the same sim, capture
|
|
290
|
+
the timeline (`rnxsim timeline start` → reproduce → `rnxsim
|
|
291
|
+
what-happened`), and look for the missing animation-completion or
|
|
292
|
+
the unresolved fetch that's racing the assertion.
|
|
@@ -0,0 +1,122 @@
|
|
|
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** (`rnxsim screenshot`)
|
|
11
|
+
and **structural diff** (`rnxsim 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 `rnxsim 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.** `rnxsim 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 `rnxsim 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, or you're diffing layout, not rendering. use
|
|
43
|
+
`--themes` to capture light and dark together.
|
|
44
|
+
- **commit baselines alongside the code change that produced them.**
|
|
45
|
+
baseline churn without code churn means someone captured noise.
|
|
46
|
+
|
|
47
|
+
## capturing — `rnxsim screenshot`
|
|
48
|
+
|
|
49
|
+
fast, no extra setup, works on any loaded app:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
rnxsim screenshot --output app.png # full canvas
|
|
53
|
+
rnxsim screenshot --area 0,200,393,400 # crop to a logical rect
|
|
54
|
+
rnxsim screenshot --id loginButton -o btn.png # crop to a node by testID
|
|
55
|
+
rnxsim screenshot --text "Sign in" # crop to a node by text
|
|
56
|
+
rnxsim screenshot --gallery --themes # crawl screens → HTML gallery, light+dark
|
|
57
|
+
rnxsim screenshot --no-shell -o tenant.png # tenant surface only, no iOS chrome
|
|
58
|
+
rnxsim screenshot --shell-only # only the simulated iOS chrome
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`--gallery` crawls reachable screens and writes a browsable HTML gallery
|
|
62
|
+
— the fastest way to eyeball a whole app for surprise rendering changes.
|
|
63
|
+
|
|
64
|
+
## comparing pixels
|
|
65
|
+
|
|
66
|
+
capture both sides to PNGs, then diff with any standard image tool.
|
|
67
|
+
|
|
68
|
+
**rnx vs rnx (regression across your own commits):**
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
rnxsim screenshot -o before.png # baseline, before your change
|
|
72
|
+
# … make the rendering change, let the sim hot-reload …
|
|
73
|
+
rnxsim screenshot -o after.png # after your change
|
|
74
|
+
npx pixelmatch before.png after.png diff.png 0.1 # writes diff.png
|
|
75
|
+
# or: magick compare before.png after.png diff.png
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**rnx vs native iOS (fidelity to the real thing):**
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
# native side — capture from your booted iOS simulator
|
|
82
|
+
xcrun simctl io booted screenshot native.png
|
|
83
|
+
# rnx side — same screen, same appearance
|
|
84
|
+
rnxsim screenshot -o rnx.png
|
|
85
|
+
npx pixelmatch native.png rnx.png diff.png 0.1
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
crop both to the same region (`--area` / `--id`) when you only care
|
|
89
|
+
about one element — a full-screen diff buries a 3px font-baseline shift
|
|
90
|
+
under unrelated chrome.
|
|
91
|
+
|
|
92
|
+
## typical workflow
|
|
93
|
+
|
|
94
|
+
1. structural diff first — confirm the change is genuinely visual.
|
|
95
|
+
2. capture the affected screen with `rnxsim screenshot` (crop to the
|
|
96
|
+
element if you can).
|
|
97
|
+
3. diff against the baseline (previous commit, or a native capture).
|
|
98
|
+
4. iterate the rendering until the diff is under threshold.
|
|
99
|
+
5. open the gallery (`--gallery`) and scroll through every screen — a red
|
|
100
|
+
patch on one icon is easy to miss when you're focused on one case.
|
|
101
|
+
|
|
102
|
+
## you're done when
|
|
103
|
+
|
|
104
|
+
- the diff is under threshold on every affected case, **or**
|
|
105
|
+
- the differences are explicitly accepted, baselines re-captured, and
|
|
106
|
+
both halves (code + baselines) committed together.
|
|
107
|
+
- the gallery shows no surprise changes when you scroll through every
|
|
108
|
+
screen, not just the one you targeted.
|
|
109
|
+
|
|
110
|
+
## recovery — common failure modes
|
|
111
|
+
|
|
112
|
+
- **a single icon/glyph diffs** — font-load race or image not yet
|
|
113
|
+
cached. recapture after `rnxsim wait idle`, or navigate away and back
|
|
114
|
+
to seed the cache.
|
|
115
|
+
- **everything diffs by a constant offset** — you captured the two sides
|
|
116
|
+
on different devices or appearances. pin both, recapture.
|
|
117
|
+
- **baselines churn without a code change** — someone captured noise (a
|
|
118
|
+
lingering badge, half-open keyboard, a drifting clock). revert and
|
|
119
|
+
recapture from a clean, idle state.
|
|
120
|
+
- **persistent delta on the same font / shadow / gradient across
|
|
121
|
+
unrelated screens** — that's an engine-level rendering fidelity issue,
|
|
122
|
+
not your app. report it with the affected captures attached.
|