@react-native-harness/jest 1.0.0-canary.1766225407244 → 1.0.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 (59) hide show
  1. package/dist/__tests__/client-log-handler.test.d.ts +2 -0
  2. package/dist/__tests__/client-log-handler.test.d.ts.map +1 -0
  3. package/dist/__tests__/client-log-handler.test.js +262 -0
  4. package/dist/cli-args.test.d.ts +2 -0
  5. package/dist/cli-args.test.d.ts.map +1 -0
  6. package/dist/cli-args.test.js +1 -0
  7. package/dist/client-log-handler.d.ts +23 -0
  8. package/dist/client-log-handler.d.ts.map +1 -0
  9. package/dist/client-log-handler.js +84 -0
  10. package/dist/crash-monitor.d.ts +16 -0
  11. package/dist/crash-monitor.d.ts.map +1 -0
  12. package/dist/crash-monitor.js +75 -0
  13. package/dist/errors.d.ts +8 -0
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +17 -0
  16. package/dist/event-mapper.d.ts +16 -0
  17. package/dist/event-mapper.d.ts.map +1 -0
  18. package/dist/event-mapper.js +120 -0
  19. package/dist/global-setup.d.ts +3 -0
  20. package/dist/global-setup.d.ts.map +1 -0
  21. package/dist/global-setup.js +60 -0
  22. package/dist/global-teardown.d.ts +3 -0
  23. package/dist/global-teardown.d.ts.map +1 -0
  24. package/dist/global-teardown.js +11 -0
  25. package/dist/harness-reporter.d.ts +8 -0
  26. package/dist/harness-reporter.d.ts.map +1 -0
  27. package/dist/harness-reporter.js +12 -0
  28. package/dist/harness.d.ts +17 -3
  29. package/dist/harness.d.ts.map +1 -1
  30. package/dist/harness.js +114 -19
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +39 -8
  33. package/dist/jest-options.d.ts +2 -0
  34. package/dist/jest-options.d.ts.map +1 -0
  35. package/dist/jest-options.js +1 -0
  36. package/dist/logs.d.ts +1 -0
  37. package/dist/logs.d.ts.map +1 -1
  38. package/dist/logs.js +3 -0
  39. package/dist/run.d.ts.map +1 -1
  40. package/dist/run.js +1 -0
  41. package/dist/setup.d.ts.map +1 -1
  42. package/dist/setup.js +6 -0
  43. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  44. package/dist/types.d.ts +17 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +1 -0
  47. package/eslint.config.mjs +5 -2
  48. package/package.json +6 -7
  49. package/src/__tests__/client-log-handler.test.ts +328 -0
  50. package/src/client-log-handler.ts +101 -0
  51. package/src/crash-monitor.ts +109 -0
  52. package/src/errors.ts +20 -0
  53. package/src/harness.ts +173 -29
  54. package/src/index.ts +46 -10
  55. package/src/logs.ts +6 -0
  56. package/src/run.ts +1 -0
  57. package/src/setup.ts +8 -0
  58. package/tsconfig.json +3 -3
  59. package/vite.config.ts +18 -0
