@unrulysystems/rn-playwright-driver-runner 0.1.1 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @unrulysystems/rn-playwright-driver-runner
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#30](https://github.com/unrulysystems/rn-playwright-driver/pull/30) [`4bdbe20`](https://github.com/unrulysystems/rn-playwright-driver/commit/4bdbe2073f7b613f6bfb8d5a13db5e0ca000e1c5) Thanks [@alleneubank](https://github.com/alleneubank)! - Upgrade the dogfood example and native module tooling to Expo SDK 56.
8
+
9
+ The driver now sends a React Native inspector-compatible WebSocket Origin when
10
+ attaching to Hermes CDP, which keeps Expo SDK 56 dev-client debugging connected
11
+ on localhost Metro servers. The runner also fast-fails companion readiness when
12
+ captured iOS or Android companion logs contain terminal build, test, or
13
+ instrumentation failure markers instead of waiting for the full probe timeout.
14
+
3
15
  ## 0.1.1
4
16
 
5
17
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -54,6 +54,10 @@ var DEFAULTS = {
54
54
  androidTokenFileName: "rn-driver-touch-token"
55
55
  };
56
56
  var SECRET_PLACEHOLDER = "<token-file>";
57
+ var COMPANION_FAILURE_MARKERS = {
58
+ ios: ["** BUILD FAILED **", "** TEST FAILED **"],
59
+ android: ["INSTRUMENTATION_FAILED", "Process crashed"]
60
+ };
57
61
 
58
62
  // src/plan/env.ts
59
63
  function buildIosDriverEnv(resolved, metro, timeoutMs) {
@@ -273,7 +277,10 @@ function planAndroid(input) {
273
277
  port: resolved.touchPort,
274
278
  tokenFile: resolved.tokenFile,
275
279
  timeoutMs: resolved.companionReadyTimeoutMs
276
- }
280
+ },
281
+ // Abort early if `am instrument` reports the companion failed to start (crash / missing
282
+ // androidTest target) instead of waiting out the readiness budget.
283
+ failureMarkers: COMPANION_FAILURE_MARKERS.android
277
284
  }
278
285
  });
279
286
  push(launchStep("android.launch-2", android, launch2Command));
@@ -604,7 +611,10 @@ function planIos(input) {
604
611
  port: resolved.touchPort,
605
612
  tokenFile: resolved.tokenFile,
606
613
  timeoutMs: resolved.companionReadyTimeoutMs
607
- }
614
+ },
615
+ // Abort early if `xcodebuild test` reports a build/test failure (it lingers "alive" after, so
616
+ // the 300s readiness budget would otherwise be burnt waiting for a companion that cannot bind).
617
+ failureMarkers: COMPANION_FAILURE_MARKERS.ios
608
618
  }
609
619
  });
