| type | Reference | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| title | Running e2e tests | ||||||||
| description | The canonical, minimal command set for running React Native Firebase e2e tests on every platform. | ||||||||
| tags |
|
||||||||
| timestamp | 2026-06-25 00:00:00 UTC |
Canonical local e2e commands. Use only these commands. -ci variants are CI-only. Avoid :test-cover-reuse, :test-cover-and-process, :test-reuse (stale native risk). If another doc disagrees, this wins.
All e2e how-to lives here; other docs link here — they do not define alternate entrypoints or commands.
Never invoke the test runner (Jet), Detox, Metro, or emulators directly. Use only the repo-root yarn tests:* commands defined in this document (for example yarn tests:packager:jet, yarn tests:emulator:start, yarn tests:<platform>:test-cover). Do not run jet, npx jet, yarn jet, detox test, cd tests && …, or ad-hoc Metro/emulator start commands. When another doc mentions e2e, Jet, Detox, or pre-flight, follow the link to this runbook — do not infer commands from log output or implementation details.
Install, prepare, and validation commands are not in this doc — they live in agent command policy (read before any non-e2e shell command).
yarn # applies .yarn/patches (jet, mocha-remote-*, detox); installs tests devDeps incl. babel-plugin-istanbul- Packager (background):
yarn tests:packager:jet- Emulators (background, always):
yarn tests:emulator:start-
Rebuild when needed
- Native changed →
yarn tests:ios:build/yarn tests:android:buildbefore e2e. macOS uses firebase-js-sdk only — no native rebuild. packages/*/lib/**changed →yarn lerna:preparemust run to completion (exit 0) before anything else — Metro servesdist/module/**, notlib/**. See prepare completion gate and agent command policy § prepare must finish first. After prepare finishes, restart the packager withyarn tests:packager:jet-reset-cachewhen Metro was already running (Rules §1).- TS coverage: iOS/Android embed JS at build time; run
:buildbefore:test-coverso Istanbul + patched test-runner coverage upload is in app. macOS loads from Metro live; after test-runner patch changes, restart the packager withyarn tests:packager:jet-reset-cache(Rules §1).
- Native changed →
-
Always run with coverage:
yarn tests:ios:test-cover
yarn tests:android:test-cover
yarn tests:macos:test-coverClean :build + :test-cover each time — not reuse variants.
-
Report locations — Coverage design.
-
One e2e at a time — never overlap
:test-coverruns on one host. All platforms share Metro:8081and the test-runner WebSocket port (default 8090); parallel runs race on coverage/device/emulator state. Every run starts after clean pre-flight. Log triage for port/orchestration markers: test-runner host orchestration. -
No source edits during e2e — wait/cancel cleanly before editing
packages/**,tests/**, or bundle-affecting OKF docs. Saves can hot reload/rebundle and invalidate tests/coverage.
Use validation tiers: unit-focused, area-focused, full. Match tier to work type. Runs are serial from clean pre-flight. Log long output; upstream gets exit code + short summary.
Policy: OKF documentation and commit policy. Terms: iteration vocabulary.
Internal only — do not invoke sub-commands. Wait on the single repo-root :test-cover command; Detox/Jest and the test runner start automatically.
yarn tests:android:test-cover # only command you run
└─ (internal) detox → jest → firebase.test.js → test runner on :8090 → app
macOS: yarn tests:macos:test-cover only — same :8090 transport, no Detox.
Do not poll pgrep, detox, process names, or :8090 for completion. They match stale wrappers, orphans, zombies, and contention.
No commands to run from this section — for interpreting :test-cover logs and CI artifacts only. Patch workflow: detox-patches.md. CI triage: iOS orchestration.
| Port | Protocol | Role |
|---|---|---|
8090 (default JET_REMOTE_PORT) |
WebSocket (mocha-remote-*) |
App ↔ host test transport; drives Mocha in the app |
8091 (default JET_REMOTE_PORT + 1, override RNFB_JET_CONTROL_PORT) |
HTTP POST only | Host ↔ test-runner control plane — not used by the app |
Why two ports — Port 8090 is a WebSocket server (ws library). Plain HTTP POST to that socket (e.g. /launch-ready) gets 426 Upgrade Required and can crash the runner with ERR_HTTP_HEADERS_SENT if a control handler shares the same HTTP stack. Control endpoints therefore live on a separate small HTTP server (startControlHttpServer in the test-runner patch).
Launch gate (orchestration race fix) — firebase.test.js starts the test runner with RNFB_JET_DEFER_RUN=1. It listens on 8090 and defers server.run() until the host signals launch success:
- Host waits for TCP 8090, then Metro (debug) if needed, then
launchAppWithRetry. - Host
POSTs/orchestrate-state({ "phase": "launch-pending" | "launch-ok" | … }) to the control port (best-effort diagnostics). - After
launchAppsucceeds, hostPOSTs/launch-ready→ test runner callsserver.run()and the app may receive the mocha-remoterunaction. - Mocha tests must not start during a stuck or retried
launchApp; on inner launch retry the host may kill and respawn the test runner beforeterminateApp/simulator reboot.
Log markers — [rnfb-e2e] orchestrate-state=…, [jet-control] deferring server.run until POST /launch-ready, [jet-control] launch-ready received, [jet-control] listening on http://…:8091, [jet-coverage] …, Jet client connected.
Pre-flight — Host-clear probes check 8090 only (stray test-runner WS listener). 8091 may be open during a run; do not treat it as a stale-process signal by itself.
GitHub Actions Testing E2E iOS adds CI-only steps local :test-cover does not run: pre-boot (boot-simulator.sh), one filtered sim-app.log stream, wait-for-load-settle.sh (threshold 20) immediately before Detox, and optional video when record_screens: true. Host syslog and unfiltered simulator logs are disabled to reduce runner baseload.
Canonical owner: iOS CI baseload policy. Artifact names and triage: simulator logging and video.
- Pre-flight; if host-clear probes fail, pre-flight recovery first.
- One foreground Shell command; set
block_until_mslarge enough (~15m macOS, ~45–60m iOS/Android). Do not background/poll. - From repo root, tee canonical command:
yarn tests:android:test-cover 2>&1 | tee /tmp/rnfb-e2e-android.log
yarn tests:ios:test-cover 2>&1 | tee /tmp/rnfb-e2e-ios.log
yarn tests:macos:test-cover 2>&1 | tee /tmp/rnfb-e2e-macos.logUse /tmp/rnfb-e2e-<platform>.log (overwrite each iteration). Do not substitute other entrypoints — see agent rule.
- Completion = shell exit code.
0finished; non-zero failed/aborted. Read log for counts. - Parse log tail; do not infer from processes:
rg 'passing|failing' /tmp/rnfb-e2e-<platform>.log | tail -1
rg '^\s+\d+\)' /tmp/rnfb-e2e-<platform>.log # failure blocks, if any
rg 'Tests Complete|jet-coverage.*merged' /tmp/rnfb-e2e-<platform>.log | tail -3Markers: ✨ Tests Complete ✨, Jest N passing / N failing, [jet-coverage] merged … before NYC shutdown, [rnfb-e2e] orchestrate-state=, [jet-control] launch-ready received.
- Return only platform, exit code, pass/fail line, failing tests, log path, optional coverage-gap line. No full log upstream.
Canonical owner for host-clear probes, recovery after abort, and service checks. Other OKF docs link here by reference — do not duplicate commands or probes elsewhere.
Run all four steps before every :test-cover. After an interrupted run, run pre-flight recovery and re-run the probes.
If product code under packages/*/lib/** was edited in this session, yarn lerna:prepare (or scoped yarn lerna run prepare --scope …) must have fully finished with exit code 0 before pre-flight steps 1–3 or any :test-cover / :build.
- Wait for the prepare shell to return — do not batch prepare in parallel with Metro restart, pre-flight probes, or e2e in the same agent turn.
- Then restart Metro when it was already running:
yarn tests:packager:jet-reset-cache(Rules §3). - Then continue with host-clear probes and service checks below.
Skipping this gate causes missing or half-written dist/module/** while Metro /status still returns 200 — a common source of bundle-load and module-not-found failures that look like product bugs.
Owner for install/prepare serialization: agent command policy § prepare must finish first.
No in-flight test run on the target platform:
| Platform | Clear when |
|---|---|
| Android | Host-clear probes pass (no instrumentation PID) |
| iOS | Host-clear probes pass — zero booted simulators and no stray listener on :8090. Detox boots iPhone 17 from tests/.detoxrc.js; do not pre-boot or leave simulators running. |
| macOS | Host-clear probes pass (no io.invertase.testing process) |
Also wait for any visible unfinished yarn tests:*:test-cover.
Host-clear probes — run the block for your platform; exit 0 = clear (chain with &&):
# iOS — booted-device count must be 0
test "$(xcrun simctl list devices booted | grep -c '(Booted)' || true)" -eq 0
test -z "$(lsof -nP -iTCP:8090 -sTCP:LISTEN -t 2>/dev/null || true)"
# Android
! adb -s emulator-5554 shell pidof com.invertase.testing.test >/dev/null 2>&1
# macOS
! pgrep -x io.invertase.testing >/dev/null 2>&1Pre-flight recovery — when probes fail after abort, kill, or EADDRINUSE on :8090. Then re-run host-clear probes.
# Android
adb -s emulator-5554 shell am force-stop com.invertase.testing
adb -s emulator-5554 shell am force-stop com.invertase.testing.test
# iOS — Detox re-boots iPhone 17 after shutdown booted
lsof -nP -iTCP:8090 -sTCP:LISTEN -t | xargs kill 2>/dev/null || true
pkill -f 'detox test --configuration ios' 2>/dev/null || true
pkill -f 'jet.js --target=ios' 2>/dev/null || true
xcrun simctl shutdown bootedDo not use boot-simulator.sh or simctl shutdown all as routine prep (what not to do).
Metro and emulators must be running and responsive — do not assume from a prior session or background start.
curl -sf http://127.0.0.1:8081/status >/dev/null # Metro (127.0.0.1 matches test app bundle URL)
curl -sf http://127.0.0.1:8080 >/dev/null # Firestore emulatorIf either fails: start yarn tests:packager:jet and yarn tests:emulator:start (background); re-check until both pass. After yarn lerna:prepare has finished (step 0) or test-runner patch edits, restart the packager with yarn tests:packager:jet-reset-cache (Rules §1) — never restart Metro while prepare is still running.
A listener on :8081 or :8080 is not sufficient — HTTP checks must succeed.
Confirm tests/app.js / tests/globals.js match the item's validation_tier (iteration vocabulary), not the branch's committed harness.
| Tier | Harness before :test-cover |
|---|---|
Unit-focused (implementation) |
Area narrowing required — trim modules + load only the spec under change (e.g. firestore + Pipeline.e2e.js); .only OK locally. Set RNFBDebug = true locally in tests/globals.js (§ fail-fast). |
Area-focused (independent-review, baseline-capture) |
Area narrowing required — same module/spec trim as unit-focused; load full spec file(s) for the package area; no .only. Set RNFBDebug = true locally (§ fail-fast). |
Full (pre-merge-validation) |
Revert all narrowing — full app (require.context, all modules); RNFBDebug = false (committed default). |
Committed full harness on the branch does not override unit-focused or area-focused tier for local runs. Package workflows define area setup (e.g. pipelines § area harness). How to edit tests/app.js: two platform blocks. Never commit narrowing until full tier.
See Harness narrowing gate (blocking) — a run that skips step 3 does not close implementation_gate or review_gate.
Completion = shell exit code + log markers — not open-ended log tailing.
| Platform | Early markers (≈2–3 min) | Done |
|---|---|---|
| macOS | Jet client connected |
✨ Tests Complete ✨, Jest N passing |
| iOS/Android | Detox launch done, Jet client connected |
Same |
If stalled — no new markers for 5 minutes, or past tier budget (~15m macOS, ~45–60m iOS/Android) without Tests Complete: treat as interrupted run. Run pre-flight recovery, confirm host-clear probes and services ready, retry. Do not keep watching flat tee output.
- macOS bundle/Metro hangs → ci-workflows/other.md § bundle load hang
- iOS Metro at launch → ci-workflows/ios.md § Metro unresponsive
Do not poll pgrep, process names, or :8090 for completion (above). Stall detection uses missing progress markers, not exit polling.
Both unit-focused and area-focused tiers require area narrowing in tests/app.js / tests/globals.js before the first :test-cover. The only difference between those tiers is whether .only is allowed and whether the full package-area spec loads — not whether the harness stays at full app load.
| Mistake | Symptom | Gate impact |
|---|---|---|
Run :test-cover on committed full harness during implementation or independent-review |
macOS/iOS/Android pass counts in the hundreds or thousands (all modules via require.context) |
Run is invalid — does not close implementation_gate or review_gate |
Narrow only if (Platform.other) or only set initial platformSupportedModules while if (!Platform.other) still pushes full native list |
macOS ~700 firestore tests pass; iOS/Android logs show database, crashlytics, etc.; thousands of tests / Jet WS 1006 under load |
Run is invalid on iOS/Android — see two platform blocks |
| Correct pipeline area harness | ~100 passing per platform for Pipeline.e2e.js only (pipeline workflow) |
Expected for pipeline area runs |
Apply locally before every :test-cover at unit-focused or area-focused tier — even when git shows the full push harness. Revert tests/app.js / tests/globals.js after the run if the branch commit keeps full harness (typical until phase R).
Validation report must state: harness narrowed (yes/no), which module/spec loads, whether pass counts match area scope, and which platforms ran with exit codes. A green full-app run is not a substitute.
Both unit-focused (implementation) and area-focused (baseline-capture, independent-review) require e2e on every platform where the changed module loads in the committed harness — not a subset for convenience.
Determine required platforms from tests/app.js (two platform blocks — use committed lists when deciding macOS vs native requirement, not a narrowed local harness):
| Platform class | When required |
|---|---|
macOS (Platform.other) |
Module appears in the committed if (Platform.other) list (or your narrowed list includes it on macOS) |
| iOS and Android | Module appears in the committed if (!Platform.other) list (or your narrowed list includes it on native) |
Area-focused (baseline-capture, independent-review) — closes review_gate / baseline only when:
- Full loaded package spec(s) with area narrowing (no
.only). - Serial
:test-coveron each required platform above — pre-flight before every run. - Native platforms:
yarn tests:<platform>:buildbefore first:test-coverwhen product/native JS changed (Rules §4). - Subagent/orchestrator return includes a platform matrix: platform, exit code, pass/fail/pending counts, log path.
Invalid shortcuts (do not close gates):
- “macOS + iOS minimum”; skipping Android when the module loads on Android.
- “Skip Android if time tight” or “Android fallback only if iOS failures look env-related” without a fresh Android run.
- Substituting a prior implementer log for
independent-reviewon the frozen tree.
Module-specific skip: only when the module is absent from that platform’s harness list (e.g. messaging is not on macOS). Record in the work-queue Notes — not an oral exception.
Unit-focused (implementation) — native touched: macOS first when the path is TS/web-only; when the module loads on iOS and Android, run both before closing implementation_gate (same narrowing; .only OK locally; never commit).
See also: coverage design § platform parity, validation checklist § handoff.
Checklist (copy before first run):
- Both platform blocks narrowed or disabled — not just macOS / not just initial array.
platformSupportedModuleslists only the package under change (e.g.firestore+app).- Spec load uses direct
requireof the area spec — notrequire.contextfor all packages — when sub-suite narrowing applies; otherwise full packagerequire.contextis OK when the module list is narrowed. - No
.onlywhen tier is area-focused;.onlyoptional when tier is unit-focused. - Grep log: pass count consistent with area scope (~100 for pipeline-only, ~700 for full firestore package on macOS), not full app (~141+ macOS baseline with full load per work queue).
For implementation work type — validation tier unit-focused (change authoring workflow):
- Pre-flight — prepare completion gate when
lib/**changed, host-clear probes, services ready, harness narrowed (step 3),RNFBDebug = truelocally; if probes fail, pre-flight recovery first. - Edit e2e/spec; add
.onlyif needed; never commit narrowing. - macOS first when TS-only:
yarn tests:macos:test-cover 2>&1 | tee /tmp/rnfb-e2e-macos.log— wait for exit code (stalled run if markers stop). - If macOS green and native touched:
yarn tests:<platform>:build && yarn tests:<platform>:test-cover 2>&1 | tee /tmp/rnfb-e2e-<platform>.log; one platform at a time. - Grep log tail → fix → repeat from step 1.
- When
implementation_gatecloses, next work type isindependent-reviewat area-focused tier — frozen tree; no.only; area narrowing per package workflow.
Never overlap runs that use :test-cover. See host rule.
| Rule | Requirement |
|---|---|
| One e2e run at a time | Wait for prior shell exit code + short log summary |
| No overlapping tiers | Never run unit-focused-tier and area-focused-tier :test-cover concurrently on one host |
| Clean pre-flight every run | Pre-flight — host-clear probes, services, harness tier |
| Phase J loop | implementation (Jest + unit-focused) → independent-review (area-focused, frozen tree) → commit — work queue protocol |
| Validation tier | E2e scope | Narrowing allowed | Typical work type |
|---|---|---|---|
| Unit-focused | Backpressure while product code is changing | it.only / describe.only / tight area narrowing in tests/app.js — never commit |
implementation |
| Area-focused | Full loaded spec(s) for the package/area under change | Area narrowing required in tests/app.js / tests/globals.js; no .only |
baseline-capture, independent-review |
| Full | All modules, all platforms | None — revert all narrowing | pre-merge-validation |
Each run owns its blocking :test-cover and returns summaries only.
Run pre-flight recovery, confirm host-clear probes pass, then rerun from repo root: yarn tests:<platform>:build && yarn tests:<platform>:test-cover (foreground; tee if logging).
- Do not invoke the test runner (Jet), Detox, Metro, or emulators except through repo-root
yarn tests:*commands in this doc — see agent rule. - Do not run
:test-cover,:build, Metro restart, or pre-flight whileyarn/yarn lerna:prepareis still in progress — wait for exit 0 first (prepare completion gate). - Do not background
:test-coverand pollpgrep,detox, or process names for completion. - Do not use
:test-cover-reuse,:test-cover-and-process, or:test-reusewhen measuring coverage or closing review gates. - Do not use
:8090listening as “e2e still running” without the platform active signal above. - Do not start iOS/Android/macOS
:test-coverconcurrently on one host. - Do not edit source while a tee'd run is still in progress.
- Do not passively tail tee output when progress markers stop — follow stalled run detection.
- Do not run full harness (
require.context, all modules) for unit-focused/area-focused tier — match harness to tier. - Do not run
.github/workflows/scripts/boot-simulator.sh,simctl shutdown all, orkill -9on:8090as prep.boot-simulator.shis CI-only or internal to iOS test-runner retry.
# Background (once):
yarn tests:emulator:start
yarn tests:packager:jet
# Per platform (rebuild when native changed):
yarn tests:ios:build && yarn tests:ios:test-cover
yarn tests:android:build && yarn tests:android:test-cover
yarn tests:macos:test-coverFull e2e loads every package. Narrow locally; never commit narrowing.
| Kind | Mechanism | Scope |
|---|---|---|
| Area narrowing | tests/app.js + tests/globals.js |
Which modules/specs load (e.g. trim platformSupportedModules; require one spec file instead of require.context) |
| Single-test narrowing | it.only(...) |
One case in a loaded file |
| Single-suite narrowing | describe.only(...) |
One block in a loaded file |
Area narrowing = tests/app.js / tests/globals.js only; not test-runner --grep or packager --target.
Canonical owner for how to narrow the test app. Package workflows name which module/spec; this section defines how to edit tests/app.js so narrowing works on every platform.
Committed tests/app.js builds platformSupportedModules from two separate blocks — only one runs per platform at bundle time:
| Block | Runs on | Committed role |
|---|---|---|
if (Platform.other) { … push … } |
macOS / Other | Full macOS module list |
if (!Platform.other) { … push … } |
iOS / Android | Full native module list |
Committed shape: const platformSupportedModules = [], then both blocks push their full lists.
Common agent mistake: set a narrowed initial array (e.g. ['app', 'firestore']) but leave if (!Platform.other) pushing every native module → macOS looks narrowed (~700 firestore tests) while iOS/Android still run the full app (thousands of tests, unrelated modules like database appear in logs). That invalidates harness narrowing gate on native platforms.
Pattern A — initial array + disable both blocks (recommended for one area on all platforms):
- Replace
const platformSupportedModules = []with the narrowed list (almost always include'app'). - Change both populate blocks to
if (false && Platform.other)andif (false && !Platform.other)so neither block re-expands the list. - Add
// TEMP: <module> area harness — never commitabove your edits.
// TEMP: firestore area harness — never commit
const platformSupportedModules = ['app', 'firestore'];
if (false && Platform.other) {
// committed macOS list — disabled while narrowed
platformSupportedModules.push('app');
// …
}
if (false && !Platform.other) {
// committed iOS/Android list — disabled while narrowed
platformSupportedModules.push('app');
// …
}Pattern B — trim inside each block (when macOS and native lists must differ):
- Keep
const platformSupportedModules = []. - Edit
if (Platform.other)pushes to only the modules needed on macOS. - Edit
if (!Platform.other)pushes to only the modules needed on iOS/Android. - Both blocks must be edited before
:test-coverwhen the work item requires native platforms — editing only one block is invalid.
Do not use if (false && Platform.other) on one block while leaving the other block active unless that asymmetry is intentional.
Module specs load later in loadTests() via platformSupportedModules.includes('<module>') — usually require.context('../packages/<module>/e2e', …).
| Goal | Change |
|---|---|
| Full package area | Leave require.context as-is; narrowing the module list is enough |
| Single spec file | Replace require.context for that module with require('../packages/<module>/e2e/<Spec>.e2e.js') (sub-suite — unit-focused diagnosis only unless package workflow says otherwise) |
Restore committed harness on both blocks:
const platformSupportedModules = []if (Platform.other) { … }— full macOS list, nofalse &&if (!Platform.other) { … }— full native list, nofalse &&- Restore any
require.contextedits; remove// TEMPcomments - Revert
tests/globals.js(RNFBDebug = false) per before merge
Pre-flight check: grep tests/app.js — if either block is if (Platform.other) / if (!Platform.other) without false && and pushes modules outside your area, native or macOS runs are not narrowed.
| Platform | Narrowed firestore-only (full packages/firestore/e2e) |
Pipeline-only (Pipeline.e2e.js) |
|---|---|---|
| macOS | ~700 passing | ~100 passing |
| iOS / Android | Same order of magnitude as macOS for the same spec scope | ~100 passing |
Pass counts in the thousands or unrelated suites (database, crashlytics, …) in the log → re-apply both blocks and re-run.
Area example (Pattern A): firestore-only platformSupportedModules + both blocks disabled; full firestore specs via existing require.context in loadTests.
Package-specific spec names: Firestore pipeline harness, namespace removal § module area harness.
RNFBDebug (tests/globals.js): for unit-focused and area-focused tiers, set globalThis.RNFBDebug = true locally before the first :test-cover — not optional. It prints per-case start/finish and disables Mocha retry/backoff, so failures surface immediately instead of burning time on retries. Committed default must stay false. Revert to false with other harness edits before full tier or commit (§ before merge).
| Kind | Mechanism | When |
|---|---|---|
| Sub-suite narrowing | describe.only / require one e2e file (e.g. Aggregate/count.e2e.js only) |
Unit-focused diagnosis only — after pre-flight is clean and the same failure repeats on back-to-back runs without assertion progress. Never for area-focused gate closure (no .only). Never commit. |
| Single-test narrowing | it.only(...) |
Same as sub-suite — unit-focused diagnosis only |
Package workflows may name default area specs (e.g. Firestore pipeline harness); sub-suite narrowing is tighter than area narrowing for iteration speed.
E2e diagnosis escalation (cross-package): change authoring § implementation inner loop.
Package workflows may further restrict narrowing per validation tier.
All tiers use canonical commands, host rule, and clean pre-flight. Tier names describe scope, not who runs the commands — see iteration vocabulary.
| Validation tier | E2e scope | Narrowing allowed | Typical work type |
|---|---|---|---|
| Unit-focused | Fast loop while product code is changing | it.only / describe.only / tight area narrowing in tests/app.js — never commit |
implementation |
| Area-focused | Full loaded spec(s) for the package/area under change | Area narrowing required in tests/app.js / tests/globals.js; no .only |
baseline-capture, independent-review |
| Full | Unfocused — all modules, all platforms | None — revert all narrowing | pre-merge-validation |
Universal rules:
- E2e is always serial — one
:test-coverat a time on the host. - Every run starts from verified pre-flight; if probes fail, pre-flight recovery before another run.
- Use only canonical commands from this doc.
- Never overlap unit-focused-tier and area-focused-tier
:test-coveron one host.
See also: unit-focused-tier loop, dispatch, pre-merge.
- Devices — Detox boots simulator/emulator (
iPhone 17on iOS,TestingAVDon Android); host-clear probes require zero booted iOS simulators before:test-cover. macOS auto-starts app. - adb empty —
adb kill-server && adb start-server && adb devices - Stale processes — one Metro (
:8081), one emulator set (:8080,:9099,:9000,:4400, …). Stray listener on:8090after a run → pre-flight recovery, then restart background services with Rules §1–2 (yarn tests:packager:jet,yarn tests:emulator:start).
Local stalls — see stalled run detection first (Metro /status, Jet client connected markers).
Native / device logs (remove instrumentation before merge):
- macOS —
log show --predicate 'process == "io.invertase.testing"' --last 10m --style compact; bundle errors → other.md - iOS —
xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process == "testing"'; silent hangs:sample <pid>ontesting - Android —
adb logcat(filter your tags)
Benign noise: iOS Detox EXEC_FAIL "xcrun simctl terminate … com.invertase.testing" … found nothing to terminate — app wasn't running; ignore.
Cloud API pressure — Installations / Remote Config failures with FIS 503 or “Too many server requests” are live-project quota on any platform, not emulator issues. See Firebase testing project — CI triage.
Pre-merge applies once to the branch commit stream before merge/push intended for merge, not after every commit.
- Revert all narrowing (full tier): restore
tests/app.js(platformSupportedModules+require.context), defaultRNFBDebugintests/globals.js, remove all.only, remove native instrumentation. - Pre-flight — host-clear probes pass before each platform run.
- Rebuild if needed (
tests:<platform>:build;yarn lerna:prepareforlib/**). - Full unfocused suite with coverage on iOS, Android, macOS — one platform at a time, all green.
- Stale native build after native edits → rebuild first.
- All three platforms required; macOS exercises the JS SDK path.