@react-native-harness/jest 1.4.0 → 1.5.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.
Files changed (133) hide show
  1. package/dist/execute-run.d.ts.map +1 -1
  2. package/dist/execute-run.js +2 -1
  3. package/dist/harness-session.d.ts.map +1 -1
  4. package/dist/harness-session.js +74 -30
  5. package/dist/resource-lock.d.ts.map +1 -1
  6. package/dist/resource-lock.js +12 -2
  7. package/dist/toJestStatus.d.ts +4 -0
  8. package/dist/toJestStatus.d.ts.map +1 -0
  9. package/dist/toJestStatus.js +2 -0
  10. package/dist/toTestResult.d.ts +3 -2
  11. package/dist/toTestResult.d.ts.map +1 -1
  12. package/dist/toTestResult.js +2 -1
  13. package/package.json +8 -8
  14. package/src/execute-run.ts +2 -1
  15. package/src/harness-session.ts +86 -27
  16. package/src/resource-lock.ts +15 -2
  17. package/src/toJestStatus.ts +6 -0
  18. package/src/toTestResult.ts +5 -3
  19. package/dist/__tests__/bridge.test.d.ts +0 -2
  20. package/dist/__tests__/bridge.test.d.ts.map +0 -1
  21. package/dist/__tests__/bridge.test.js +0 -167
  22. package/dist/__tests__/client-log-handler.test.d.ts +0 -2
  23. package/dist/__tests__/client-log-handler.test.d.ts.map +0 -1
  24. package/dist/__tests__/client-log-handler.test.js +0 -306
  25. package/dist/__tests__/crash-monitor.test.d.ts +0 -2
  26. package/dist/__tests__/crash-monitor.test.d.ts.map +0 -1
  27. package/dist/__tests__/crash-monitor.test.js +0 -230
  28. package/dist/__tests__/crash-supervisor.test.d.ts +0 -2
  29. package/dist/__tests__/crash-supervisor.test.d.ts.map +0 -1
  30. package/dist/__tests__/crash-supervisor.test.js +0 -151
  31. package/dist/__tests__/environment-reset.test.d.ts +0 -2
  32. package/dist/__tests__/environment-reset.test.d.ts.map +0 -1
  33. package/dist/__tests__/environment-reset.test.js +0 -238
  34. package/dist/__tests__/errors.test.d.ts +0 -2
  35. package/dist/__tests__/errors.test.d.ts.map +0 -1
  36. package/dist/__tests__/errors.test.js +0 -98
  37. package/dist/__tests__/execute-run.test.d.ts +0 -2
  38. package/dist/__tests__/execute-run.test.d.ts.map +0 -1
  39. package/dist/__tests__/execute-run.test.js +0 -639
  40. package/dist/__tests__/fixtures/mock-platform-runner.d.ts +0 -7
  41. package/dist/__tests__/fixtures/mock-platform-runner.d.ts.map +0 -1
  42. package/dist/__tests__/fixtures/mock-platform-runner.js +0 -6
  43. package/dist/__tests__/format-harness-error.test.d.ts +0 -2
  44. package/dist/__tests__/format-harness-error.test.d.ts.map +0 -1
  45. package/dist/__tests__/format-harness-error.test.js +0 -27
  46. package/dist/__tests__/harness-cache.test.d.ts +0 -2
  47. package/dist/__tests__/harness-cache.test.d.ts.map +0 -1
  48. package/dist/__tests__/harness-cache.test.js +0 -72
  49. package/dist/__tests__/harness-session-signal.test.d.ts +0 -2
  50. package/dist/__tests__/harness-session-signal.test.d.ts.map +0 -1
  51. package/dist/__tests__/harness-session-signal.test.js +0 -298
  52. package/dist/__tests__/harness-session.test.d.ts +0 -2
  53. package/dist/__tests__/harness-session.test.d.ts.map +0 -1
  54. package/dist/__tests__/harness-session.test.js +0 -233
  55. package/dist/__tests__/harness.test.d.ts +0 -2
  56. package/dist/__tests__/harness.test.d.ts.map +0 -1
  57. package/dist/__tests__/harness.test.js +0 -636
  58. package/dist/__tests__/hook-queue.test.d.ts +0 -2
  59. package/dist/__tests__/hook-queue.test.d.ts.map +0 -1
  60. package/dist/__tests__/hook-queue.test.js +0 -56
  61. package/dist/__tests__/index.test.d.ts +0 -2
  62. package/dist/__tests__/index.test.d.ts.map +0 -1
  63. package/dist/__tests__/index.test.js +0 -150
  64. package/dist/__tests__/metro-port.test.d.ts +0 -2
  65. package/dist/__tests__/metro-port.test.d.ts.map +0 -1
  66. package/dist/__tests__/metro-port.test.js +0 -54
  67. package/dist/__tests__/resource-lock.test.d.ts +0 -2
  68. package/dist/__tests__/resource-lock.test.d.ts.map +0 -1
  69. package/dist/__tests__/resource-lock.test.js +0 -138
  70. package/dist/__tests__/run.test.d.ts +0 -2
  71. package/dist/__tests__/run.test.d.ts.map +0 -1
  72. package/dist/__tests__/run.test.js +0 -48
  73. package/dist/__tests__/test-file-platform-filter.test.d.ts +0 -2
  74. package/dist/__tests__/test-file-platform-filter.test.d.ts.map +0 -1
  75. package/dist/__tests__/test-file-platform-filter.test.js +0 -47
  76. package/dist/cli-args.test.d.ts +0 -2
  77. package/dist/cli-args.test.d.ts.map +0 -1
  78. package/dist/cli-args.test.js +0 -1
  79. package/dist/crash-supervisor.d.ts +0 -21
  80. package/dist/crash-supervisor.d.ts.map +0 -1
  81. package/dist/crash-supervisor.js +0 -210
  82. package/dist/diagnostics/__tests__/derivers.test.d.ts +0 -2
  83. package/dist/diagnostics/__tests__/derivers.test.d.ts.map +0 -1
  84. package/dist/diagnostics/__tests__/derivers.test.js +0 -248
  85. package/dist/diagnostics/__tests__/report.test.d.ts +0 -2
  86. package/dist/diagnostics/__tests__/report.test.d.ts.map +0 -1
  87. package/dist/diagnostics/__tests__/report.test.js +0 -97
  88. package/dist/event-mapper.d.ts +0 -16
  89. package/dist/event-mapper.d.ts.map +0 -1
  90. package/dist/event-mapper.js +0 -120
  91. package/dist/global-setup.d.ts +0 -3
  92. package/dist/global-setup.d.ts.map +0 -1
  93. package/dist/global-setup.js +0 -60
  94. package/dist/global-teardown.d.ts +0 -3
  95. package/dist/global-teardown.d.ts.map +0 -1
  96. package/dist/global-teardown.js +0 -11
  97. package/dist/harness-reporter.d.ts +0 -8
  98. package/dist/harness-reporter.d.ts.map +0 -1
  99. package/dist/harness-reporter.js +0 -12
  100. package/dist/harness.d.ts +0 -47
  101. package/dist/harness.d.ts.map +0 -1
  102. package/dist/harness.js +0 -619
  103. package/dist/jest-options.d.ts +0 -2
  104. package/dist/jest-options.d.ts.map +0 -1
  105. package/dist/jest-options.js +0 -1
  106. package/dist/setup.d.ts +0 -3
  107. package/dist/setup.d.ts.map +0 -1
  108. package/dist/setup.js +0 -80
  109. package/dist/teardown.d.ts +0 -3
  110. package/dist/teardown.d.ts.map +0 -1
  111. package/dist/teardown.js +0 -10
  112. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  113. package/dist/types.d.ts +0 -17
  114. package/dist/types.d.ts.map +0 -1
  115. package/dist/types.js +0 -1
  116. package/src/__tests__/bridge.test.ts +0 -224
  117. package/src/__tests__/client-log-handler.test.ts +0 -364
  118. package/src/__tests__/crash-monitor.test.ts +0 -323
  119. package/src/__tests__/environment-reset.test.ts +0 -308
  120. package/src/__tests__/errors.test.ts +0 -157
  121. package/src/__tests__/execute-run.test.ts +0 -849
  122. package/src/__tests__/format-harness-error.test.ts +0 -32
  123. package/src/__tests__/harness-cache.test.ts +0 -91
  124. package/src/__tests__/harness-session.test.ts +0 -303
  125. package/src/__tests__/hook-queue.test.ts +0 -77
  126. package/src/__tests__/index.test.ts +0 -221
  127. package/src/__tests__/metro-port.test.ts +0 -63
  128. package/src/__tests__/resource-lock.test.ts +0 -179
  129. package/src/__tests__/run.test.ts +0 -74
  130. package/src/__tests__/test-file-platform-filter.test.ts +0 -83
  131. package/src/diagnostics/__tests__/derivers.test.ts +0 -300
  132. package/src/diagnostics/__tests__/report.test.ts +0 -128
  133. package/tsconfig.tsbuildinfo +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"execute-run.d.ts","sourceRoot":"","sources":["../src/execute-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,WAAW,EACX,MAAM,EACN,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AA6BjF,KAAK,aAAa,GAAG,CAAC,IAAI,SAAS,MAAM,UAAU,EACjD,SAAS,EAAE,IAAI,EACf,GAAG,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;AA+FnB,eAAO,MAAM,UAAU,GACrB,SAAS,cAAc,EACvB,OAAO,KAAK,CAAC,IAAI,CAAC,EAClB,SAAS,WAAW,EACpB,WAAW,aAAa,EACxB,cAAc,MAAM,CAAC,YAAY,KAChC,OAAO,CAAC,IAAI,CAsOd,CAAC"}
