|
| 1 | +# Detox yarn patches |
| 2 | + |
| 3 | +E2E runs on **Detox 20.51.0** (`tests/package.json`), applied via Yarn Berry patch: |
| 4 | + |
| 5 | +`.yarn/patches/detox-npm-20.51.0-3e13b6e309.patch` |
| 6 | + |
| 7 | +Patches are maintained in-repo (including by agents). Prefer **editing the patch file directly** or the headless workflow below — `yarn patch-commit` can prompt for overwrite confirmation, which fails in non-interactive shells. |
| 8 | + |
| 9 | +## Inventory |
| 10 | + |
| 11 | +| Change | File(s) | Platforms | Problem | |
| 12 | +|--------|---------|-----------|---------| |
| 13 | +| Disable network idling | `NetworkIdlingResource.kt` | Android | OkHttp never idle in RN Firebase tests → Detox sync timeout | |
| 14 | +| Disable timers idling | `TimersIdlingResource.kt` | Android | RN timer queue never drains → infinite idle wait | |
| 15 | +| Disable Fabric UI idling | `FabricUIManagerIdlingResources.kt` | Android | Stuck mount items on API 36+ / edge-to-edge → false busy | |
| 16 | +| Buffer early `ready` | `AnonymousConnectionHandler.js` | iOS | App sends `ready` before Detox login → `launchApp` stuck | |
| 17 | +| Ignore missing adb reverse on teardown | `ADB.js` | Android | Jet WS 1006 triggers mid-run `reverse --remove` → adb exit 1 | |
| 18 | +| **2× device-registry lock stale** | `ExclusiveLockfile.js` | iOS, macOS, Android | `proper-lockfile` `ECOMPROMISED` before tests start | |
| 19 | + |
| 20 | +Related non-Detox patches: `jet`, `mocha-remote-client`, `mocha-remote-server` (coverage over WebSocket) — see [coverage design](../testing/coverage-design.md). |
| 21 | + |
| 22 | +## Device registry lock (`ECOMPROMISED`) |
| 23 | + |
| 24 | +### Symptom |
| 25 | + |
| 26 | +Detox Test step fails **before any test output**, often ~30–60s after `yarn tests:ios:test:release` starts: |
| 27 | + |
| 28 | +``` |
| 29 | +Error: Unable to update lock within the stale threshold |
| 30 | + at .../proper-lockfile/lib/lockfile.js:109 |
| 31 | + code: 'ECOMPROMISED' |
| 32 | +``` |
| 33 | + |
| 34 | +Build, pre-boot, and app install usually succeed. Codecov and coverage steps are skipped because Jest never starts. |
| 35 | + |
| 36 | +### Cause |
| 37 | + |
| 38 | +Detox serializes access to `~/Library/Detox/device.registry.json` (the **device allocation ledger**: which simulators/emulators are busy, session IDs, PIDs). It uses `proper-lockfile`, which refreshes the lock file mtime on a timer (default **stale = 10s**). If the runner is under load — simulator logging, Firestore emulator, `yeetd`, Xcode build cache — a heartbeat can miss that window and Detox throws `ECOMPROMISED`. |
| 39 | + |
| 40 | +This is a known Detox / CI flake ([Detox #4210](https://github.com/wix/Detox/issues/4210)). Community reports ~1-in-25 on busy macOS CI. |
| 41 | + |
| 42 | +### Mitigation (this repo) |
| 43 | + |
| 44 | +**Patch** `ExclusiveLockfile.js` to pass `{ stale: 20000 }` (2× default) into `plockfile.lockSync` when locking the device registry. |
| 45 | + |
| 46 | +We **do not** run `detox reset-lock-file` in CI. That command wipes `device.registry.json` — useful for local recovery, but it destroys Detox's ledger state and can mask concurrent-session bugs. Extending the stale window preserves the ledger while tolerating slow heartbeats. |
| 47 | + |
| 48 | +Other prerequisites already in place: |
| 49 | + |
| 50 | +- `maxWorkers: 1` in `tests/e2e/jest.config.js` (multi-worker lock contention is a common trigger) |
| 51 | +- Pre-boot simulator before Detox (orthogonal; fixes boot/migration, not lock heartbeats) |
| 52 | + |
| 53 | +### Diagnosing |
| 54 | + |
| 55 | +| Pattern | Meaning | |
| 56 | +|---------|---------| |
| 57 | +| Failure in first minute, no Jest/Detox test lines | Startup lock failure (this issue) | |
| 58 | +| `proper-lockfile` + `ECOMPROMISED` in Detox step log | Confirms lock heartbeat, not test logic | |
| 59 | +| Release fails, debug passes (or vice versa) | Timing/load flake; same root cause | |
| 60 | +| Orphan `node` in job cleanup | Stuck Detox/Jest from lock abort | |
| 61 | + |
| 62 | +```bash |
| 63 | +rg 'ECOMPROMISED|proper-lockfile|Unable to update lock' detox-step.log |
| 64 | +``` |
| 65 | + |
| 66 | +### When to bump further |
| 67 | + |
| 68 | +If `ECOMPROMISED` persists after 20s, consider 30s in the patch (MetaMask used 20s on Bitrise). Re-evaluate when migrating off Detox to Appium. |
| 69 | + |
| 70 | +## Updating a Detox patch (headless) |
| 71 | + |
| 72 | +**Do not** `rsync` the whole `node_modules/detox` tree into a patch folder — that pulls in frameworks and multi‑MB artifacts. |
| 73 | + |
| 74 | +1. Edit the patched file under `tests/node_modules/detox/...` (after `yarn install`), **or** append a correct unified-diff hunk to `.yarn/patches/detox-npm-20.51.0-3e13b6e309.patch`. |
| 75 | +2. Regenerate the patch file without prompts: |
| 76 | + |
| 77 | +```bash |
| 78 | +PATCH_DIR=$(yarn patch detox@npm:20.51.0 2>&1 | sed -n 's/.*folder: //p') |
| 79 | +SRC=tests/node_modules/detox |
| 80 | + |
| 81 | +# Copy ONLY the files this patch touches (see inventory table above) |
| 82 | +/bin/cp -f "$SRC/src/utils/ExclusiveLockfile.js" "$PATCH_DIR/src/utils/ExclusiveLockfile.js" |
| 83 | +/bin/cp -f "$SRC/src/server/handlers/AnonymousConnectionHandler.js" "$PATCH_DIR/src/server/handlers/AnonymousConnectionHandler.js" |
| 84 | +/bin/cp -f "$SRC/src/devices/common/drivers/android/exec/ADB.js" "$PATCH_DIR/src/devices/common/drivers/android/exec/ADB.js" |
| 85 | +/bin/cp -f "$SRC/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkIdlingResource.kt" \ |
| 86 | + "$PATCH_DIR/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/network/NetworkIdlingResource.kt" |
| 87 | +/bin/cp -f "$SRC/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt" \ |
| 88 | + "$PATCH_DIR/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/timers/TimersIdlingResource.kt" |
| 89 | +/bin/cp -f "$SRC/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt" \ |
| 90 | + "$PATCH_DIR/android/detox/src/full/java/com/wix/detox/reactnative/idlingresources/uimodule/fabric/FabricUIManagerIdlingResources.kt" |
| 91 | + |
| 92 | +yarn patch-commit -s "$PATCH_DIR" # non-interactive when using /bin/cp -f, not plain cp |
| 93 | +``` |
| 94 | + |
| 95 | +3. `yarn install` from repo root and confirm the change in `tests/node_modules/detox/...`. |
| 96 | +4. Update this doc and platform pages (`ios.md`, `android.md`) if behaviour or file list changes. |
| 97 | + |
| 98 | +**Detox version bump:** change `tests/package.json`, run `yarn`, re-apply all hunks (or redo the headless flow from a fresh `yarn patch`), run iOS + Android E2E. |
| 99 | + |
| 100 | +## Platform docs |
| 101 | + |
| 102 | +- [iOS](ios.md) — simulator boot, early-ready, Jet WS, lock flake sentinels |
| 103 | +- [Android](android.md) — idling resources, adb reverse teardown |
0 commit comments