@react-native-harness/jest 1.3.0 → 1.4.0-rc.1
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/dist/__tests__/bridge.test.js +23 -2
- package/dist/__tests__/crash-monitor.test.js +146 -192
- package/dist/__tests__/errors.test.js +39 -0
- package/dist/__tests__/execute-run.test.js +190 -26
- package/dist/__tests__/harness-session.test.js +63 -239
- package/dist/__tests__/index.test.js +59 -10
- package/dist/__tests__/test-file-platform-filter.test.d.ts +2 -0
- package/dist/__tests__/test-file-platform-filter.test.d.ts.map +1 -0
- package/dist/__tests__/test-file-platform-filter.test.js +47 -0
- package/dist/crash-monitor.d.ts +5 -4
- package/dist/crash-monitor.d.ts.map +1 -1
- package/dist/crash-monitor.js +116 -102
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +41 -2
- package/dist/execute-run.d.ts +4 -2
- package/dist/execute-run.d.ts.map +1 -1
- package/dist/execute-run.js +85 -7
- package/dist/harness-session.d.ts +21 -1
- package/dist/harness-session.d.ts.map +1 -1
- package/dist/harness-session.js +134 -39
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -2
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +1 -0
- package/dist/test-file-platform-filter.d.ts +5 -0
- package/dist/test-file-platform-filter.d.ts.map +1 -0
- package/dist/test-file-platform-filter.js +40 -0
- package/dist/toTestResult.d.ts +1 -0
- package/dist/toTestResult.d.ts.map +1 -1
- package/dist/toTestResult.js +2 -2
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/__tests__/bridge.test.ts +37 -2
- package/src/__tests__/crash-monitor.test.ts +182 -231
- package/src/__tests__/errors.test.ts +75 -0
- package/src/__tests__/execute-run.test.ts +235 -34
- package/src/__tests__/harness-session.test.ts +93 -0
- package/src/__tests__/index.test.ts +89 -11
- package/src/__tests__/test-file-platform-filter.test.ts +83 -0
- package/src/crash-monitor.ts +192 -131
- package/src/errors.ts +62 -1
- package/src/execute-run.ts +125 -11
- package/src/harness-session.ts +177 -42
- package/src/index.ts +40 -11
- package/src/run.ts +1 -0
- package/src/test-file-platform-filter.ts +57 -0
- package/src/toTestResult.ts +3 -2
package/dist/execute-run.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { runHarnessTestFile } from './run.js';
|
|
4
|
-
import { NativeCrashError, StartupStallError, } from './errors.js';
|
|
5
|
-
import { DeviceNotRespondingError } from '@react-native-harness/bridge/server';
|
|
4
|
+
import { NativeCrashError, RuntimeDisconnectError, StartupStallError, } from './errors.js';
|
|
5
|
+
import { AppBridgeDisconnectedError, DeviceNotRespondingError, } from '@react-native-harness/bridge/server';
|
|
6
|
+
import { createPlatformSkippedTestResult, shouldRunHarnessTestFile, } from './test-file-platform-filter.js';
|
|
6
7
|
const createRunSummary = () => ({ passed: 0, failed: 0, skipped: 0, todo: 0 });
|
|
7
8
|
const applyJestResultToSummary = (summary, result) => {
|
|
8
9
|
summary.passed += result.numPassingTests;
|
|
@@ -18,19 +19,62 @@ class CancelRun extends Error {
|
|
|
18
19
|
}
|
|
19
20
|
const buildTestFailure = (err) => {
|
|
20
21
|
if (err instanceof NativeCrashError ||
|
|
22
|
+
err instanceof RuntimeDisconnectError ||
|
|
21
23
|
err instanceof StartupStallError ||
|
|
24
|
+
err instanceof AppBridgeDisconnectedError ||
|
|
22
25
|
err instanceof DeviceNotRespondingError) {
|
|
23
26
|
return { message: err.message, stack: '' };
|
|
24
27
|
}
|
|
25
28
|
return err;
|
|
26
29
|
};
|
|
27
|
-
|
|
30
|
+
const toJestMode = (declarationMode) => declarationMode;
|
|
31
|
+
const emitHarnessTestStarted = async (emitEvent, event) => {
|
|
32
|
+
await emitEvent('test-case-start', event.file, {
|
|
33
|
+
ancestorTitles: event.ancestorTitles,
|
|
34
|
+
fullName: event.fullName,
|
|
35
|
+
mode: toJestMode(event.declarationMode),
|
|
36
|
+
title: event.name,
|
|
37
|
+
startedAt: event.startedAt,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const emitHarnessTestFinished = async (emitEvent, event) => {
|
|
41
|
+
const failureMessage = event.error?.message;
|
|
42
|
+
const codeFrame = event.error?.codeFrame;
|
|
43
|
+
const location = codeFrame?.location
|
|
44
|
+
? { column: codeFrame.location.column, line: codeFrame.location.row }
|
|
45
|
+
: null;
|
|
46
|
+
const testCaseResult = {
|
|
47
|
+
ancestorTitles: event.ancestorTitles,
|
|
48
|
+
duration: event.duration,
|
|
49
|
+
failureDetails: [],
|
|
50
|
+
failureMessages: failureMessage
|
|
51
|
+
? [`${failureMessage}${codeFrame ? `\n\n${codeFrame.content}` : ''}`]
|
|
52
|
+
: [],
|
|
53
|
+
fullName: event.fullName,
|
|
54
|
+
location,
|
|
55
|
+
numPassingAsserts: event.status === 'passed' ? 1 : 0,
|
|
56
|
+
startedAt: event.startedAt,
|
|
57
|
+
status: event.status,
|
|
58
|
+
title: event.name,
|
|
59
|
+
};
|
|
60
|
+
await emitEvent('test-case-result', event.file, testCaseResult);
|
|
61
|
+
};
|
|
62
|
+
const isHarnessCaseEvent = (event) => event.type === 'test-started' || event.type === 'test-finished';
|
|
63
|
+
export const executeRun = async (session, tests, watcher, emitEvent, globalConfig) => {
|
|
28
64
|
const runId = randomUUID();
|
|
29
65
|
const startTime = Date.now();
|
|
30
66
|
const watchMode = globalConfig.watch || globalConfig.watchAll;
|
|
31
67
|
const rootDir = globalConfig.rootDir ?? process.cwd();
|
|
32
68
|
const testFiles = tests.map((t) => path.relative(rootDir, t.path));
|
|
33
69
|
const summary = createRunSummary();
|
|
70
|
+
let caseEventChain = Promise.resolve();
|
|
71
|
+
const unsubscribe = session.onTestRunnerEvent((event) => {
|
|
72
|
+
if (isHarnessCaseEvent(event)) {
|
|
73
|
+
caseEventChain = caseEventChain.then(() => event.type === 'test-started'
|
|
74
|
+
? emitHarnessTestStarted(emitEvent, event)
|
|
75
|
+
: emitHarnessTestFinished(emitEvent, event));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
34
78
|
const updateRunState = (overrides = {}) => {
|
|
35
79
|
const state = {
|
|
36
80
|
runId,
|
|
@@ -59,6 +103,8 @@ export const executeRun = async (session, tests, watcher, onStart, onResult, onF
|
|
|
59
103
|
host: session.config.host?.trim() || undefined,
|
|
60
104
|
});
|
|
61
105
|
const shouldResetEnv = session.config.resetEnvironmentBetweenTestFiles;
|
|
106
|
+
const platformId = session.context.platform.platformId;
|
|
107
|
+
const knownPlatformIds = new Set(session.config.runners.map((runner) => runner.platformId));
|
|
62
108
|
let isFirstTest = true;
|
|
63
109
|
let runError;
|
|
64
110
|
try {
|
|
@@ -79,13 +125,39 @@ export const executeRun = async (session, tests, watcher, onStart, onResult, onF
|
|
|
79
125
|
});
|
|
80
126
|
};
|
|
81
127
|
await session.callHook('test-file:started', { runId, file: relativeTestPath });
|
|
128
|
+
if (!shouldRunHarnessTestFile(test.path, platformId, knownPlatformIds)) {
|
|
129
|
+
try {
|
|
130
|
+
await emitEvent('test-file-start', test);
|
|
131
|
+
const skippedResult = createPlatformSkippedTestResult(test.path);
|
|
132
|
+
applyJestResultToSummary(summary, skippedResult);
|
|
133
|
+
updateRunState();
|
|
134
|
+
await emitTestFileFinished({
|
|
135
|
+
status: 'skipped',
|
|
136
|
+
duration: Date.now() - fileStartedAt,
|
|
137
|
+
result: null,
|
|
138
|
+
});
|
|
139
|
+
await emitEvent('test-file-success', test, skippedResult);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (!emittedTestFileFinished) {
|
|
143
|
+
await emitTestFileFinished({
|
|
144
|
+
status: 'failed',
|
|
145
|
+
duration: Date.now() - fileStartedAt,
|
|
146
|
+
result: null,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
updateRunState({ error: err });
|
|
150
|
+
await emitEvent('test-file-failure', test, buildTestFailure(err));
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
82
154
|
try {
|
|
83
155
|
if (shouldResetEnv && !isFirstTest) {
|
|
84
156
|
await session.restartApp(test.path);
|
|
85
157
|
}
|
|
86
158
|
isFirstTest = false;
|
|
87
159
|
session.flushClientLogs();
|
|
88
|
-
await
|
|
160
|
+
await emitEvent('test-file-start', test);
|
|
89
161
|
await session.ensureAppReady(test.path);
|
|
90
162
|
// Crash detection is handled inside session.runTestFile; NativeCrashError
|
|
91
163
|
// propagates here if a crash wins the race.
|
|
@@ -106,7 +178,8 @@ export const executeRun = async (session, tests, watcher, onStart, onResult, onF
|
|
|
106
178
|
duration: result.duration,
|
|
107
179
|
result: result.harnessResult,
|
|
108
180
|
});
|
|
109
|
-
await
|
|
181
|
+
await caseEventChain;
|
|
182
|
+
await emitEvent('test-file-success', test, result.jestResult);
|
|
110
183
|
}
|
|
111
184
|
catch (err) {
|
|
112
185
|
if (!emittedTestFileFinished) {
|
|
@@ -117,17 +190,20 @@ export const executeRun = async (session, tests, watcher, onStart, onResult, onF
|
|
|
117
190
|
});
|
|
118
191
|
}
|
|
119
192
|
const isRuntimeFailure = err instanceof NativeCrashError ||
|
|
193
|
+
err instanceof RuntimeDisconnectError ||
|
|
120
194
|
err instanceof StartupStallError ||
|
|
195
|
+
err instanceof AppBridgeDisconnectedError ||
|
|
121
196
|
err instanceof DeviceNotRespondingError;
|
|
122
197
|
if (isRuntimeFailure) {
|
|
123
198
|
summary.failed += 1;
|
|
124
199
|
updateRunState();
|
|
125
200
|
}
|
|
126
|
-
if (err instanceof NativeCrashError) {
|
|
201
|
+
if (err instanceof NativeCrashError || err instanceof RuntimeDisconnectError) {
|
|
127
202
|
session.resetCrashState();
|
|
128
203
|
}
|
|
129
204
|
updateRunState({ error: isRuntimeFailure ? undefined : err });
|
|
130
|
-
|
|
205
|
+
await caseEventChain;
|
|
206
|
+
await emitEvent('test-file-failure', test, buildTestFailure(err));
|
|
131
207
|
}
|
|
132
208
|
}
|
|
133
209
|
}
|
|
@@ -147,5 +223,7 @@ export const executeRun = async (session, tests, watcher, onStart, onResult, onF
|
|
|
147
223
|
status: runState.status ?? (runError != null ? 'failed' : 'passed'),
|
|
148
224
|
...(runError != null ? { error: runError } : {}),
|
|
149
225
|
});
|
|
226
|
+
await caseEventChain;
|
|
227
|
+
unsubscribe();
|
|
150
228
|
}
|
|
151
229
|
};
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type AppConnection } from '@react-native-harness/bridge/server';
|
|
2
|
+
import { type HarnessContext, type TestRunnerEvents, type TestExecutionOptions, type TestSuiteResult } from '@react-native-harness/bridge';
|
|
2
3
|
import { type HarnessPlatform } from '@react-native-harness/platforms';
|
|
3
4
|
import { type HarnessPluginManager, type HarnessRunStatus, type HarnessRunSummary } from '@react-native-harness/plugins';
|
|
4
5
|
import { type Config as HarnessConfig } from '@react-native-harness/config';
|
|
5
6
|
import type { Config as JestConfig } from 'jest-runner';
|
|
7
|
+
import { type CrashMonitor } from './crash-monitor.js';
|
|
6
8
|
import { type ClientLogBuffer } from './client-log-handler.js';
|
|
7
9
|
import { type ResourceLockManager } from './resource-lock.js';
|
|
10
|
+
type DisconnectObservableBridge = {
|
|
11
|
+
readonly connection: AppConnection | null;
|
|
12
|
+
on: (event: 'disconnected', listener: () => void) => void;
|
|
13
|
+
off: (event: 'disconnected', listener: () => void) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const waitForBridgeDisconnectOrTimeout: ({ bridge, connection, timeoutMs, }: {
|
|
16
|
+
bridge: DisconnectObservableBridge;
|
|
17
|
+
connection: AppConnection;
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
}) => Promise<boolean>;
|
|
8
20
|
export type HarnessRunState = {
|
|
9
21
|
readonly runId: string;
|
|
10
22
|
readonly startTime: number;
|
|
@@ -19,6 +31,7 @@ export type HarnessRunTestsOptions = Exclude<TestExecutionOptions, 'platform'>;
|
|
|
19
31
|
export type HarnessSession = {
|
|
20
32
|
readonly config: HarnessConfig;
|
|
21
33
|
readonly context: HarnessContext;
|
|
34
|
+
onTestRunnerEvent: (listener: (event: TestRunnerEvents) => void) => () => void;
|
|
22
35
|
runTestFile: (path: string, options: HarnessRunTestsOptions) => Promise<TestSuiteResult>;
|
|
23
36
|
ensureAppReady: (testFilePath: string) => Promise<void>;
|
|
24
37
|
restartApp: (testFilePath?: string) => Promise<void>;
|
|
@@ -28,7 +41,14 @@ export type HarnessSession = {
|
|
|
28
41
|
setRunState: (state: HarnessRunState | null) => void;
|
|
29
42
|
dispose: (reason?: 'normal' | 'abort' | 'error') => Promise<void>;
|
|
30
43
|
};
|
|
44
|
+
export declare const waitForStartupCrash: ({ crashMonitor, detectNativeCrashes, testFilePath, signal, }: {
|
|
45
|
+
crashMonitor: CrashMonitor;
|
|
46
|
+
detectNativeCrashes: boolean;
|
|
47
|
+
testFilePath: string;
|
|
48
|
+
signal: AbortSignal;
|
|
49
|
+
}) => Promise<never>;
|
|
31
50
|
export declare const createHarnessSession: (globalConfig: JestConfig.GlobalConfig, { lockManager }?: {
|
|
32
51
|
lockManager?: ResourceLockManager;
|
|
33
52
|
}) => Promise<HarnessSession>;
|
|
53
|
+
export {};
|
|
34
54
|
//# sourceMappingURL=harness-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"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"}
|
package/dist/harness-session.js
CHANGED
|
@@ -2,9 +2,10 @@ import { createHarnessBridge, } from '@react-native-harness/bridge/server';
|
|
|
2
2
|
import { HARNESS_BRIDGE_PATH, } from '@react-native-harness/bridge';
|
|
3
3
|
import { getMetroInstance, isMetroCacheReusable, waitForMetroBackedAppReady, } from '@react-native-harness/bundler-metro';
|
|
4
4
|
import { createHarnessPluginManager, } from '@react-native-harness/plugins';
|
|
5
|
-
import {
|
|
5
|
+
import { createCrashArtifactWriter, logger, getTimeoutSignal, raceAbortSignals, } from '@react-native-harness/tools';
|
|
6
6
|
import { getConfig, ConfigSchema, } from '@react-native-harness/config';
|
|
7
7
|
import { preRunMessage } from 'jest-util';
|
|
8
|
+
import path from 'node:path';
|
|
8
9
|
import { PlatformReadyTimeoutError } from './errors.js';
|
|
9
10
|
import { NoRunnerSpecifiedError, RunnerNotFoundError } from './errors.js';
|
|
10
11
|
import { createCrashMonitor } from './crash-monitor.js';
|
|
@@ -18,6 +19,28 @@ import { logMetroCacheReused, logMetroPortFallback, logNativeCoverageCollected,
|
|
|
18
19
|
const sessionLogger = logger.child('runtime');
|
|
19
20
|
const defaultResourceLockManager = createResourceLockManager();
|
|
20
21
|
const ignorePromiseRejection = () => undefined;
|
|
22
|
+
const isBridgeDisconnectError = (error) => error instanceof Error && error.message === 'App bridge disconnected';
|
|
23
|
+
const TEST_RUN_BRIDGE_STABILITY_WAIT_MS = 500;
|
|
24
|
+
export const waitForBridgeDisconnectOrTimeout = async ({ bridge, connection, timeoutMs, }) => {
|
|
25
|
+
if (bridge.connection !== connection) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return await new Promise((resolve) => {
|
|
29
|
+
const onDisconnected = () => {
|
|
30
|
+
cleanup();
|
|
31
|
+
resolve(true);
|
|
32
|
+
};
|
|
33
|
+
const cleanup = () => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
bridge.off('disconnected', onDisconnected);
|
|
36
|
+
};
|
|
37
|
+
const timer = setTimeout(() => {
|
|
38
|
+
cleanup();
|
|
39
|
+
resolve(bridge.connection !== connection);
|
|
40
|
+
}, timeoutMs);
|
|
41
|
+
bridge.on('disconnected', onDisconnected);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
21
44
|
// ---------------------------------------------------------------------------
|
|
22
45
|
// Internal helpers
|
|
23
46
|
// ---------------------------------------------------------------------------
|
|
@@ -46,8 +69,21 @@ const withPlatformReadyTimeout = async (options) => {
|
|
|
46
69
|
throw error;
|
|
47
70
|
}
|
|
48
71
|
};
|
|
72
|
+
export const waitForStartupCrash = async ({ crashMonitor, detectNativeCrashes, testFilePath, signal, }) => {
|
|
73
|
+
if (!detectNativeCrashes) {
|
|
74
|
+
return await waitForAbort(signal);
|
|
75
|
+
}
|
|
76
|
+
const watch = crashMonitor.watch(testFilePath, 'startup');
|
|
77
|
+
watch.promise.catch(ignorePromiseRejection); // suppress unhandled-rejection when abort wins race
|
|
78
|
+
try {
|
|
79
|
+
return await Promise.race([watch.promise, waitForAbort(signal)]);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
watch.cancel();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
49
85
|
const waitForAppReady = async (base, testFilePath) => {
|
|
50
|
-
const { metroInstance, bridge,
|
|
86
|
+
const { metroInstance, bridge, platformId, bundleStartTimeout, readyTimeout, maxAppRestarts, crashMonitor, detectNativeCrashes, restartAppSession, } = base;
|
|
51
87
|
const logWait = (message, ...args) => sessionLogger.debug(`waitForAppReady: ${message}`, ...args);
|
|
52
88
|
return await waitForMetroBackedAppReady({
|
|
53
89
|
metro: metroInstance,
|
|
@@ -58,7 +94,7 @@ const waitForAppReady = async (base, testFilePath) => {
|
|
|
58
94
|
signal: new AbortController().signal,
|
|
59
95
|
startAttempt: async () => {
|
|
60
96
|
logWait('launching app for %s', testFilePath);
|
|
61
|
-
await
|
|
97
|
+
await restartAppSession();
|
|
62
98
|
logWait('launch request completed, waiting for bridge ready');
|
|
63
99
|
},
|
|
64
100
|
waitForReady: async (signal) => {
|
|
@@ -69,7 +105,7 @@ const waitForAppReady = async (base, testFilePath) => {
|
|
|
69
105
|
// connection from a previous run would resolve the promise before startAttempt
|
|
70
106
|
// even restarts the app — leaving bridge.connection null after the restart.
|
|
71
107
|
await new Promise((resolve, reject) => {
|
|
72
|
-
const onConnected = (
|
|
108
|
+
const onConnected = () => { cleanup(); resolve(); };
|
|
73
109
|
const onAbort = () => { cleanup(); reject(signal.reason ?? new DOMException('Aborted', 'AbortError')); };
|
|
74
110
|
const cleanup = () => {
|
|
75
111
|
bridge.off('connected', onConnected);
|
|
@@ -85,15 +121,13 @@ const waitForAppReady = async (base, testFilePath) => {
|
|
|
85
121
|
logWait('runtime ready received');
|
|
86
122
|
},
|
|
87
123
|
waitForCrash: async (signal) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
watch.cancel();
|
|
96
|
-
}
|
|
124
|
+
logWait('waiting for crash or runtime ready');
|
|
125
|
+
return await waitForStartupCrash({
|
|
126
|
+
crashMonitor,
|
|
127
|
+
detectNativeCrashes,
|
|
128
|
+
testFilePath,
|
|
129
|
+
signal,
|
|
130
|
+
});
|
|
97
131
|
},
|
|
98
132
|
onAttemptStart: () => {
|
|
99
133
|
logWait('beginning launch attempt for %s', testFilePath);
|
|
@@ -242,6 +276,11 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
242
276
|
const getCurrentRunId = () => currentRun?.runId;
|
|
243
277
|
const clientLogCollector = createClientLogCollector();
|
|
244
278
|
const context = { platform };
|
|
279
|
+
const crashArtifactWriter = createCrashArtifactWriter({
|
|
280
|
+
runnerName: platform.name,
|
|
281
|
+
platformId: platform.platformId,
|
|
282
|
+
rootDir: path.join(projectRoot, '.harness', 'crash-reports'),
|
|
283
|
+
});
|
|
245
284
|
const bridge = await createHarnessBridge({
|
|
246
285
|
noServer: true,
|
|
247
286
|
timeout: runtimeConfig.bridgeTimeout,
|
|
@@ -268,6 +307,7 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
268
307
|
work: async (signal) => {
|
|
269
308
|
return await import(platform.runner).then((module) => module.default(platform.config, runtimeConfig, {
|
|
270
309
|
signal,
|
|
310
|
+
crashArtifactWriter,
|
|
271
311
|
})).then((instance) => {
|
|
272
312
|
sessionLogger.debug('platform runner initialized');
|
|
273
313
|
return instance;
|
|
@@ -281,25 +321,38 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
281
321
|
await bridge.dispose();
|
|
282
322
|
throw error;
|
|
283
323
|
}
|
|
284
|
-
const crashArtifactWriter = createCrashArtifactWriter({
|
|
285
|
-
runnerName: platform.name,
|
|
286
|
-
platformId: platform.platformId,
|
|
287
|
-
});
|
|
288
|
-
const appMonitor = platformInstance.createAppMonitor({ crashArtifactWriter });
|
|
289
324
|
const appLaunchOptions = platform.config.appLaunchOptions;
|
|
290
|
-
|
|
325
|
+
let currentAppSession = null;
|
|
326
|
+
const crashMonitor = createCrashMonitor();
|
|
327
|
+
const disposeCurrentAppSession = async () => {
|
|
328
|
+
const session = currentAppSession;
|
|
329
|
+
currentAppSession = null;
|
|
330
|
+
crashMonitor.setAppSession(null);
|
|
331
|
+
if (session) {
|
|
332
|
+
await session.dispose();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
const restartAppSession = async () => {
|
|
336
|
+
await crashMonitor.stop();
|
|
337
|
+
await disposeCurrentAppSession();
|
|
338
|
+
const session = await platformInstance.createAppSession(appLaunchOptions);
|
|
339
|
+
currentAppSession = session;
|
|
340
|
+
crashMonitor.setAppSession(session);
|
|
341
|
+
await crashMonitor.start();
|
|
342
|
+
return session;
|
|
343
|
+
};
|
|
291
344
|
// Pre-build the options that are constant across all app-ready calls;
|
|
292
345
|
// only testFilePath varies per call.
|
|
293
346
|
const appReadyBaseOptions = {
|
|
294
347
|
metroInstance,
|
|
295
348
|
bridge,
|
|
296
|
-
platformInstance,
|
|
297
349
|
platformId: platform.platformId,
|
|
298
350
|
bundleStartTimeout: runtimeConfig.bundleStartTimeout ?? 60000,
|
|
299
351
|
readyTimeout: runtimeConfig.bridgeTimeout,
|
|
300
352
|
maxAppRestarts: runtimeConfig.maxAppRestarts ?? 2,
|
|
301
353
|
crashMonitor,
|
|
302
|
-
|
|
354
|
+
detectNativeCrashes: runtimeConfig.detectNativeCrashes !== false,
|
|
355
|
+
restartAppSession,
|
|
303
356
|
};
|
|
304
357
|
// --- Event listeners ---
|
|
305
358
|
const bridgeEventListener = buildBridgeHookScheduler(hooks, pluginManager, getCurrentRunId);
|
|
@@ -311,6 +364,17 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
311
364
|
};
|
|
312
365
|
const flushClientLogs = () => clientLogCollector.flush();
|
|
313
366
|
const clientLogListener = clientLogCollector.handleEvent;
|
|
367
|
+
const testRunnerEventListeners = new Set();
|
|
368
|
+
const onTestRunnerEvent = (event) => {
|
|
369
|
+
if (event.type === 'test-started' ||
|
|
370
|
+
event.type === 'test-finished' ||
|
|
371
|
+
event.type === 'suite-started' ||
|
|
372
|
+
event.type === 'suite-finished' ||
|
|
373
|
+
event.type === 'file-started' ||
|
|
374
|
+
event.type === 'file-finished') {
|
|
375
|
+
testRunnerEventListeners.forEach((listener) => listener(event));
|
|
376
|
+
}
|
|
377
|
+
};
|
|
314
378
|
const onConnected = (conn) => {
|
|
315
379
|
const runId = getCurrentRunId();
|
|
316
380
|
if (!runId)
|
|
@@ -322,10 +386,14 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
322
386
|
if (!runId)
|
|
323
387
|
return;
|
|
324
388
|
hooks.schedule(() => pluginManager.callHook('runtime:disconnected', { runId, reason: 'bridge-disconnected' }));
|
|
389
|
+
if (runtimeConfig.detectNativeCrashes !== false) {
|
|
390
|
+
crashMonitor.handleBridgeDisconnect();
|
|
391
|
+
}
|
|
325
392
|
};
|
|
326
393
|
bridge.on('connected', onConnected);
|
|
327
394
|
bridge.on('disconnected', onDisconnected);
|
|
328
395
|
bridge.on('event', bridgeEventListener);
|
|
396
|
+
bridge.on('event', onTestRunnerEvent);
|
|
329
397
|
metroInstance.events.addListener(onMetroEvent);
|
|
330
398
|
if (runtimeConfig.forwardClientLogs) {
|
|
331
399
|
metroInstance.events.addListener(clientLogListener);
|
|
@@ -365,10 +433,12 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
365
433
|
bridge.off('connected', onConnected);
|
|
366
434
|
bridge.off('disconnected', onDisconnected);
|
|
367
435
|
bridge.off('event', bridgeEventListener);
|
|
436
|
+
bridge.off('event', onTestRunnerEvent);
|
|
368
437
|
const nativeCoverageConfig = runtimeConfig.coverage?.native?.ios;
|
|
369
438
|
if (nativeCoverageConfig?.pods?.length && platformInstance.collectNativeCoverage) {
|
|
370
439
|
try {
|
|
371
|
-
await
|
|
440
|
+
await crashMonitor.stop();
|
|
441
|
+
await disposeCurrentAppSession();
|
|
372
442
|
const lcovPath = await platformInstance.collectNativeCoverage({
|
|
373
443
|
pods: nativeCoverageConfig.pods,
|
|
374
444
|
outputDir: projectRoot,
|
|
@@ -385,6 +455,7 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
385
455
|
try {
|
|
386
456
|
await Promise.all([
|
|
387
457
|
crashMonitor.dispose(),
|
|
458
|
+
disposeCurrentAppSession(),
|
|
388
459
|
bridge.dispose(),
|
|
389
460
|
platformInstance.dispose(),
|
|
390
461
|
metroInstance.dispose(),
|
|
@@ -419,8 +490,6 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
419
490
|
try {
|
|
420
491
|
await pluginManager.callHook('harness:before-creation', { appLaunchOptions });
|
|
421
492
|
await hooks.drain();
|
|
422
|
-
await appMonitor.start();
|
|
423
|
-
sessionLogger.debug('app monitor started');
|
|
424
493
|
await pluginManager.callHook('harness:before-run', { appLaunchOptions });
|
|
425
494
|
await hooks.drain();
|
|
426
495
|
}
|
|
@@ -436,9 +505,12 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
436
505
|
const ensureAppReady = async (testFilePath) => {
|
|
437
506
|
await hooks.drain();
|
|
438
507
|
sessionLogger.debug('ensuring app is ready for %s', testFilePath);
|
|
439
|
-
if (crashMonitor.isAlive() && bridge.connection !== null &&
|
|
440
|
-
|
|
441
|
-
|
|
508
|
+
if (crashMonitor.isAlive() && bridge.connection !== null && currentAppSession) {
|
|
509
|
+
const state = await currentAppSession.getState();
|
|
510
|
+
if (state.status === 'running') {
|
|
511
|
+
sessionLogger.debug('reusing existing ready app for %s', testFilePath);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
442
514
|
}
|
|
443
515
|
crashMonitor.reset();
|
|
444
516
|
sessionLogger.debug('app not ready, waiting for launch and runtime readiness');
|
|
@@ -450,16 +522,16 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
450
522
|
await hooks.drain();
|
|
451
523
|
await crashMonitor.stop();
|
|
452
524
|
sessionLogger.debug('restarting app (testFile=%s mode=%s)', testFilePath ?? 'n/a', testFilePath ? 'stop-and-ensure-ready' : 'direct-restart');
|
|
525
|
+
await disposeCurrentAppSession();
|
|
453
526
|
if (testFilePath) {
|
|
454
|
-
await
|
|
527
|
+
await ensureAppReady(testFilePath);
|
|
455
528
|
}
|
|
456
529
|
else {
|
|
457
|
-
await platformInstance.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
await ensureAppReady(testFilePath);
|
|
530
|
+
const session = await platformInstance.createAppSession(appLaunchOptions);
|
|
531
|
+
currentAppSession = session;
|
|
532
|
+
crashMonitor.setAppSession(session);
|
|
533
|
+
crashMonitor.reset();
|
|
534
|
+
await crashMonitor.start();
|
|
463
535
|
}
|
|
464
536
|
await hooks.drain();
|
|
465
537
|
sessionLogger.debug('restart completed');
|
|
@@ -470,7 +542,7 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
470
542
|
if (!conn)
|
|
471
543
|
throw new Error('No active app connection');
|
|
472
544
|
sessionLogger.debug('running test file on client: %s', testPath);
|
|
473
|
-
if (
|
|
545
|
+
if (runtimeConfig.detectNativeCrashes === false) {
|
|
474
546
|
const result = await conn.runTests(testPath, { ...options, runner: platform.runner });
|
|
475
547
|
await hooks.drain();
|
|
476
548
|
return result;
|
|
@@ -480,13 +552,30 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
480
552
|
// crash wins the race or cancel() is called after the test run wins.
|
|
481
553
|
crashWatch.promise.catch(ignorePromiseRejection);
|
|
482
554
|
try {
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
555
|
+
const testRunPromise = conn.runTests(testPath, {
|
|
556
|
+
...options,
|
|
557
|
+
runner: platform.runner,
|
|
558
|
+
});
|
|
559
|
+
const result = await Promise.race([testRunPromise, crashWatch.promise]);
|
|
560
|
+
const bridgeDisconnected = await waitForBridgeDisconnectOrTimeout({
|
|
561
|
+
bridge,
|
|
562
|
+
connection: conn,
|
|
563
|
+
timeoutMs: TEST_RUN_BRIDGE_STABILITY_WAIT_MS,
|
|
564
|
+
});
|
|
565
|
+
if (bridgeDisconnected) {
|
|
566
|
+
return await crashWatch.promise;
|
|
567
|
+
}
|
|
487
568
|
await hooks.drain();
|
|
488
569
|
return result;
|
|
489
570
|
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
if (isBridgeDisconnectError(error)) {
|
|
573
|
+
const result = await crashWatch.promise;
|
|
574
|
+
await hooks.drain();
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
throw error;
|
|
578
|
+
}
|
|
490
579
|
finally {
|
|
491
580
|
crashWatch.cancel();
|
|
492
581
|
}
|
|
@@ -494,6 +583,12 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
494
583
|
return {
|
|
495
584
|
config: runtimeConfig,
|
|
496
585
|
context,
|
|
586
|
+
onTestRunnerEvent: (listener) => {
|
|
587
|
+
testRunnerEventListeners.add(listener);
|
|
588
|
+
return () => {
|
|
589
|
+
testRunnerEventListeners.delete(listener);
|
|
590
|
+
};
|
|
591
|
+
},
|
|
497
592
|
runTestFile,
|
|
498
593
|
ensureAppReady,
|
|
499
594
|
restartApp,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { Config, EmittingTestRunnerInterface, Test, TestEvents, TestRunnerOptions, TestWatcher } from 'jest-runner';
|
|
2
|
+
type TestEventListener<Name extends keyof TestEvents> = (eventData: TestEvents[Name]) => void | Promise<void>;
|
|
3
|
+
export default class JestHarness implements EmittingTestRunnerInterface {
|
|
3
4
|
#private;
|
|
4
5
|
readonly isSerial = true;
|
|
6
|
+
readonly supportsEventEmitters: true;
|
|
5
7
|
constructor(globalConfig: Config.GlobalConfig);
|
|
6
|
-
|
|
8
|
+
on<Name extends keyof TestEvents>(eventName: Name, listener: TestEventListener<Name>): () => void;
|
|
9
|
+
runTests(tests: Array<Test>, watcher: TestWatcher, options: TestRunnerOptions): Promise<void>;
|
|
7
10
|
}
|
|
11
|
+
export {};
|
|
8
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,2BAA2B,EAC3B,IAAI,EACJ,UAAU,EACV,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;AAMrB,KAAK,iBAAiB,CAAC,IAAI,SAAS,MAAM,UAAU,IAAI,CACtD,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,KACxB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,CAAC,OAAO,OAAO,WAAY,YAAW,2BAA2B;;IACrE,QAAQ,CAAC,QAAQ,QAAQ;IACzB,QAAQ,CAAC,qBAAqB,EAAG,IAAI,CAAU;gBAMnC,YAAY,EAAE,MAAM,CAAC,YAAY;IAI7C,EAAE,CAAC,IAAI,SAAS,MAAM,UAAU,EAC9B,SAAS,EAAE,IAAI,EACf,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAChC,MAAM,IAAI;IAaP,QAAQ,CACZ,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC;CA6CjB"}
|
package/dist/index.js
CHANGED
|
@@ -4,12 +4,25 @@ import { HarnessError } from '@react-native-harness/tools';
|
|
|
4
4
|
import { getErrorMessage } from './logs.js';
|
|
5
5
|
export default class JestHarness {
|
|
6
6
|
isSerial = true;
|
|
7
|
+
supportsEventEmitters = true;
|
|
7
8
|
#globalConfig;
|
|
8
9
|
#session = null;
|
|
10
|
+
#listeners = new Map();
|
|
9
11
|
constructor(globalConfig) {
|
|
10
12
|
this.#globalConfig = globalConfig;
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
on(eventName, listener) {
|
|
15
|
+
const listeners = this.#listeners.get(eventName) ?? new Set();
|
|
16
|
+
listeners.add(listener);
|
|
17
|
+
this.#listeners.set(eventName, listeners);
|
|
18
|
+
return () => {
|
|
19
|
+
listeners.delete(listener);
|
|
20
|
+
if (listeners.size === 0) {
|
|
21
|
+
this.#listeners.delete(eventName);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async runTests(tests, watcher, options) {
|
|
13
26
|
if (!options.serial) {
|
|
14
27
|
throw new Error('Parallel test running is not supported');
|
|
15
28
|
}
|
|
@@ -18,7 +31,7 @@ export default class JestHarness {
|
|
|
18
31
|
if (!this.#session) {
|
|
19
32
|
this.#session = await createHarnessSession(this.#globalConfig);
|
|
20
33
|
}
|
|
21
|
-
await executeRun(this.#session, tests, watcher,
|
|
34
|
+
await executeRun(this.#session, tests, watcher, this.#emit, this.#globalConfig);
|
|
22
35
|
}
|
|
23
36
|
catch (error) {
|
|
24
37
|
if (error instanceof HarnessError) {
|
|
@@ -33,4 +46,13 @@ export default class JestHarness {
|
|
|
33
46
|
}
|
|
34
47
|
}
|
|
35
48
|
}
|
|
49
|
+
#emit = async (eventName, ...eventData) => {
|
|
50
|
+
const listeners = this.#listeners.get(eventName);
|
|
51
|
+
if (!listeners) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const listener of listeners) {
|
|
55
|
+
await listener(eventData);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
36
58
|
}
|
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;AACjE,OAAO,KAAK,EACV,eAAe,IAAI,sBAAsB,EAE1C,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAuD3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC;IACZ,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,sBAAsB,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,
|
|
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;AACjE,OAAO,KAAK,EACV,eAAe,IAAI,sBAAsB,EAE1C,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAuD3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC;IACZ,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,sBAAsB,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,kBAmFhC,CAAC"}
|
package/dist/run.js
CHANGED
|
@@ -67,6 +67,7 @@ export const runHarnessTestFile = async ({ testPath, globalConfig, projectConfig
|
|
|
67
67
|
? `${errorMessage}${codeFrame ? `\n\n${codeFrame.content}` : ''}`
|
|
68
68
|
: undefined,
|
|
69
69
|
title: test.name,
|
|
70
|
+
fullName: test.fullName,
|
|
70
71
|
status: test.status,
|
|
71
72
|
location: codeFrame?.location
|
|
72
73
|
? { column: codeFrame.location.column, line: codeFrame.location.row }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TestResult as JestTestResult } from '@jest/test-result';
|
|
2
|
+
export declare const getHarnessTestFilePlatform: (testPath: string, knownPlatformIds: ReadonlySet<string>) => string | null;
|
|
3
|
+
export declare const shouldRunHarnessTestFile: (testPath: string, platformId: string, knownPlatformIds: ReadonlySet<string>) => boolean;
|
|
4
|
+
export declare const createPlatformSkippedTestResult: (testPath: string) => JestTestResult;
|
|
5
|
+
//# sourceMappingURL=test-file-platform-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-file-platform-filter.d.ts","sourceRoot":"","sources":["../src/test-file-platform-filter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMtE,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,EAChB,kBAAkB,WAAW,CAAC,MAAM,CAAC,KACpC,MAAM,GAAG,IAQX,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,UAAU,MAAM,EAChB,YAAY,MAAM,EAClB,kBAAkB,WAAW,CAAC,MAAM,CAAC,KACpC,OAGF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,UAAU,MAAM,KACf,cAyBF,CAAC"}
|