1
+ {"version":3,"file":"execute-run.d.ts","sourceRoot":"","sources":["../src/execute-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,WAAW,EACX,MAAM,EACN,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AA8BjF,KAAK,aAAa,GAAG,CAAC,IAAI,SAAS,MAAM,UAAU,EACjD,SAAS,EAAE,IAAI,EACf,GAAG,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;AA+FnB,eAAO,MAAM,UAAU,GACrB,SAAS,cAAc,EACvB,OAAO,KAAK,CAAC,IAAI,CAAC,EAClB,SAAS,WAAW,EACpB,WAAW,aAAa,EACxB,cAAc,MAAM,CAAC,YAAY,KAChC,OAAO,CAAC,IAAI,CAsOd,CAAC"}
@@ -8,6 +8,7 @@ import { createPlatformSkippedTestResult, shouldRunHarnessTestFile, } from './te
8
8
  import { formatHarnessErrorMessage } from './format-harness-error.js';
9
9
  import { logger } from '@react-native-harness/tools';
10
10
  import { printSummary, writeTraceFile } from './diagnostics/index.js';
11
+ import { toJestStatus } from './toJestStatus.js';
11
12
  const diagnosticsLogger = logger.child('diagnostics');
12
13
  const createRunSummary = () => ({ passed: 0, failed: 0, skipped: 0, todo: 0 });
13
14
  const applyJestResultToSummary = (summary, result) => {
@@ -59,7 +60,7 @@ const emitHarnessTestFinished = async (emitEvent, event) => {
59
60
  location,
60
61
  numPassingAsserts: event.status === 'passed' ? 1 : 0,
61
62
  startedAt: event.startedAt,
62
- status: event.status,
63
+ status: toJestStatus(event.status),
63
64
  title: event.name,
64
65
  };
65
66
  await emitEvent('test-case-result', event.file, testCaseResult);
@@ -1 +1 @@
1
- {"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { HARNESS_BRIDGE_PATH, } from '@react-native-harness/bridge';
3
3
  import { createHarnessCache } from '@react-native-harness/cache';
4
4
  import { getMetroInstance, waitForMetroBackedAppReady, } from '@react-native-harness/bundler-metro';
5
5
  import { createHarnessPluginManager, } from '@react-native-harness/plugins';
6
- import { createCrashArtifactWriter, createDiagnostics, logger, getTimeoutSignal, ensureSignalsDeliverable, waitForAbort, } from '@react-native-harness/tools';
6
+ import { createCrashArtifactWriter, createDiagnostics, delay, logger, getTimeoutSignal, ensureSignalsDeliverable, waitForAbort, } from '@react-native-harness/tools';
7
7
  import { getConfig, isDiagnosticsEnabled, resolveMetroCacheEnabled, ConfigSchema, } from '@react-native-harness/config';
8
8
  import { preRunMessage } from 'jest-util';
9
9
  import path from 'node:path';
@@ -24,6 +24,19 @@ const defaultResourceLockManager = createResourceLockManager();
24
24
  const ignorePromiseRejection = () => undefined;
25
25
  const isBridgeDisconnectError = (error) => error instanceof Error && error.message === 'App bridge disconnected';
26
26
  const TEST_RUN_BRIDGE_STABILITY_WAIT_MS = 500;
27
+ const DISPOSE_HOOK_TIMEOUT_MS = 5_000;
28
+ const waitForBoundedSettlement = async (promise, timeoutMs) => {
29
+ const timeout = delay(timeoutMs);
30
+ try {
31
+ return await Promise.race([
32
+ promise.then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason })),
33
+ timeout.promise.then(() => null),
34
+ ]);
35
+ }
36
+ finally {
37
+ timeout.cancel();
38
+ }
39
+ };
27
40
  export const waitForBridgeDisconnectOrTimeout = async ({ bridge, connection, timeoutMs, }) => {
28
41
  if (bridge.connection !== connection) {
29
42
  return true;
@@ -260,7 +273,11 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
260
273
  // child the runner threads it into.
261
274
  ensureSignalsDeliverable();
262
275
  const sessionController = new AbortController();
263
- const onEarlySignal = () => sessionController.abort();
276
+ let receivedEarlySignal = false;
277
+ const onEarlySignal = () => {
278
+ receivedEarlySignal = true;
279
+ sessionController.abort();
280
+ };
264
281
  process.once('SIGTERM', onEarlySignal);
265
282
  process.once('SIGINT', onEarlySignal);
266
283
  const resourceLockKey = await (platform.getResourceLockKey?.() ?? getDefaultResourceLockKey(platform));
@@ -341,6 +358,11 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
341
358
  sessionLogger.debug('bridge initialized on Metro websocket path %s', HARNESS_BRIDGE_PATH);
342
359
  let metroInstance;
343
360
  let platformInstance;
361
+ const initialized = {
362
+ metro: null,
363
+ platform: null,
364
+ };
365
+ let initializationCancelled = false;
344
366
  // Noop until the Metro instance resolves and the deriver is attached.
345
367
  let disposeMetroDiagnostics = () => undefined;
346
368
  try {
@@ -348,11 +370,18 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
348
370
  diagnostics.measure('metro.init', () => getMetroInstance({
349
371
  projectRoot,
350
372
  harnessConfig: runtimeConfig,
373
+ metroConfigEnhancer: platform.metroConfigEnhancer,
374
+ platformId: platform.platformId,
375
+ platformConfig: platform.config,
351
376
  websocketEndpoints: {
352
377
  [HARNESS_BRIDGE_PATH]: bridge.ws,
353
378
  },
354
379
  watchMode: globalConfig.watch || globalConfig.watchAll,
355
380
  }, sessionController.signal).then((instance) => {
381
+ if (initializationCancelled) {
382
+ return instance.dispose().then(() => instance);
383
+ }
384
+ initialized.metro = instance;
356
385
  sessionLogger.debug('Metro initialized');
357
386
  // Attach the Metro diagnostics deriver before the eager prewarm
358
387
  // fires so its bundle build/request events land in the trace.
@@ -384,6 +413,10 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
384
413
  crashArtifactWriter,
385
414
  diagnostics,
386
415
  })).then((instance) => {
416
+ if (initializationCancelled) {
417
+ return instance.dispose().then(() => instance);
418
+ }
419
+ initialized.platform = instance;
387
420
  sessionLogger.debug('platform runner initialized');
388
421
  return instance;
389
422
  });
@@ -392,10 +425,16 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
392
425
  ]);
393
426
  }