@@ -0,0 +1,3 @@
1
+ import type { Config as JestConfig } from 'jest-runner';
2
+ export default function (globalConfig: JestConfig.GlobalConfig): Promise<void>;
3
+ //# sourceMappingURL=global-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../src/global-setup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AA4CxD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBA4CnE"}
@@ -0,0 +1,60 @@
1
+ import { getConfig, } from '@react-native-harness/config';
2
+ import { getHarness as getHarnessExternal, NoRunnerSpecifiedError, RunnerNotFoundError, } from '@react-native-harness/cli/external';
3
+ import { preRunMessage } from 'jest-util';
4
+ import { getAdditionalCliArgs } from './cli-args.js';
5
+ import { logTestEnvironmentReady, logTestRunHeader } from './logs.js';
6
+ const getHarnessConfig = async (globalConfig) => {
7
+ const projectRoot = globalConfig.rootDir;
8
+ const { config: harnessConfig } = await getConfig(projectRoot);
9
+ return harnessConfig;
10
+ };
11
+ const getHarnessRunner = (config, cliArgs) => {
12
+ const selectedRunnerName = cliArgs.harnessRunner ?? config.defaultRunner;
13
+ if (!selectedRunnerName) {
14
+ throw new NoRunnerSpecifiedError(config.runners);
15
+ }
16
+ const runner = config.runners.find((runner) => runner.name === selectedRunnerName);
17
+ if (!runner) {
18
+ throw new RunnerNotFoundError(selectedRunnerName, config.runners);
19
+ }
20
+ return runner;
21
+ };
22
+ const getHarness = async (runner) => {
23
+ return await getHarnessExternal(runner);
24
+ };
25
+ export default async function (globalConfig) {
26
+ preRunMessage.remove(process.stderr);
27
+ const harnessConfig = global.HARNESS_CONFIG ?? (await getHarnessConfig(globalConfig));
28
+ const isWatchMode = globalConfig.watch || globalConfig.watchAll;
29
+ if (global.HARNESS) {
30
+ // Do not setup again if HARNESS is already initialized
31
+ // This is useful when running tests in watch mode
32
+ if (harnessConfig.resetEnvironmentBetweenTestFiles) {
33
+ // In watch mode, we want to restart the environment before each test run
34
+ await new Promise((resolve) => {
35
+ global.HARNESS.bridge.once('ready', resolve);
36
+ global.HARNESS.environment.restart();
37
+ });
38
+ }
39
+ return;
40
+ }
41
+ if (isWatchMode) {
42
+ // In watch mode, we want to dispose the Harness when the process exits.
43
+ process.on('exit', async () => {
44
+ await global.HARNESS.bridge.dispose();
45
+ await global.HARNESS.environment.dispose();
46
+ });
47
+ }
48
+ const cliArgs = getAdditionalCliArgs();
49
+ const selectedRunner = getHarnessRunner(harnessConfig, cliArgs);
50
+ if (globalConfig.collectCoverage) {
51
+ // This is going to be used by @react-native-harness/babel-preset
52
+ // to enable instrumentation of test files.
53
+ process.env.RN_HARNESS_COLLECT_COVERAGE = 'true';
54
+ }
55
+ logTestRunHeader(selectedRunner);
56
+ const harness = await getHarness(selectedRunner);
57
+ logTestEnvironmentReady(selectedRunner);
58
+ global.HARNESS_CONFIG = harnessConfig;
59
+ global.HARNESS = harness;
60
+ }
@@ -0,0 +1,3 @@
1
+ import type { Config as JestConfig } from 'jest-runner';
2
+ export default function (globalConfig: JestConfig.GlobalConfig): Promise<void>;
3
+ //# sourceMappingURL=global-teardown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-teardown.d.ts","sourceRoot":"","sources":["../src/global-teardown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAExD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBAYnE"}
@@ -0,0 +1,11 @@
1
+ export default async function (globalConfig) {
2
+ const isWatchMode = globalConfig.watch || globalConfig.watchAll;
3
+ if (isWatchMode) {
4
+ // In watch mode, we don't want to dispose the Harness.
5
+ return;
6
+ }
7
+ if (global.HARNESS) {
8
+ await global.HARNESS.bridge.dispose();
9
+ await global.HARNESS.environment.dispose();
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ import { BaseReporter } from '@jest/reporters';
2
+ import { Config } from '@jest/types';
3
+ export default class HarnessReporter extends BaseReporter {
4
+ #private;
5
+ constructor(globalConfig: Config.GlobalConfig);
6
+ onRunStart(): void;
7
+ }
8
+ //# sourceMappingURL=harness-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-reporter.d.ts","sourceRoot":"","sources":["../src/harness-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,YAAY;;gBAE3C,YAAY,EAAE,MAAM,CAAC,YAAY;IAKpC,UAAU,IAAI,IAAI;CAM5B"}
@@ -0,0 +1,12 @@
1
+ import { BaseReporter } from '@jest/reporters';
2
+ export default class HarnessReporter extends BaseReporter {
3
+ #globalConfig;
4
+ constructor(globalConfig) {
5
+ super();
6
+ this.#globalConfig = globalConfig;
7
+ }
8
+ onRunStart() {
9
+ super.onRunStart();
10
+ this.log(`Selected runner: ${this.#globalConfig.selectedRunner.name}`);
11
+ }
12
+ }
package/dist/harness.d.ts CHANGED
@@ -1,10 +1,24 @@
1
- import { BridgeClientFunctions } from '@react-native-harness/bridge';
2
- import { HarnessPlatform } from '@react-native-harness/platforms';
1
+ import { BridgeServer } from '@react-native-harness/bridge/server';
2
+ import { HarnessContext, TestExecutionOptions, TestSuiteResult } from '@react-native-harness/bridge';
3
+ import { HarnessPlatform, HarnessPlatformRunner } from '@react-native-harness/platforms';
4
+ import { Reporter } from '@react-native-harness/bundler-metro';
3
5
  import { Config as HarnessConfig } from '@react-native-harness/config';
6
+ import { CrashMonitor } from './crash-monitor.js';
7
+ export type HarnessRunTestsOptions = Exclude<TestExecutionOptions, 'platform'>;
4
8
  export type Harness = {
5
- runTests: BridgeClientFunctions['runTests'];
9
+ context: HarnessContext;
10
+ runTests: (path: string, options: HarnessRunTestsOptions) => Promise<TestSuiteResult>;
6
11
  restart: () => Promise<void>;
7
12
  dispose: () => Promise<void>;
13
+ crashMonitor: CrashMonitor;
8
14
  };
15
+ export declare const waitForAppReady: (options: {
16
+ metroEvents: Reporter;
17
+ serverBridge: BridgeServer;
18
+ platformInstance: HarnessPlatformRunner;
19
+ bundleStartTimeout: number;
20
+ maxRestarts: number;
21
+ signal: AbortSignal;
22
+ }) => Promise<void>;
9
23
  export declare const getHarness: (config: HarnessConfig, platform: HarnessPlatform, projectRoot: string) => Promise<Harness>;
10
24
  //# sourceMappingURL=harness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGlE,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKvE,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AA2EF,eAAO,MAAM,UAAU,GACrB,QAAQ,aAAa,EACrB,UAAU,eAAe,EACzB,aAAa,MAAM,KAClB,OAAO,CAAC,OAAO,CAkBjB,CAAC"}
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EACb,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,eAAe,EACf,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAGL,QAAQ,EAET,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAsB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAItE,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;AAE/E,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,sBAAsB,KAC5B,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,SAAS;IAC7C,WAAW,EAAE,QAAQ,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,qBAAqB,CAAC;IACxC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;CACrB,KAAG,OAAO,CAAC,IAAI,CAgFf,CAAC;AA4GF,eAAO,MAAM,UAAU,GACrB,QAAQ,aAAa,EACrB,UAAU,eAAe,EACzB,aAAa,MAAM,KAClB,OAAO,CAAC,OAAO,CAkBjB,CAAC"}
package/dist/harness.js CHANGED
@@ -1,39 +1,125 @@
1
- import { getBridgeServer } from '@react-native-harness/bridge/server';
2
- import { getMetroInstance } from '@react-native-harness/bundler-metro';
3
- import { InitializationTimeoutError } from './errors.js';
4
- import pRetry from 'p-retry';
5
- const BRIDGE_READY_TIMEOUT = 10000;
1
+ import { getBridgeServer, } from '@react-native-harness/bridge/server';
2
+ import { getMetroInstance, prewarmMetroBundle, } from '@react-native-harness/bundler-metro';
3
+ import { InitializationTimeoutError, MaxAppRestartsError } from './errors.js';
4
+ import { createCrashMonitor } from './crash-monitor.js';
5
+ import { createClientLogListener } from './client-log-handler.js';
6
+ import { logMetroPrewarmCompleted } from './logs.js';
7
+ export const waitForAppReady = async (options) => {
8
+ const { metroEvents, serverBridge, platformInstance, bundleStartTimeout, maxRestarts, signal, } = options;
9
+ let restartCount = 0;
10
+ let isBundling = false;
11
+ let bundleTimeoutId = null;
12
+ const clearBundleTimeout = () => {
13
+ if (bundleTimeoutId) {
14
+ clearTimeout(bundleTimeoutId);
15
+ bundleTimeoutId = null;
16
+ }
17
+ };
18
+ return new Promise((resolve, reject) => {
19
+ // Handle abort signal
20
+ signal.addEventListener('abort', () => {
21
+ clearBundleTimeout();
22
+ reject(new DOMException('The operation was aborted', 'AbortError'));
23
+ });
24
+ // Start/restart the bundle timeout
25
+ const startBundleTimeout = () => {
26
+ clearBundleTimeout();
27
+ bundleTimeoutId = setTimeout(() => {
28
+ if (isBundling)
29
+ return; // Don't restart while bundling
30
+ if (restartCount >= maxRestarts) {
31
+ cleanup();
32
+ reject(new MaxAppRestartsError(restartCount + 1));
33
+ return;
34
+ }
35
+ restartCount++;
36
+ platformInstance.restartApp().catch(reject);
37
+ startBundleTimeout(); // Reset timer for next attempt
38
+ }, bundleStartTimeout);
39
+ };
40
+ // Metro event listener
41
+ const onMetroEvent = (event) => {
42
+ if (event.type === 'bundle_build_started') {
43
+ isBundling = true;
44
+ clearBundleTimeout(); // Cancel restart timer while bundling
45
+ }
46
+ else if (event.type === 'bundle_build_done' ||
47
+ event.type === 'bundle_build_failed') {
48
+ isBundling = false;
49
+ startBundleTimeout(); // Reset timer after bundle completes
50
+ }
51
+ };
52
+ // Bridge ready listener
53
+ const onReady = () => {
54
+ cleanup();
55
+ resolve();
56
+ };
57
+ const cleanup = () => {
58
+ clearBundleTimeout();
59
+ metroEvents.removeListener(onMetroEvent);
60
+ serverBridge.off('ready', onReady);
61
+ };
62
+ // Setup listeners
63
+ metroEvents.addListener(onMetroEvent);
64
+ serverBridge.once('ready', onReady);
65
+ // Start the app and timeout
66
+ platformInstance.restartApp().catch(reject);
67
+ startBundleTimeout();
68
+ });
69
+ };
6
70
  const getHarnessInternal = async (config, platform, projectRoot, signal) => {
71
+ const context = {
72
+ platform,
73
+ };
7
74
  const [metroInstance, platformInstance, serverBridge] = await Promise.all([
8
75
  getMetroInstance({ projectRoot, harnessConfig: config }, signal),
9
- import(platform.runner).then((module) => module.default(platform.config)),
76
+ import(platform.runner).then((module) => module.default(platform.config, config)),
10
77
  getBridgeServer({
11
- port: 3001,
78
+ port: config.webSocketPort,
12
79
  timeout: config.bridgeTimeout,
80
+ context,
13
81
  }),
14
82
  ]);
83
+ // Forward client logs to console if enabled
84
+ const clientLogListener = createClientLogListener();
85
+ if (config.forwardClientLogs) {
86
+ metroInstance.events.addListener(clientLogListener);
87
+ }
15
88
  const dispose = async () => {
89
+ if (config.forwardClientLogs) {
90
+ metroInstance.events.removeListener(clientLogListener);
91
+ }
16
92
  await Promise.all([
17
93
  serverBridge.dispose(),
18
94
  platformInstance.dispose(),
19
95
  metroInstance.dispose(),
20
96
  ]);
21
97
  };
98
+ const crashMonitor = createCrashMonitor({
99
+ interval: config.crashDetectionInterval,
100
+ platformRunner: platformInstance,
101
+ bridgeServer: serverBridge,
102
+ });
22
103
  if (signal.aborted) {
23
104
  await dispose();
24
105
  throw new DOMException('The operation was aborted', 'AbortError');
25
106
  }
26
107
  try {
27
- await pRetry(() => new Promise((resolve, reject) => {
28
- signal.addEventListener('abort', () => {
29
- reject(new DOMException('The operation was aborted', 'AbortError'));
30
- });
31
- serverBridge.once('ready', () => resolve());
32
- platformInstance.restartApp().catch(reject);
33
- }), {
34
- minTimeout: BRIDGE_READY_TIMEOUT,
35
- maxTimeout: BRIDGE_READY_TIMEOUT,
36
- retries: Infinity,
108
+ await prewarmMetroBundle({
109
+ projectRoot,
110
+ entryPoint: config.entryPoint,
111
+ platform: platform.platformId,
112
+ dev: true,
113
+ minify: false,
114
+ signal,
115
+ });
116
+ logMetroPrewarmCompleted(platform);
117
+ await waitForAppReady({
118
+ metroEvents: metroInstance.events,
119
+ serverBridge,
120
+ platformInstance: platformInstance,
121
+ bundleStartTimeout: config.bundleStartTimeout,
122
+ maxRestarts: config.maxAppRestarts,
37
123
  signal,
38
124
  });
39
125
  }
@@ -42,19 +128,28 @@ const getHarnessInternal = async (config, platform, projectRoot, signal) => {
42
128
  throw error;
43
129
  }
44
130
  const restart = () => new Promise((resolve, reject) => {
45
- serverBridge.once('ready', () => resolve());
131
+ crashMonitor.markIntentionalRestart();
132
+ serverBridge.once('ready', () => {
133
+ crashMonitor.clearIntentionalRestart();
134
+ resolve();
135
+ });
46
136
  platformInstance.restartApp().catch(reject);
47
137
  });
48
138
  return {
139
+ context,
49
140
  runTests: async (path, options) => {
50
141
  const client = serverBridge.rpc.clients.at(-1);
51
142
  if (!client) {
52
143
  throw new Error('No client found');
53
144
  }
54
- return await client.runTests(path, options);
145
+ return await client.runTests(path, {
146
+ ...options,
147
+ runner: platform.runner,
148
+ });
55
149
  },
56
150
  restart,
57
151
  dispose,
152
+ crashMonitor,
58
153
  };
59
154
  };
60
155
  export const getHarness = async (config, platform, projectRoot) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,MAAM,EACN,aAAa,EACb,WAAW,EACX,aAAa,EACb,IAAI,EACJ,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAc5C,MAAM,CAAC,OAAO,OAAO,WAAY,YAAW,2BAA2B;;IACrE,QAAQ,CAAC,QAAQ,QAAQ;gBAIb,YAAY,EAAE,MAAM,CAAC,YAAY;IAIvC,QAAQ,CACZ,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC;IAkCV,oBAAoB,CACxB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,IAAI,CAAC;CA+CjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,MAAM,EACN,aAAa,EACb,WAAW,EACX,aAAa,EACb,IAAI,EACJ,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAe5C,MAAM,CAAC,OAAO,OAAO,WAAY,YAAW,2BAA2B;;IACrE,QAAQ,CAAC,QAAQ,QAAQ;gBAIb,YAAY,EAAE,MAAM,CAAC,YAAY;IAIvC,QAAQ,CACZ,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC;IAkCV,oBAAoB,CACxB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,IAAI,CAAC;CAkFjB"}
package/dist/index.js CHANGED
@@ -4,7 +4,8 @@ import { setup } from './setup.js';
4
4
  import { teardown } from './teardown.js';
5
5
  import { HarnessError } from '@react-native-harness/tools';
6
6
  import { getErrorMessage } from './logs.js';
7
- import { DeviceNotRespondingError } from '@react-native-harness/bridge';
7
+ import { DeviceNotRespondingError } from '@react-native-harness/bridge/server';
8
+ import { NativeCrashError } from './errors.js';
8
9
  class CancelRun extends Error {
9
10
  constructor(message) {
10
11
  super(message);
@@ -53,15 +54,45 @@ export default class JestHarness {
53
54
  await harness.restart();
54
55
  }
55
56
  isFirstTest = false;
56
- return onStart(test).then(() => runHarnessTestFile({
57
- testPath: test.path,
58
- harness,
59
- globalConfig: this.#globalConfig,
60
- projectConfig: test.context.config,
61
- }));
57
+ return onStart(test).then(async () => {
58
+ if (!harnessConfig.detectNativeCrashes) {
59
+ return runHarnessTestFile({
60
+ testPath: test.path,
61
+ harness,
62
+ globalConfig: this.#globalConfig,
63
+ projectConfig: test.context.config,
64
+ });
65
+ }
66
+ // Start crash monitoring
67
+ const crashPromise = harness.crashMonitor.startMonitoring(test.path);
68
+ try {
69
+ const result = await Promise.race([
70
+ runHarnessTestFile({
71
+ testPath: test.path,
72
+ harness,
73
+ globalConfig: this.#globalConfig,
74
+ projectConfig: test.context.config,
75
+ }),
76
+ crashPromise,
77
+ ]);
78
+ return result;
79
+ }
80
+ finally {
81
+ harness.crashMonitor.stopMonitoring();
82
+ }
83
+ });
62
84
  })
63
85
  .then((result) => onResult(test, result))
64
- .catch((err) => {
86
+ .catch(async (err) => {
87
+ if (err instanceof NativeCrashError) {
88
+ onFailure(test, {
89
+ message: err.message,
90
+ stack: '',
91
+ });
92
+ // Restart the app for the next test file
93
+ await harness.restart();
94
+ return;
95
+ }
65
96
  if (err instanceof DeviceNotRespondingError) {
66
97
  onFailure(test, {
67
98
  message: err.message,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jest-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest-options.d.ts","sourceRoot":"","sources":["../src/jest-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export {};
package/dist/logs.d.ts CHANGED
@@ -3,5 +3,6 @@ import { HarnessError } from '@react-native-harness/tools';
3
3
  export declare const log: (message: string) => void;
4
4
  export declare const logTestRunHeader: (runner: HarnessPlatform) => void;
5
5
  export declare const logTestEnvironmentReady: (runner: HarnessPlatform) => void;
6
+ export declare const logMetroPrewarmCompleted: (runner: HarnessPlatform) => void;
6
7
  export declare const getErrorMessage: (error: HarnessError) => string;
7
8
  //# sourceMappingURL=logs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAY3D,eAAO,MAAM,GAAG,GAAI,SAAS,MAAM,KAAG,IAErC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,eAAe,KAAG,IAI1D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,eAAe,KAAG,IAEjE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,YAAY,KAAG,MAErD,CAAC"}
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAY3D,eAAO,MAAM,GAAG,GAAI,SAAS,MAAM,KAAG,IAErC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,eAAe,KAAG,IAI1D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,eAAe,KAAG,IAEjE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,eAAe,KAAG,IAIlE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,YAAY,KAAG,MAErD,CAAC"}
package/dist/logs.js CHANGED
@@ -15,6 +15,9 @@ export const logTestRunHeader = (runner) => {
15
15
  export const logTestEnvironmentReady = (runner) => {
16
16
  log(`${TAG} Runner ${chalk.bold(runner.name)} ready\n`);
17
17
  };
18
+ export const logMetroPrewarmCompleted = (runner) => {
19
+ log(`${TAG} Metro pre-warm for ${chalk.bold(runner.name)} completed\n`);
20
+ };
18
21
  export const getErrorMessage = (error) => {
19
22
  return `${ERROR_TAG} ${error.message}\n`;
20
23
  };
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuD5C,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAE9B,eAAO,MAAM,kBAAkB,EAAE,kBA4EhC,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuD5C,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAE9B,eAAO,MAAM,kBAAkB,EAAE,kBA6EhC,CAAC"}
package/dist/run.js CHANGED
@@ -52,6 +52,7 @@ export const runHarnessTestFile = async ({ testPath, globalConfig, projectConfig
52
52
  testNamePattern: globalConfig.testNamePattern,
53
53
  setupFiles,
54
54
  setupFilesAfterEnv,
55
+ runner: harness.context.platform.runner,
55
56
  });
56
57
  const end = Date.now();
57
58
  const allTests = flattenTests(results);
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAqCxD,eAAO,MAAM,KAAK,GAAU,cAAc,UAAU,CAAC,YAAY,kBAyChE,CAAC"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAqCxD,eAAO,MAAM,KAAK,GAAU,cAAc,UAAU,CAAC,YAAY,kBAiDhE,CAAC"}
package/dist/setup.js CHANGED
@@ -42,6 +42,12 @@ export const setup = async (globalConfig) => {
42
42
  // This is going to be used by @react-native-harness/babel-preset
43
43
  // to enable instrumentation of test files.
44
44
  process.env.RN_HARNESS_COLLECT_COVERAGE = 'true';
45
+ if (harnessConfig.coverage?.root) {
46
+ process.env.RN_HARNESS_COVERAGE_ROOT = harnessConfig.coverage.root;
47
+ }
48
+ }
49
+ if (harnessConfig.disableViewFlattening) {
50
+ process.env.RN_HARNESS_VIEW_FLATTENING = 'false';
45
51
  }
46
52
  logTestRunHeader(selectedRunner);
47
53
  const harness = await getHarness(harnessConfig, selectedRunner, globalConfig.rootDir);