Skip to content

Commit d3c4110

Browse files
committed
test(e2e): fix mocha-remote parse patch and macOS Metro bundle startup
1 parent 11a2fa2 commit d3c4110

8 files changed

Lines changed: 170 additions & 9 deletions

File tree

.github/workflows/tests_e2e_other.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ jobs:
264264
sleep 2
265265
done
266266
echo "Packager is online! Preparing bundle..."
267-
curl --output /dev/null --silent --head --fail "http://localhost:8081/index.bundle?platform=macos&dev=true&minify=false&inlineSourceMap=true"
267+
curl --output /dev/null --silent --head --fail "http://127.0.0.1:8081/index.bundle?platform=macos&dev=true&lazy=true&minify=false&inlineSourceMap=true&modulesOnly=false&runModule=true&app=org.reactjs.native.io-invertase-testing"
268268
echo "...javascript bundle ready"
269269
270270
- name: Start Screen Recording and System Logging

.yarn/patches/mocha-remote-server-npm-1.13.2-619a29d2e3.patch

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,39 @@ index ad9debe2086ab9b96e97a69aec966da8114ad102..51cc18658e11d5aec5d5a76c4eb5f965
191191
}
192192
}
193193
/**
194+
diff --git a/dist/serialization.js b/dist/serialization.js
195+
index b2dd290111197907d125e3151a4a965e9fe0528d..7a29ad98febf3ec7dbffe02b9d555fa40340e325 100644
196+
--- a/dist/serialization.js
197+
+++ b/dist/serialization.js
198+
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
199+
return (mod && mod.__esModule) ? mod : { "default": mod };
200+
};
201+
Object.defineProperty(exports, "__esModule", { value: true });
202+
-exports.deserialize = exports.createReviver = void 0;
203+
+exports.tryDeserialize = exports.deserialize = exports.createReviver = void 0;
204+
const flatted_1 = __importDefault(require("flatted"));
205+
const debug_1 = require("./debug");
206+
const debug = (0, debug_1.extend)("serialization");
207+
@@ -45,6 +45,22 @@ function createReviver() {
208+
};
209+
}
210+
exports.createReviver = createReviver;
211+
+function tryDeserialize(text, reviver = createReviver()) {
212+
+ if (!text) {
213+
+ return { ok: false, incomplete: true, error: new Error("empty buffer") };
214+
+ }
215+
+ try {
216+
+ const value = flatted_1.default.parse(text, reviver);
217+
+ return { ok: true, value };
218+
+ }
219+
+ catch (error) {
220+
+ const err = error instanceof Error ? error : new Error(String(error));
221+
+ const message = err.message || "";
222+
+ const incomplete = /unexpected end of (json )?input|end of data/i.test(message);
223+
+ return { ok: false, incomplete, error: err };
224+
+ }
225+
+}
226+
+exports.tryDeserialize = tryDeserialize;
227+
function deserialize(text, reviver = createReviver()) {
228+
debug("Deserializing %s", text);
229+
return flatted_1.default.parse(text, reviver);

okf-bundle/ci-workflows/android.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Observed on Android CI under load (e.g. [run 28044822049](https://github.com/inv
5959

6060
| Change | Location |
6161
|--------|----------|
62-
| Inbound parse buffer + `tryDeserialize` / `parse_skip` logging | mocha-remote-server patch`Server.js` |
62+
| Inbound parse buffer + `tryDeserialize` in `serialization.js` / `parse_skip` logging | mocha-remote-server patch |
6363
| Outbound queue flushed on reconnect | mocha-remote-client patch |
6464
| `JET_PROTOCOL_ERROR_RE` → retryable Jet session (attempt 2) | `tests/e2e/firebase.test.js` |
6565
| Cold-boot ready wait + post-boot settle before Jet attempt 1 | `firebase.test.js` (`waitForAndroidEmulatorReady`, `RNFB_ANDROID_BOOT_SETTLE_MS`) |

okf-bundle/ci-workflows/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Knowledge for GitHub Actions workflows in this repository: how jobs are structur
66

77
* [iOS](ios.md) — simulator boot reliability, logging, and troubleshooting
88
* [Android](android.md) — idling resources, adb teardown, native coverage pull
9-
* [Other](other.md) — macOS Detox (non-iOS), Windows, and shared workflow concerns — TBD
9+
* [Other](other.md) — macOS Jet e2e (Metro bundle startup), Windows, and shared workflow concerns
1010

1111
## Shared E2E dependencies
1212

okf-bundle/ci-workflows/other.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,66 @@
11
# Other CI workflows
22

3-
TBD — macOS Detox (`tests_e2e_other.yml`), Windows, documentation workflows, and shared actions (caches, Codecov, etc.).
3+
## macOS Jet e2e (`tests_e2e_other.yml`)
4+
5+
macOS e2e runs **Jet directly** (no Detox): `yarn tests:macos:test-cover`. The test app is a react-native-macos binary launched via `open` in `tests/.jetrc.js`.
6+
7+
### Pipeline
8+
9+
1. Build macOS app (`yarn tests:macos:build`, `SKIP_BUNDLING=1`)
10+
2. Start Firestore emulator
11+
3. Start Metro (`yarn tests:packager:jet-ci`)
12+
4. **Pre-fetch JS bundle** (workflow step) — must match the URL the app requests
13+
5. `yarn tests:macos:test-cover` — Jet `before` hook also prefetches, then `open` app
14+
15+
### CI failure: bundle load hang / `Could not connect to development server`
16+
17+
**Symptom** — Jet logs `[💻] macOS app started` but never `[🟩] Jet client connected`. System log / `syslog` artifact shows:
18+
19+
```
20+
HTTP load failed, 384/63910994 bytes (error code: -1017)
21+
Could not connect to development server.
22+
URL: http://localhost:8081/index.bundle?platform=macos&...&inlineSourceMap=false...
23+
```
24+
25+
**Cause**
26+
27+
1. **Startup race**`.jetrc.js` opened the app before Metro finished the first ~64MB bundle. The native HTTP client received a truncated response (`-1017`) and JS never loaded, so mocha-remote never connected.
28+
2. **Prefetch URL mismatch** — CI warmed `inlineSourceMap=true` without `lazy` / `app=` query params, while the default `AppDelegate` requested `localhost` with `inlineSourceMap=false` — a cache miss and a second full bundle build at app launch.
29+
3. **`localhost` vs `127.0.0.1`** — same class of issue as [iOS AppDelegate](ios.md#operational-notes); macOS can connect via `::1` while Metro is still streaming.
30+
31+
**Mitigations in this repo**
32+
33+
| Change | Location |
34+
|--------|----------|
35+
| `127.0.0.1` + explicit `inlineSourceMap:YES` bundle URL (mirrors iOS) | `tests/macos/io.invertase.testing-macOS/AppDelegate.mm` |
36+
| `waitForMetroMacosBundle()` before `open` app | `tests/.jetrc.js``macos.before` |
37+
| CI prefetch uses **exact** bundle query string (127.0.0.1, `lazy`, `inlineSourceMap=true`, `app=`) | `.github/workflows/tests_e2e_other.yml` |
38+
39+
**Diagnosing**
40+
41+
```bash
42+
rg 'Could not connect to development server|HTTP load failed|Jet client connected|macOS Metro bundle prefetched' detox-step.log syslog.log
43+
log show --predicate 'process == "io.invertase.testing"' --last 10m --style compact | rg 'development server|HTTP load failed'
44+
```
45+
46+
### CI failure: `tryDeserialize is not a function` (mocha-remote-server patch)
47+
48+
**Symptom** — immediate crash on first WS message after client connects:
49+
50+
```
51+
TypeError: (0 , serialization_1.tryDeserialize) is not a function
52+
at Server.handleMessage (.../mocha-remote-server/dist/Server.js:149)
53+
```
54+
55+
**Cause**`Server.js` patch called `tryDeserialize` but the helper was never added to `dist/serialization.js`.
56+
57+
**Fix** — export `tryDeserialize` from `serialization.js` in `.yarn/patches/mocha-remote-server-npm-1.13.2-*.patch`; run `yarn patch-commit` + `yarn install` so `yarn.lock` hash updates.
58+
59+
### Related
60+
61+
- [iOS CI](ios.md) — simulator + Detox; shared Jet / mocha-remote patches
62+
- [Coverage design](../testing/coverage-design.md) — macOS uploads `e2e-ts-macos` only (no native gate)
63+
64+
## Windows / shared
65+
66+
TBD — Windows workflows and shared actions (caches, Codecov, etc.).

tests/.jetrc.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11
const { execSync, spawn } = require('child_process');
2+
const { promisify } = require('util');
3+
const execFile = promisify(require('child_process').execFile);
24

35
let macOsRetries = 0;
46

7+
const MACOS_BUNDLE_QUERY =
8+
'platform=macos&dev=true&lazy=true&minify=false&inlineSourceMap=true&modulesOnly=false&runModule=true&app=org.reactjs.native.io-invertase-testing';
9+
10+
async function waitForMetroMacosBundle(metroPort = 8081, timeoutMs = 600000) {
11+
const host = '127.0.0.1';
12+
const statusUrl = `http://${host}:${metroPort}/status`;
13+
const bundleUrl = `http://${host}:${metroPort}/index.bundle?${MACOS_BUNDLE_QUERY}`;
14+
const started = Date.now();
15+
16+
while (Date.now() - started < timeoutMs) {
17+
try {
18+
const { stdout } = await execFile('curl', ['-sf', statusUrl]);
19+
if (stdout.includes('packager-status:running')) {
20+
break;
21+
}
22+
} catch (_e) {
23+
// Metro still starting.
24+
}
25+
await new Promise(resolve => setTimeout(resolve, 2000));
26+
}
27+
28+
const remainingSec = Math.max(60, Math.ceil((timeoutMs - (Date.now() - started)) / 1000));
29+
await execFile('curl', ['-sf', '--max-time', String(remainingSec), '-o', '/dev/null', bundleUrl]);
30+
console.warn(`[rnfb-e2e] macOS Metro bundle prefetched from ${bundleUrl}`);
31+
}
32+
533
module.exports = {
634
config: {
735
slow: 3000,
@@ -36,6 +64,7 @@ module.exports = {
3664
} catch (_e) {
3765
// noop
3866
}
67+
await waitForMetroMacosBundle(config.metroPort ?? 8081);
3968
const macApp = spawn(
4069
'open',
4170
['./macos/build/Build/Products/Debug/io.invertase.testing.app'],

tests/macos/io.invertase.testing-macOS/AppDelegate.mm

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,45 @@
11
#import "AppDelegate.h"
22

33
#import <React/RCTBundleURLProvider.h>
4+
#import <React/RCTConstants.h>
5+
6+
static NSString *const RNFBTestingMetroHost = @"127.0.0.1";
7+
8+
static NSString *RNFBTestingMetroHostPort(void)
9+
{
10+
return [NSString stringWithFormat:@"%@:%lu", RNFBTestingMetroHost, (unsigned long)RCT_METRO_PORT];
11+
}
12+
13+
#if DEBUG
14+
static void RNFBTestingConfigureMetroHost(void)
15+
{
16+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
17+
[settings setJsLocation:RNFBTestingMetroHostPort()];
18+
// NYC/babel-istanbul remap needs inline source maps in the dev bundle.
19+
[settings setInlineSourceMap:YES];
20+
}
21+
22+
static NSURL *RNFBTestingDebugBundleURL(void)
23+
{
24+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
25+
return [RCTBundleURLProvider jsBundleURLForBundleRoot:@"index"
26+
packagerHost:RNFBTestingMetroHostPort()
27+
packagerScheme:settings.packagerScheme ?: @"http"
28+
enableDev:settings.enableDev
29+
enableMinification:settings.enableMinification
30+
inlineSourceMap:YES
31+
modulesOnly:NO
32+
runModule:YES];
33+
}
34+
#endif
435

536
@implementation AppDelegate
637

738
- (void)applicationDidFinishLaunching:(NSNotification *)notification
839
{
40+
#if DEBUG
41+
RNFBTestingConfigureMetroHost();
42+
#endif
943
self.moduleName = @"testing";
1044
// You can add your custom initial props in the dictionary below.
1145
// They will be passed down to the ViewController used by React Native.
@@ -17,17 +51,16 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification
1751
- (NSURL *)bundleURL
1852
{
1953
#if DEBUG
20-
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
54+
return RNFBTestingDebugBundleURL();
2155
#else
2256
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
2357
#endif
2458
}
2559

2660
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
2761
{
28-
2962
#if DEBUG
30-
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
63+
return RNFBTestingDebugBundleURL();
3164
#else
3265
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
3366
#endif

yarn.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19562,13 +19562,13 @@ __metadata:
1956219562

1956319563
"mocha-remote-server@patch:mocha-remote-server@npm%3A1.13.2#~/.yarn/patches/mocha-remote-server-npm-1.13.2-619a29d2e3.patch":
1956419564
version: 1.13.2
19565-
resolution: "mocha-remote-server@patch:mocha-remote-server@npm%3A1.13.2#~/.yarn/patches/mocha-remote-server-npm-1.13.2-619a29d2e3.patch::version=1.13.2&hash=bab3ca"
19565+
resolution: "mocha-remote-server@patch:mocha-remote-server@npm%3A1.13.2#~/.yarn/patches/mocha-remote-server-npm-1.13.2-619a29d2e3.patch::version=1.13.2&hash=04ee13"
1956619566
dependencies:
1956719567
debug: "npm:^4.3.4"
1956819568
flatted: "npm:^3.3.1"
1956919569
mocha-remote-common: "npm:1.13.2"
1957019570
ws: "npm:^8.17.1"
19571-
checksum: 10/12dd1d674164de70bd3ad9b3ac545cb6de1a6e8aaec1bfaf62b3e699948667f38dd5d5fbf85382e412298dacc70dfc3ad9e80dd3aa6b8b89a442ea39fc6ae5d4
19571+
checksum: 10/cd9f2a1d15889e1fb7f3c63ae196ef8eea08ad9f0d3abc8ee7ff58f80edbddb7379c5fb322e710d5e6ebbce943157d683d806f1e207a7d7a2326852aa8666bfd
1957219572
languageName: node
1957319573
linkType: hard
1957419574

0 commit comments

Comments
 (0)