394
427
  catch (error) {
395
- // Only bridge and the Metro diagnostics listener need cleanup here;
396
- // leases are released by the outer catch.
428
+ // Roll back every resource whose initialization won a concurrent race.
429
+ // Leases are released by the outer catch.
430
+ initializationCancelled = true;
431
+ sessionController.abort();
397
432
  disposeMetroDiagnostics();
398
- await bridge.dispose();
433
+ await Promise.allSettled([
434
+ bridge.dispose(),
435
+ initialized.metro?.dispose(),
436
+ initialized.platform?.dispose(),
437
+ ]);
399
438
  throw error;
400
439
  }
401
440
  const appLaunchOptions = platform.config.appLaunchOptions;
@@ -483,7 +522,10 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
483
522
  sessionLogger.debug('disposing session (reason=%s)', reason);
484
523
  const disposeSpan = diagnostics.start('dispose.total', { reason });
485
524
  let hookError;
486
- try {
525
+ // Abort only cancels finite/in-flight work. Ownership remains explicit
526
+ // below, so this cannot race a platform or app-session disposal path.
527
+ sessionController.abort();
528
+ const hookWork = (async () => {
487
529
  await hooks.drain();
488
530
  await pluginManager.callHook('harness:after-run', {
489
531
  runId: currentRun?.runId,
@@ -501,9 +543,13 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
501
543
  error: currentRun?.error,
502
544
  });
503
545
  await hooks.drain();
546
+ })();
547
+ const hookResult = await waitForBoundedSettlement(hookWork, DISPOSE_HOOK_TIMEOUT_MS);
548
+ if (hookResult === null) {
549
+ hookError = new Error(`Timed out after ${DISPOSE_HOOK_TIMEOUT_MS}ms while running disposal hooks.`);
504
550
  }
505
- catch (error) {
506
- hookError = error;
551
+ else if (hookResult.status === 'rejected') {
552
+ hookError = hookResult.reason;
507
553
  }
508
554
  if (runtimeConfig.forwardClientLogs) {
509
555
  metroInstance.events.removeListener(clientLogListener);
@@ -532,28 +578,23 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
532
578
  sessionLogger.warn('failed to collect native coverage: %s', error);
533
579
  }
534
580
  }
535
- let cleanupError;
536
- try {
537
- // The app session (and its logcat/device log stream) must be fully
538
- // torn down before the platform is disposed: platform disposal can
539
- // shut down the device/emulator, and doing that concurrently with
540
- // the app session's own cleanup races the device's log stream
541
- // against the device disappearing out from under it.
542
- await Promise.all([crashMonitor.dispose(), disposeCurrentAppSession()]);
543
- await Promise.all([
544
- bridge.dispose(),
545
- platformInstance.dispose(),
546
- metroInstance.dispose(),
547
- metroPortLease?.release(),
548
- ]);
549
- }
550
- catch (error) {
551
- cleanupError = error;
552
- }
553
- finally {
554
- await resourceLease.release();
555
- sessionController.abort();
556
- }
581
+ // The app session (and its log streams) must finish before platform
582
+ // disposal can remove the underlying device. Run independent cleanup
583
+ // all-settled so one failure cannot strand another owned resource.
584
+ const appCleanup = await Promise.allSettled([
585
+ crashMonitor.dispose(),
586
+ disposeCurrentAppSession(),
587
+ ]);
588
+ const infrastructureCleanup = await Promise.allSettled([
589
+ bridge.dispose(),
590
+ platformInstance.dispose(),
591
+ metroInstance.dispose(),
592
+ metroPortLease?.release(),
593
+ ]);
594
+ const leaseCleanup = await Promise.allSettled([resourceLease.release()]);
595
+ const cleanupError = [...appCleanup, ...infrastructureCleanup, ...leaseCleanup]
596
+ .find((result) => result.status === 'rejected')
597
+ ?.reason;
557
598
  sessionLogger.debug('session resources disposed');
558
599
  if (hookError) {
559
600
  disposeSpan.fail(hookError);
@@ -786,6 +827,9 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
786
827
  // (e.g. a platform init call abandoned after a readiness timeout race).
787
828
  sessionController.abort();
788
829
  await Promise.allSettled([resourceLease.release(), metroPortLease?.release()]);
830
+ if (receivedEarlySignal) {
831
+ process.exit(1);
832
+ }
789
833
  throw error;
790
834
  }
791
835
  };