610
620
  if (isDevClient) {
@@ -860,8 +870,10 @@ function renderAction(action) {
860
870
  return `write ${action.path}${action.mode ? ` (mode ${action.mode.toString(8)})` : ""}`;
861
871
  case "free-port":
862
872
  return `free-port ${action.port}`;
863
- case "probe":
864
- return `probe ${renderProbe(action.probe)}`;
873
+ case "probe": {
874
+ const fastFail = action.failureMarkers?.length ? ` [fast-fail on: ${action.failureMarkers.join(", ")}]` : "";
875
+ return `probe ${renderProbe(action.probe)}${fastFail}`;
876
+ }
865
877
  default: {
866
878
  const _exhaustive = action;
867
879
  throw new Error(`unhandled action: ${JSON.stringify(_exhaustive)}`);
@@ -907,6 +919,25 @@ function renderProbe(probe) {
907
919
  }
908
920
  }
909
921
 
922
+ // src/runner/probe-failure.ts
923
+ var ProbeFailure = class extends Error {
924
+ marker;
925
+ constructor(marker, detail) {
926
+ super(
927
+ `companion process reported a terminal failure ("${marker}") \u2014 aborting the readiness wait. The build/test failed; it will not become ready.${detail ? `
928
+ ${detail}` : ""}`
929
+ );
930
+ this.name = "ProbeFailure";
931
+ this.marker = marker;
932
+ }
933
+ };
934
+ function findFailureMarker(log, markers) {
935
+ for (const marker of markers) {
936
+ if (log.includes(marker)) return marker;
937
+ }
938
+ return null;
939
+ }
940
+
910
941
  // src/runner/execute.ts
911
942
  var StageError = class extends Error {
912
943
  stage;
@@ -974,22 +1005,30 @@ async function runStep(step, runner, opts, processes, isAlive) {
974
1005
  case "probe": {
975
1006
  const key = processKeyForProbe(action.probe);
976
1007
  const aliveFn = key === null ? () => true : () => isAlive(key);
977
- let ready = await runner.probe(action.probe, aliveFn);
978
- let remaining = action.retry?.max ?? 0;
979
- while (!ready && remaining > 0) {
980
- remaining -= 1;
981
- if (action.retry) {
982
- runner.log(`retry ${step.id}: re-running launch (${remaining} attempt(s) left)`);
983
- await runner.exec(action.retry.command);
1008
+ const watch = key !== null && action.failureMarkers && action.failureMarkers.length > 0 ? { logPath: logPathFor(opts.logDir, key), failureMarkers: action.failureMarkers } : void 0;
1009
+ try {
1010
+ let ready = await runner.probe(action.probe, aliveFn, watch);
1011
+ let remaining = action.retry?.max ?? 0;
1012
+ while (!ready && remaining > 0) {
1013
+ remaining -= 1;
1014
+ if (action.retry) {
1015
+ runner.log(`retry ${step.id}: re-running launch (${remaining} attempt(s) left)`);
1016
+ await runner.exec(action.retry.command);
1017
+ }
1018
+ ready = await runner.probe(action.probe, aliveFn, watch);
984
1019
  }
985
- ready = await runner.probe(action.probe, aliveFn);
986
- }
987
- if (!ready) {
988
- throw new StageError(
989
- step.stage,
990
- step.id,
991
- `readiness timed out after ${action.probe.timeoutMs}ms`
992
- );
1020
+ if (!ready) {
1021
+ throw new StageError(
1022
+ step.stage,
1023
+ step.id,
1024
+ `readiness timed out after ${action.probe.timeoutMs}ms`
1025
+ );
1026
+ }
1027
+ } catch (error) {
1028
+ if (error instanceof ProbeFailure) {
1029
+ throw new StageError(step.stage, step.id, error.message);
1030
+ }
1031
+ throw error;
993
1032
  }
994
1033
  return;
995
1034
  }
@@ -1128,10 +1167,15 @@ var NodeProcessRunner = class {
1128
1167
  }
1129
1168
  if (pids.length > 0) await delay(1e3);
1130
1169
  }
1131
- probe(probe, isAlive) {
1170
+ probe(probe, isAlive, watch) {
1132
1171
  const deadline = Date.now() + probe.timeoutMs;
1133
1172
  const attempt = async () => {
1134
1173
  for (; ; ) {
1174
+ if (watch) {
1175
+ const log = await readWatchedLog(watch.logPath);
1176
+ const marker = findFailureMarker(log, watch.failureMarkers);
1177
+ if (marker) throw new ProbeFailure(marker, lastLines(log));
1178
+ }
1135
1179
  if (!isAlive()) return false;
1136
1180
  if (await probeOnce(probe)) return true;
1137
1181
  if (Date.now() >= deadline) return false;
@@ -1176,6 +1220,19 @@ function delay(ms) {
1176
1220
  setTimeout(resolve, ms);
1177
1221
  });
1178
1222
  }
1223
+ async function readWatchedLog(path4) {
1224
+ try {
1225
+ return await promises.readFile(path4, "utf8");
1226
+ } catch (error) {
1227
+ throw new Error(
1228
+ `cannot read companion log for fast-fail marker detection (${path4}): ${String(error)}`,
1229
+ { cause: error }
1230
+ );
1231
+ }
1232
+ }
1233
+ function lastLines(text, n) {
1234
+ return text.split("\n").filter((line) => line.trim().length > 0).slice(-12).join("\n");
1235
+ }
1179
1236
  async function probeOnce(probe) {
1180
1237
  switch (probe.kind) {
1181
1238
  case "metro-status":