@@ -1 +1 @@
1
- {"version":3,"file":"resource-lock.d.ts","sourceRoot":"","sources":["../src/resource-lock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBzE,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,CACP,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,0BAA0B,KACjC,OAAO,CAAC,aAAa,CAAC,CAAC;CAC7B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAqQF,eAAO,MAAM,yBAAyB,GACpC,UAAS,0BAA+B,KACvC,mBAkKF,CAAC"}
1
+ {"version":3,"file":"resource-lock.d.ts","sourceRoot":"","sources":["../src/resource-lock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBzE,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,CACP,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,0BAA0B,KACjC,OAAO,CAAC,aAAa,CAAC,CAAC;CAC7B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAqQF,eAAO,MAAM,yBAAyB,GACpC,UAAS,0BAA+B,KACvC,mBA+KF,CAAC"}
@@ -239,8 +239,18 @@ export const createResourceLockManager = (options = {}) => {
239
239
  if (released) {
240
240
  return;
241
241
  }
242
- await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
243
- scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
242
+ try {
243
+ await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
244
+ scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
245
+ }
246
+ catch (error) {
247
+ // A failed refresh is not fatal by design: the lock goes stale
248
+ // and another holder reclaims it. Swallow it so a transient
249
+ // write error (e.g. the owner file racing a concurrent release,
250
+ // or an EPERM on Windows when the directory is being torn down)
251
+ // never surfaces as an unhandled rejection from this interval.
252
+ scopedLogger.debug('heartbeat refresh for ticket %s failed: %s', ticketId, error instanceof Error ? error.message : String(error));
253
+ }
244
254
  }
245
255
  finally {
246
256
  heartbeatInFlight = false;
@@ -0,0 +1,4 @@
1
+ import type { Status } from '@jest/test-result';
2
+ import type { TestResultStatus } from '@react-native-harness/bridge';
3
+ export declare const toJestStatus: (status: TestResultStatus) => Status;
4
+ //# sourceMappingURL=toJestStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toJestStatus.d.ts","sourceRoot":"","sources":["../src/toJestStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,eAAO,MAAM,YAAY,GAAI,QAAQ,gBAAgB,KAAG,MACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ // Jest's own runners expose skipped assertions as `pending` to reporters.
2
+ export const toJestStatus = (status) => status === 'skipped' ? 'pending' : status;
@@ -1,4 +1,5 @@
1
- import type { Status, TestResult } from '@jest/test-result';
1
+ import type { TestResult } from '@jest/test-result';
2
+ import type { TestResultStatus } from '@react-native-harness/bridge';
2
3
  export type Options = {
3
4
  stats: {
4
5
  failures: number;
@@ -16,7 +17,7 @@ export type Options = {
16
17
  testPath?: string;
17
18
  title?: string;
18
19
  fullName?: string;
19
- status: Status;
20
+ status: TestResultStatus;
20
21
  location?: {
21
22
  column: number;
22
23
  line: number;
@@ -1 +1 @@
1
- {"version":3,"file":"toTestResult.d.ts","sourceRoot":"","sources":["../src/toTestResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;QACX,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE;YACT,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;CACnC,CAAC;AAwDF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,UAiB/C,CAAC"}
1
+ {"version":3,"file":"toTestResult.d.ts","sourceRoot":"","sources":["../src/toTestResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;QACX,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,gBAAgB,CAAC;QACzB,QAAQ,CAAC,EAAE;YACT,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;CACnC,CAAC;AAwDF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,UAiB/C,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { toJestStatus } from './toJestStatus.js';
1
2
  const getPerfStats = ({ stats }) => {
2
3
  const start = new Date(stats.start).getTime();
3
4
  const end = new Date(stats.end).getTime();
@@ -38,7 +39,7 @@ const getTestResults = ({ errorMessage, tests, jestTestPath, }) => {
38
39
  failureMessages: actualErrorMessage ? [actualErrorMessage] : [],
39
40
  fullName: test.fullName || test.testPath || jestTestPath || '',
40
41
  numPassingAsserts: test.status === 'passed' ? 1 : 0,
41
- status: test.status,
42
+ status: toJestStatus(test.status),
42
43
  title: test.title || '',
43
44
  location: test.location,
44
45
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-harness/jest",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -39,13 +39,13 @@
39
39
  "jest-util": "^30.2.0",
40
40
  "tslib": "^2.3.0",
41
41
  "yargs": "^17.7.2",
42
- "@react-native-harness/cache": "1.4.0",
43
- "@react-native-harness/bundler-metro": "1.4.0",
44
- "@react-native-harness/config": "1.4.0",
45
- "@react-native-harness/bridge": "1.4.0",
46
- "@react-native-harness/plugins": "1.4.0",
47
- "@react-native-harness/platforms": "1.4.0",
48
- "@react-native-harness/tools": "1.4.0"
42
+ "@react-native-harness/bridge": "1.5.0",
43
+ "@react-native-harness/config": "1.5.0",
44
+ "@react-native-harness/plugins": "1.5.0",
45
+ "@react-native-harness/cache": "1.5.0",
46
+ "@react-native-harness/bundler-metro": "1.5.0",
47
+ "@react-native-harness/platforms": "1.5.0",
48
+ "@react-native-harness/tools": "1.5.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/yargs": "^17.0.32",
@@ -32,6 +32,7 @@ import {
32
32
  import { formatHarnessErrorMessage } from './format-harness-error.js';
33
33
  import { logger } from '@react-native-harness/tools';
34
34
  import { printSummary, writeTraceFile } from './diagnostics/index.js';
35
+ import { toJestStatus } from './toJestStatus.js';
35
36
 
36
37
  const diagnosticsLogger = logger.child('diagnostics');
37
38
 
@@ -109,7 +110,7 @@ const emitHarnessTestFinished = async (
109
110
  location,
110
111
  numPassingAsserts: event.status === 'passed' ? 1 : 0,
111
112
  startedAt: event.startedAt,
112
- status: event.status,
113
+ status: toJestStatus(event.status),
113
114
  title: event.name,
114
115
  };
115
116
 
@@ -38,6 +38,7 @@ import {
38
38
  import {
39
39
  createCrashArtifactWriter,
40
40
  createDiagnostics,
41
+ delay,
41
42
  logger,
42
43
  getTimeoutSignal,
43
44
  ensureSignalsDeliverable,
@@ -94,6 +95,25 @@ const ignorePromiseRejection = () => undefined;
94
95
  const isBridgeDisconnectError = (error: unknown) =>
95
96
  error instanceof Error && error.message === 'App bridge disconnected';
96
97
  const TEST_RUN_BRIDGE_STABILITY_WAIT_MS = 500;
98
+ const DISPOSE_HOOK_TIMEOUT_MS = 5_000;
99
+
100
+ const waitForBoundedSettlement = async <T>(
101
+ promise: Promise<T>,
102
+ timeoutMs: number
103
+ ): Promise<PromiseSettledResult<T> | null> => {
104
+ const timeout = delay(timeoutMs);
105
+ try {
106
+ return await Promise.race([
107
+ promise.then(
108
+ (value) => ({ status: 'fulfilled', value }) as const,
109
+ (reason) => ({ status: 'rejected', reason }) as const
110
+ ),
111
+ timeout.promise.then(() => null),
112
+ ]);
113
+ } finally {
114
+ timeout.cancel();
115
+ }
116
+ };
97
117
 
98
118
  type DisconnectObservableBridge = {
99
119
  readonly connection: AppConnection | null;
@@ -487,7 +507,11 @@ export const createHarnessSession = async (
487
507
  // child the runner threads it into.
488
508
  ensureSignalsDeliverable();
489
509
  const sessionController = new AbortController();
490
- const onEarlySignal = () => sessionController.abort();
510
+ let receivedEarlySignal = false;
511
+ const onEarlySignal = () => {
512
+ receivedEarlySignal = true;
513
+ sessionController.abort();
514
+ };
491
515
  process.once('SIGTERM', onEarlySignal);
492
516
  process.once('SIGINT', onEarlySignal);
493
517
 
@@ -584,6 +608,11 @@ export const createHarnessSession = async (
584
608
 
585
609
  let metroInstance: MetroInstance;
586
610
  let platformInstance: HarnessPlatformRunner;
611
+ const initialized = {
612
+ metro: null as MetroInstance | null,
613
+ platform: null as HarnessPlatformRunner | null,
614
+ };
615
+ let initializationCancelled = false;
587
616
  // Noop until the Metro instance resolves and the deriver is attached.
588
617
  let disposeMetroDiagnostics: () => void = () => undefined;
589
618
 
@@ -594,6 +623,9 @@ export const createHarnessSession = async (
594
623
  {
595
624
  projectRoot,
596
625
  harnessConfig: runtimeConfig,
626
+ metroConfigEnhancer: platform.metroConfigEnhancer,
627
+ platformId: platform.platformId,
628
+ platformConfig: platform.config,
597
629
  websocketEndpoints: {
598
630
  [HARNESS_BRIDGE_PATH]: bridge.ws as unknown as MetroWebSocketEndpoint,
599
631
  },
@@ -601,6 +633,10 @@ export const createHarnessSession = async (
601
633
  },
602
634
  sessionController.signal,
603
635
  ).then((instance) => {
636
+ if (initializationCancelled) {
637
+ return instance.dispose().then(() => instance);
638
+ }
639
+ initialized.metro = instance;
604
640
  sessionLogger.debug('Metro initialized');
605
641
  // Attach the Metro diagnostics deriver before the eager prewarm
606
642
  // fires so its bundle build/request events land in the trace.
@@ -638,6 +674,10 @@ export const createHarnessSession = async (
638
674
  diagnostics,
639
675
  } satisfies HarnessPlatformInitOptions),
640
676
  ).then((instance) => {
677
+ if (initializationCancelled) {
678
+ return instance.dispose().then(() => instance);
679
+ }
680
+ initialized.platform = instance;
641
681
  sessionLogger.debug('platform runner initialized');
642
682
  return instance;
643
683
  });
@@ -647,10 +687,16 @@ export const createHarnessSession = async (
647
687
  ),
648
688
  ]);
649
689
  } catch (error) {
650
- // Only bridge and the Metro diagnostics listener need cleanup here;
651
- // leases are released by the outer catch.
690
+ // Roll back every resource whose initialization won a concurrent race.
691
+ // Leases are released by the outer catch.
692
+ initializationCancelled = true;
693
+ sessionController.abort();
652
694
  disposeMetroDiagnostics();
653
- await bridge.dispose();
695
+ await Promise.allSettled([
696
+ bridge.dispose(),
697
+ initialized.metro?.dispose(),
698
+ initialized.platform?.dispose(),
699
+ ]);
654
700
  throw error;
655
701
  }
656
702
 
@@ -759,7 +805,11 @@ export const createHarnessSession = async (
759
805
  const disposeSpan = diagnostics.start('dispose.total', { reason });
760
806
  let hookError: unknown;
761
807
 
762
- try {
808
+ // Abort only cancels finite/in-flight work. Ownership remains explicit
809
+ // below, so this cannot race a platform or app-session disposal path.
810
+ sessionController.abort();
811
+
812
+ const hookWork = (async () => {
763
813
  await hooks.drain();
764
814
  await pluginManager.callHook('harness:after-run', {
765
815
  runId: currentRun?.runId,
@@ -777,8 +827,17 @@ export const createHarnessSession = async (
777
827
  error: currentRun?.error,
778
828
  });
779
829
  await hooks.drain();
780
- } catch (error) {
781
- hookError = error;
830
+ })();
831
+ const hookResult = await waitForBoundedSettlement(
832
+ hookWork,
833
+ DISPOSE_HOOK_TIMEOUT_MS
834
+ );
835
+ if (hookResult === null) {
836
+ hookError = new Error(
837
+ `Timed out after ${DISPOSE_HOOK_TIMEOUT_MS}ms while running disposal hooks.`
838
+ );
839
+ } else if (hookResult.status === 'rejected') {
840
+ hookError = hookResult.reason;
782
841
  }
783
842
 
784
843
  if (runtimeConfig.forwardClientLogs) {
@@ -809,26 +868,23 @@ export const createHarnessSession = async (
809
868
  }
810
869
  }
811
870
 
812
- let cleanupError: unknown;
813
- try {
814
- // The app session (and its logcat/device log stream) must be fully
815
- // torn down before the platform is disposed: platform disposal can
816
- // shut down the device/emulator, and doing that concurrently with
817
- // the app session's own cleanup races the device's log stream
818
- // against the device disappearing out from under it.
819
- await Promise.all([crashMonitor.dispose(), disposeCurrentAppSession()]);
820
- await Promise.all([
821
- bridge.dispose(),
822
- platformInstance.dispose(),
823
- metroInstance.dispose(),
824
- metroPortLease?.release(),
825
- ]);
826
- } catch (error) {
827
- cleanupError = error;
828
- } finally {
829
- await resourceLease.release();
830
- sessionController.abort();
831
- }
871
+ // The app session (and its log streams) must finish before platform
872
+ // disposal can remove the underlying device. Run independent cleanup
873
+ // all-settled so one failure cannot strand another owned resource.
874
+ const appCleanup = await Promise.allSettled([
875
+ crashMonitor.dispose(),
876
+ disposeCurrentAppSession(),
877
+ ]);
878
+ const infrastructureCleanup = await Promise.allSettled([
879
+ bridge.dispose(),
880
+ platformInstance.dispose(),
881
+ metroInstance.dispose(),
882
+ metroPortLease?.release(),
883
+ ]);
884
+ const leaseCleanup = await Promise.allSettled([resourceLease.release()]);
885
+ const cleanupError = [...appCleanup, ...infrastructureCleanup, ...leaseCleanup]
886
+ .find((result): result is PromiseRejectedResult => result.status === 'rejected')
887
+ ?.reason;
832
888
 
833
889
  sessionLogger.debug('session resources disposed');
834
890
 
@@ -1102,6 +1158,9 @@ export const createHarnessSession = async (
1102
1158
  // (e.g. a platform init call abandoned after a readiness timeout race).
1103
1159
  sessionController.abort();
1104
1160
  await Promise.allSettled([resourceLease.release(), metroPortLease?.release()]);
1161
+ if (receivedEarlySignal) {
1162
+ process.exit(1);
1163
+ }
1105
1164
  throw error;
1106
1165
  }
1107
1166
  };
@@ -392,8 +392,21 @@ export const createResourceLockManager = (
392
392
  return;
393
393
  }
394
394
 
395
- await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
396
- scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
395
+ try {
396
+ await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
397
+ scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
398
+ } catch (error) {
399
+ // A failed refresh is not fatal by design: the lock goes stale
400
+ // and another holder reclaims it. Swallow it so a transient
401
+ // write error (e.g. the owner file racing a concurrent release,
402
+ // or an EPERM on Windows when the directory is being torn down)
403
+ // never surfaces as an unhandled rejection from this interval.
404
+ scopedLogger.debug(
405
+ 'heartbeat refresh for ticket %s failed: %s',
406
+ ticketId,
407
+ error instanceof Error ? error.message : String(error),
408
+ );
409
+ }
397
410
  } finally {
398
411
  heartbeatInFlight = false;
399
412
  }
@@ -0,0 +1,6 @@
1
+ import type { Status } from '@jest/test-result';
2
+ import type { TestResultStatus } from '@react-native-harness/bridge';
3
+
4
+ // Jest's own runners expose skipped assertions as `pending` to reporters.
5
+ export const toJestStatus = (status: TestResultStatus): Status =>
6
+ status === 'skipped' ? 'pending' : status;
@@ -1,4 +1,6 @@
1
- import type { Status, TestResult } from '@jest/test-result';
1
+ import type { TestResult } from '@jest/test-result';
2
+ import type { TestResultStatus } from '@react-native-harness/bridge';
3
+ import { toJestStatus } from './toJestStatus.js';
2
4
 
3
5
  export type Options = {
4
6
  stats: {
@@ -17,7 +19,7 @@ export type Options = {
17
19
  testPath?: string;
18
20
  title?: string;
19
21
  fullName?: string;
20
- status: Status;
22
+ status: TestResultStatus;
21
23
  location?: {
22
24
  column: number;
23
25
  line: number;
@@ -75,7 +77,7 @@ const getTestResults = ({
75
77
  failureMessages: actualErrorMessage ? [actualErrorMessage] : [],
76
78
  fullName: test.fullName || test.testPath || jestTestPath || '',
77
79
  numPassingAsserts: test.status === 'passed' ? 1 : 0,
78
- status: test.status,
80
+ status: toJestStatus(test.status),
79
81
  title: test.title || '',
80
82
  location: test.location,
81
83
  };
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=bridge.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bridge.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bridge.test.ts"],"names":[],"mappings":""}