@react-native-harness/jest 1.0.0 → 1.1.0-rc.2
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__/crash-supervisor.test.d.ts +2 -0
- package/dist/__tests__/crash-supervisor.test.d.ts.map +1 -0
- package/dist/__tests__/crash-supervisor.test.js +151 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +54 -0
- package/dist/__tests__/harness-cache.test.d.ts +2 -0
- package/dist/__tests__/harness-cache.test.d.ts.map +1 -0
- package/dist/__tests__/harness-cache.test.js +51 -0
- package/dist/__tests__/harness.test.d.ts +2 -0
- package/dist/__tests__/harness.test.d.ts.map +1 -0
- package/dist/__tests__/harness.test.js +399 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +42 -0
- package/dist/cli-args.d.ts +1 -0
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +5 -0
- package/dist/crash-supervisor.d.ts +21 -0
- package/dist/crash-supervisor.d.ts.map +1 -0
- package/dist/crash-supervisor.js +210 -0
- package/dist/errors.d.ts +9 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +43 -8
- package/dist/harness.d.ts +30 -8
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +440 -98
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -57
- package/dist/logs.d.ts +1 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +3 -0
- package/dist/run.d.ts +7 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +13 -8
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +18 -6
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/src/__tests__/crash-supervisor.test.ts +200 -0
- package/src/__tests__/errors.test.ts +70 -0
- package/src/__tests__/harness-cache.test.ts +74 -0
- package/src/__tests__/harness.test.ts +547 -0
- package/src/__tests__/index.test.ts +61 -0
- package/src/cli-args.ts +6 -0
- package/src/crash-supervisor.ts +321 -0
- package/src/env.d.ts +1 -1
- package/src/errors.ts +68 -8
- package/src/harness.ts +566 -120
- package/src/index.ts +276 -66
- package/src/logs.ts +4 -0
- package/src/run.ts +20 -10
- package/src/setup.ts +22 -6
- package/tsconfig.json +3 -3
- package/tsconfig.lib.json +6 -6
- package/dist/cli-args.test.d.ts +0 -2
- package/dist/cli-args.test.d.ts.map +0 -1
- package/dist/cli-args.test.js +0 -1
- package/dist/crash-monitor.d.ts +0 -16
- package/dist/crash-monitor.d.ts.map +0 -1
- package/dist/crash-monitor.js +0 -75
- package/dist/event-mapper.d.ts +0 -16
- package/dist/event-mapper.d.ts.map +0 -1
- package/dist/event-mapper.js +0 -120
- package/dist/global-setup.d.ts +0 -3
- package/dist/global-setup.d.ts.map +0 -1
- package/dist/global-setup.js +0 -60
- package/dist/global-teardown.d.ts +0 -3
- package/dist/global-teardown.d.ts.map +0 -1
- package/dist/global-teardown.js +0 -11
- package/dist/harness-reporter.d.ts +0 -8
- package/dist/harness-reporter.d.ts.map +0 -1
- package/dist/harness-reporter.js +0 -12
- package/dist/jest-options.d.ts +0 -2
- package/dist/jest-options.d.ts.map +0 -1
- package/dist/jest-options.js +0 -1
- package/dist/types.d.ts +0 -17
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
- package/src/crash-monitor.ts +0 -109
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import JestHarness from '../index.js';
|
|
3
|
+
import { StartupStallError } from '../errors.js';
|
|
4
|
+
describe('JestHarness', () => {
|
|
5
|
+
it('reports StartupStallError without a stack trace', async () => {
|
|
6
|
+
const runner = new JestHarness({});
|
|
7
|
+
const onFailure = vi.fn();
|
|
8
|
+
const harness = {
|
|
9
|
+
ensureAppReady: vi
|
|
10
|
+
.fn()
|
|
11
|
+
.mockRejectedValue(new StartupStallError(1500, 3)),
|
|
12
|
+
callHook: vi.fn(async () => undefined),
|
|
13
|
+
setRunState: vi.fn(),
|
|
14
|
+
getRunState: vi.fn(() => null),
|
|
15
|
+
crashSupervisor: {
|
|
16
|
+
beginTestRun: vi.fn(),
|
|
17
|
+
waitForCrash: vi.fn(),
|
|
18
|
+
cancelCrashWaiters: vi.fn(),
|
|
19
|
+
reset: vi.fn(),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
await runner._createInBandTestRun([
|
|
23
|
+
{
|
|
24
|
+
path: '/tmp/example.harness.ts',
|
|
25
|
+
context: {
|
|
26
|
+
config: {},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
], {
|
|
30
|
+
isInterrupted: () => false,
|
|
31
|
+
}, harness, {
|
|
32
|
+
detectNativeCrashes: true,
|
|
33
|
+
resetEnvironmentBetweenTestFiles: false,
|
|
34
|
+
}, () => Promise.resolve(), () => Promise.resolve(), onFailure);
|
|
35
|
+
expect(onFailure).toHaveBeenCalledWith(expect.objectContaining({
|
|
36
|
+
path: '/tmp/example.harness.ts',
|
|
37
|
+
}), {
|
|
38
|
+
message: 'The app did not request its Metro bundle after 3 launch attempts within 1500ms. Last Metro status: unknown.',
|
|
39
|
+
stack: '',
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
package/dist/cli-args.d.ts
CHANGED
package/dist/cli-args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAAO,cAoBvC,CAAC"}
|
package/dist/cli-args.js
CHANGED
|
@@ -5,6 +5,10 @@ export const getAdditionalCliArgs = () => {
|
|
|
5
5
|
.option('harnessRunner', {
|
|
6
6
|
type: 'string',
|
|
7
7
|
description: 'Specify which Harness runner to use',
|
|
8
|
+
})
|
|
9
|
+
.option('metroPort', {
|
|
10
|
+
type: 'number',
|
|
11
|
+
description: 'Override the Metro bundler port',
|
|
8
12
|
})
|
|
9
13
|
.strict(false)
|
|
10
14
|
.help(false)
|
|
@@ -13,5 +17,6 @@ export const getAdditionalCliArgs = () => {
|
|
|
13
17
|
.parseSync();
|
|
14
18
|
return {
|
|
15
19
|
harnessRunner: argv.harnessRunner,
|
|
20
|
+
metroPort: argv.metroPort,
|
|
16
21
|
};
|
|
17
22
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AppMonitor, type HarnessPlatformRunner } from '@react-native-harness/platforms';
|
|
2
|
+
export type CrashSupervisorState = 'idle' | 'launching' | 'ready' | 'running' | 'disposing';
|
|
3
|
+
export type CrashSupervisor = {
|
|
4
|
+
setActiveTestFile: (testFilePath: string | null) => void;
|
|
5
|
+
beginLaunch: (testFilePath: string) => void;
|
|
6
|
+
markReady: () => void;
|
|
7
|
+
beginTestRun: (testFilePath: string) => void;
|
|
8
|
+
stop: () => Promise<void>;
|
|
9
|
+
start: () => Promise<void>;
|
|
10
|
+
waitForCrash: (testFilePath: string) => Promise<never>;
|
|
11
|
+
isReady: () => boolean;
|
|
12
|
+
cancelCrashWaiters: () => void;
|
|
13
|
+
reset: () => void;
|
|
14
|
+
dispose: () => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export type CrashSupervisorOptions = {
|
|
17
|
+
appMonitor: AppMonitor;
|
|
18
|
+
platformRunner: HarnessPlatformRunner;
|
|
19
|
+
};
|
|
20
|
+
export declare const createCrashSupervisor: ({ appMonitor, platformRunner, }: CrashSupervisorOptions) => CrashSupervisor;
|
|
21
|
+
//# sourceMappingURL=crash-supervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-supervisor.d.ts","sourceRoot":"","sources":["../src/crash-supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAIf,KAAK,qBAAqB,EAC3B,MAAM,iCAAiC,CAAC;AAUzC,MAAM,MAAM,oBAAoB,GAC5B,MAAM,GACN,WAAW,GACX,OAAO,GACP,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,MAAM,eAAe,GAAG;IAC5B,iBAAiB,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACvD,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,qBAAqB,CAAC;CACvC,CAAC;AAmCF,eAAO,MAAM,qBAAqB,GAAI,iCAGnC,sBAAsB,KAAG,eAkP3B,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { NativeCrashError, } from './errors.js';
|
|
2
|
+
import { logger } from '@react-native-harness/tools';
|
|
3
|
+
const crashLogger = logger.child('crash');
|
|
4
|
+
const getCrashPhase = (state) => state === 'running' ? 'execution' : 'startup';
|
|
5
|
+
const mergeCrashDetails = (phase, initialDetails, enrichedDetails, fallbackSummary) => ({
|
|
6
|
+
phase,
|
|
7
|
+
source: enrichedDetails?.source ?? initialDetails?.source,
|
|
8
|
+
summary: enrichedDetails?.summary ?? initialDetails?.summary ?? fallbackSummary,
|
|
9
|
+
signal: enrichedDetails?.signal ?? initialDetails?.signal,
|
|
10
|
+
exceptionType: enrichedDetails?.exceptionType ?? initialDetails?.exceptionType,
|
|
11
|
+
processName: enrichedDetails?.processName ?? initialDetails?.processName,
|
|
12
|
+
pid: enrichedDetails?.pid ?? initialDetails?.pid,
|
|
13
|
+
stackTrace: enrichedDetails?.stackTrace ?? initialDetails?.stackTrace,
|
|
14
|
+
rawLines: enrichedDetails?.rawLines ?? initialDetails?.rawLines,
|
|
15
|
+
artifactType: enrichedDetails?.artifactType ?? initialDetails?.artifactType,
|
|
16
|
+
artifactPath: enrichedDetails?.artifactPath ?? initialDetails?.artifactPath,
|
|
17
|
+
});
|
|
18
|
+
export const createCrashSupervisor = ({ appMonitor, platformRunner, }) => {
|
|
19
|
+
let state = 'idle';
|
|
20
|
+
let activeTestFilePath = null;
|
|
21
|
+
let crashRejectors = new Set();
|
|
22
|
+
let disposed = false;
|
|
23
|
+
let monitoring = true;
|
|
24
|
+
let isResolvingCrash = false;
|
|
25
|
+
const getCrashDetailsProvider = () => {
|
|
26
|
+
if ('getCrashDetails' in appMonitor) {
|
|
27
|
+
return appMonitor;
|
|
28
|
+
}
|
|
29
|
+
if (platformRunner.getCrashDetails) {
|
|
30
|
+
return platformRunner;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const rejectCrashWaiters = (testFilePath, details) => {
|
|
35
|
+
const error = new NativeCrashError(testFilePath, details);
|
|
36
|
+
for (const reject of crashRejectors) {
|
|
37
|
+
reject(error);
|
|
38
|
+
}
|
|
39
|
+
crashRejectors = new Set();
|
|
40
|
+
};
|
|
41
|
+
const handleCrash = async (reason, details) => {
|
|
42
|
+
if (isResolvingCrash ||
|
|
43
|
+
(state !== 'launching' && state !== 'running' && state !== 'ready')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!activeTestFilePath) {
|
|
47
|
+
crashLogger.debug('ignoring crash signal without active test: %s', reason);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
isResolvingCrash = true;
|
|
51
|
+
crashLogger.debug('native crash detected during %s (state=%s reason=%s)', activeTestFilePath, state, reason || '(none)');
|
|
52
|
+
for (const line of details?.rawLines ?? []) {
|
|
53
|
+
crashLogger.debug('%s', line);
|
|
54
|
+
}
|
|
55
|
+
const phase = getCrashPhase(state);
|
|
56
|
+
const testFilePath = activeTestFilePath;
|
|
57
|
+
state = 'idle';
|
|
58
|
+
try {
|
|
59
|
+
const enrichedDetails = await getCrashDetailsProvider()?.getCrashDetails?.({
|
|
60
|
+
processName: details?.processName,
|
|
61
|
+
pid: details?.pid,
|
|
62
|
+
occurredAt: Date.now(),
|
|
63
|
+
});
|
|
64
|
+
const mergedDetails = mergeCrashDetails(phase, details, enrichedDetails, reason);
|
|
65
|
+
crashLogger.debug('crash details: %o', {
|
|
66
|
+
phase: mergedDetails.phase,
|
|
67
|
+
source: mergedDetails.source,
|
|
68
|
+
summary: mergedDetails.summary,
|
|
69
|
+
signal: mergedDetails.signal,
|
|
70
|
+
exceptionType: mergedDetails.exceptionType,
|
|
71
|
+
processName: mergedDetails.processName,
|
|
72
|
+
pid: mergedDetails.pid,
|
|
73
|
+
});
|
|
74
|
+
rejectCrashWaiters(testFilePath, mergedDetails);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
isResolvingCrash = false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const confirmCrash = async (reason, details) => {
|
|
81
|
+
if (disposed || !monitoring || state === 'disposing') {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const isRunning = await platformRunner.isAppRunning();
|
|
86
|
+
if (!isRunning) {
|
|
87
|
+
void handleCrash(reason, details);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
crashLogger.debug('crash confirmation failed', error);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const appMonitorListener = (event) => {
|
|
95
|
+
if (disposed || !monitoring) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (event.type === 'app_started') {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (event.type === 'app_exited') {
|
|
102
|
+
const details = {
|
|
103
|
+
source: event.crashDetails?.source ?? event.source,
|
|
104
|
+
summary: event.crashDetails?.summary,
|
|
105
|
+
signal: event.crashDetails?.signal,
|
|
106
|
+
exceptionType: event.crashDetails?.exceptionType,
|
|
107
|
+
processName: event.crashDetails?.processName,
|
|
108
|
+
pid: event.crashDetails?.pid ?? event.pid,
|
|
109
|
+
stackTrace: event.crashDetails?.stackTrace,
|
|
110
|
+
rawLines: event.crashDetails?.rawLines ??
|
|
111
|
+
(event.line ? [event.line] : undefined),
|
|
112
|
+
};
|
|
113
|
+
if (event.isConfirmed ?? event.source === 'polling') {
|
|
114
|
+
void handleCrash('', details);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
void confirmCrash('', details);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (event.type === 'possible_crash') {
|
|
122
|
+
const details = {
|
|
123
|
+
source: event.crashDetails?.source ?? event.source,
|
|
124
|
+
summary: event.crashDetails?.summary,
|
|
125
|
+
signal: event.crashDetails?.signal,
|
|
126
|
+
exceptionType: event.crashDetails?.exceptionType,
|
|
127
|
+
processName: event.crashDetails?.processName,
|
|
128
|
+
pid: event.crashDetails?.pid ?? event.pid,
|
|
129
|
+
stackTrace: event.crashDetails?.stackTrace,
|
|
130
|
+
rawLines: event.crashDetails?.rawLines ??
|
|
131
|
+
(event.line ? [event.line] : undefined),
|
|
132
|
+
};
|
|
133
|
+
if (event.isConfirmed) {
|
|
134
|
+
void handleCrash(`possible crash signal (${event.source ?? 'unknown'})`, details);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
void confirmCrash(`possible crash signal (${event.source ?? 'unknown'})`, details);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
appMonitor.addListener(appMonitorListener);
|
|
142
|
+
const setActiveTestFile = (testFilePath) => {
|
|
143
|
+
activeTestFilePath = testFilePath;
|
|
144
|
+
};
|
|
145
|
+
const beginLaunch = (testFilePath) => {
|
|
146
|
+
activeTestFilePath = testFilePath;
|
|
147
|
+
state = 'launching';
|
|
148
|
+
};
|
|
149
|
+
const markReady = () => {
|
|
150
|
+
if (state !== 'disposing') {
|
|
151
|
+
state = 'ready';
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const beginTestRun = (testFilePath) => {
|
|
155
|
+
activeTestFilePath = testFilePath;
|
|
156
|
+
state = 'running';
|
|
157
|
+
};
|
|
158
|
+
const stop = async () => {
|
|
159
|
+
monitoring = false;
|
|
160
|
+
await appMonitor.stop();
|
|
161
|
+
};
|
|
162
|
+
const start = async () => {
|
|
163
|
+
monitoring = true;
|
|
164
|
+
await appMonitor.start();
|
|
165
|
+
};
|
|
166
|
+
const waitForCrash = (testFilePath) => new Promise((_, reject) => {
|
|
167
|
+
if (disposed) {
|
|
168
|
+
reject(new NativeCrashError(testFilePath, {
|
|
169
|
+
phase: 'startup',
|
|
170
|
+
source: 'polling',
|
|
171
|
+
summary: 'Crash supervisor disposed while waiting for app startup.',
|
|
172
|
+
}));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
crashRejectors.add(reject);
|
|
176
|
+
});
|
|
177
|
+
const isReady = () => state === 'ready' || state === 'running';
|
|
178
|
+
const cancelCrashWaiters = () => {
|
|
179
|
+
crashRejectors = new Set();
|
|
180
|
+
};
|
|
181
|
+
const reset = () => {
|
|
182
|
+
cancelCrashWaiters();
|
|
183
|
+
isResolvingCrash = false;
|
|
184
|
+
if (state !== 'disposing') {
|
|
185
|
+
state = 'idle';
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const dispose = async () => {
|
|
189
|
+
disposed = true;
|
|
190
|
+
monitoring = false;
|
|
191
|
+
state = 'disposing';
|
|
192
|
+
crashRejectors = new Set();
|
|
193
|
+
isResolvingCrash = false;
|
|
194
|
+
appMonitor.removeListener(appMonitorListener);
|
|
195
|
+
await appMonitor.dispose();
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
setActiveTestFile,
|
|
199
|
+
beginLaunch,
|
|
200
|
+
markReady,
|
|
201
|
+
beginTestRun,
|
|
202
|
+
stop,
|
|
203
|
+
start,
|
|
204
|
+
waitForCrash,
|
|
205
|
+
isReady,
|
|
206
|
+
cancelCrashWaiters,
|
|
207
|
+
reset,
|
|
208
|
+
dispose,
|
|
209
|
+
};
|
|
210
|
+
};
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { HarnessError } from '@react-native-harness/tools';
|
|
2
|
+
import type { AppCrashDetails } from '@react-native-harness/platforms';
|
|
3
|
+
export { StartupStallError, type StartupStallCode, type StartupStallDetails, } from '@react-native-harness/bundler-metro';
|
|
2
4
|
export declare class NoRunnerSpecifiedError extends HarnessError {
|
|
3
5
|
constructor();
|
|
4
6
|
}
|
|
@@ -9,12 +11,15 @@ export declare class RunnerNotFoundError extends HarnessError {
|
|
|
9
11
|
export declare class InitializationTimeoutError extends HarnessError {
|
|
10
12
|
constructor();
|
|
11
13
|
}
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
export type NativeCrashPhase = 'startup' | 'execution';
|
|
15
|
+
export type NativeCrashDetails = AppCrashDetails & {
|
|
16
|
+
phase: NativeCrashPhase;
|
|
17
|
+
};
|
|
15
18
|
export declare class NativeCrashError extends HarnessError {
|
|
16
19
|
readonly testFilePath: string;
|
|
20
|
+
readonly details: NativeCrashDetails;
|
|
17
21
|
readonly lastKnownTest?: string | undefined;
|
|
18
|
-
constructor(testFilePath: string, lastKnownTest?: string | undefined);
|
|
22
|
+
constructor(testFilePath: string, details: NativeCrashDetails, lastKnownTest?: string | undefined);
|
|
23
|
+
get phase(): NativeCrashPhase;
|
|
19
24
|
}
|
|
20
25
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,qCAAqC,CAAC;AAE7C,qBAAa,sBAAuB,SAAQ,YAAY;;CAKvD;AAED,qBAAa,mBAAoB,SAAQ,YAAY;aACvB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI/C;AAED,qBAAa,0BAA2B,SAAQ,YAAY;;CAK3D;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,CAAC;AAEvD,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG;IACjD,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAsDF,qBAAa,gBAAiB,SAAQ,YAAY;aAE9B,YAAY,EAAE,MAAM;aACpB,OAAO,EAAE,kBAAkB;aAC3B,aAAa,CAAC,EAAE,MAAM;gBAFtB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,kBAAkB,EAC3B,aAAa,CAAC,EAAE,MAAM,YAAA;IAOxC,IAAI,KAAK,qBAER;CACF"}
|
package/dist/errors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HarnessError } from '@react-native-harness/tools';
|
|
2
|
+
export { StartupStallError, } from '@react-native-harness/bundler-metro';
|
|
2
3
|
export class NoRunnerSpecifiedError extends HarnessError {
|
|
3
4
|
constructor() {
|
|
4
5
|
super('No runner specified');
|
|
@@ -19,20 +20,54 @@ export class InitializationTimeoutError extends HarnessError {
|
|
|
19
20
|
this.name = 'InitializationTimeoutError';
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const buildNativeCrashMessage = ({ phase, summary, signal, exceptionType, processName, pid, stackTrace, artifactType, }) => {
|
|
24
|
+
const lines = [
|
|
25
|
+
phase === 'startup'
|
|
26
|
+
? 'The native app crashed while preparing to run this test file.'
|
|
27
|
+
: 'The native app crashed during test execution.',
|
|
28
|
+
];
|
|
29
|
+
const hasCrashBlock = summary?.includes('\n') ?? false;
|
|
30
|
+
const shouldRenderSummary = Boolean(summary) &&
|
|
31
|
+
!(!hasCrashBlock &&
|
|
32
|
+
artifactType === 'ios-crash-report');
|
|
33
|
+
if (shouldRenderSummary && summary) {
|
|
34
|
+
lines.push('');
|
|
35
|
+
lines.push(summary);
|
|
27
36
|
}
|
|
28
|
-
|
|
37
|
+
if (!hasCrashBlock && signal) {
|
|
38
|
+
lines.push(`Signal: ${signal}`);
|
|
39
|
+
}
|
|
40
|
+
if (!hasCrashBlock && exceptionType) {
|
|
41
|
+
lines.push(`Exception: ${exceptionType}`);
|
|
42
|
+
}
|
|
43
|
+
if (!hasCrashBlock && processName && pid !== undefined) {
|
|
44
|
+
lines.push(`Process: ${processName} (pid ${pid})`);
|
|
45
|
+
}
|
|
46
|
+
else if (!hasCrashBlock && processName) {
|
|
47
|
+
lines.push(`Process: ${processName}`);
|
|
48
|
+
}
|
|
49
|
+
else if (!hasCrashBlock && pid !== undefined) {
|
|
50
|
+
lines.push(`PID: ${pid}`);
|
|
51
|
+
}
|
|
52
|
+
if (!hasCrashBlock && stackTrace && stackTrace.length > 0) {
|
|
53
|
+
lines.push('');
|
|
54
|
+
lines.push(...stackTrace.map((line) => ` ${line}`));
|
|
55
|
+
}
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
};
|
|
29
58
|
export class NativeCrashError extends HarnessError {
|
|
30
59
|
testFilePath;
|
|
60
|
+
details;
|
|
31
61
|
lastKnownTest;
|
|
32
|
-
constructor(testFilePath, lastKnownTest) {
|
|
33
|
-
super(
|
|
62
|
+
constructor(testFilePath, details, lastKnownTest) {
|
|
63
|
+
super(buildNativeCrashMessage(details));
|
|
34
64
|
this.testFilePath = testFilePath;
|
|
65
|
+
this.details = details;
|
|
35
66
|
this.lastKnownTest = lastKnownTest;
|
|
36
67
|
this.name = 'NativeCrashError';
|
|
68
|
+
this.stack = `${this.name}: ${this.message.split('\n')[0]}`;
|
|
69
|
+
}
|
|
70
|
+
get phase() {
|
|
71
|
+
return this.details.phase;
|
|
37
72
|
}
|
|
38
73
|
}
|
package/dist/harness.d.ts
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
import { BridgeServer } from '@react-native-harness/bridge/server';
|
|
2
2
|
import { HarnessContext, TestExecutionOptions, TestSuiteResult } from '@react-native-harness/bridge';
|
|
3
|
-
import { HarnessPlatform, HarnessPlatformRunner } from '@react-native-harness/platforms';
|
|
4
|
-
import {
|
|
3
|
+
import { type AppLaunchOptions, HarnessPlatform, HarnessPlatformRunner } from '@react-native-harness/platforms';
|
|
4
|
+
import { type MetroInstance } from '@react-native-harness/bundler-metro';
|
|
5
|
+
import { type HarnessPluginManager, type HarnessRunStatus, type HarnessRunSummary } from '@react-native-harness/plugins';
|
|
5
6
|
import { Config as HarnessConfig } from '@react-native-harness/config';
|
|
6
|
-
import {
|
|
7
|
+
import { type CrashSupervisor } from './crash-supervisor.js';
|
|
7
8
|
export type HarnessRunTestsOptions = Exclude<TestExecutionOptions, 'platform'>;
|
|
9
|
+
export type HarnessRunState = {
|
|
10
|
+
runId: string;
|
|
11
|
+
startTime: number;
|
|
12
|
+
testFiles: string[];
|
|
13
|
+
watchMode: boolean;
|
|
14
|
+
coverageEnabled: boolean;
|
|
15
|
+
summary?: HarnessRunSummary;
|
|
16
|
+
status?: HarnessRunStatus;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
};
|
|
8
19
|
export type Harness = {
|
|
9
20
|
context: HarnessContext;
|
|
10
21
|
runTests: (path: string, options: HarnessRunTestsOptions) => Promise<TestSuiteResult>;
|
|
11
|
-
|
|
22
|
+
ensureAppReady: (testFilePath: string) => Promise<void>;
|
|
23
|
+
restart: (testFilePath?: string) => Promise<void>;
|
|
12
24
|
dispose: () => Promise<void>;
|
|
13
|
-
|
|
25
|
+
crashSupervisor: CrashSupervisor;
|
|
26
|
+
callHook: HarnessPluginManager<HarnessConfig, HarnessPlatform>['callHook'];
|
|
27
|
+
setRunState: (runState: HarnessRunState | null) => void;
|
|
28
|
+
getRunState: () => HarnessRunState | null;
|
|
14
29
|
};
|
|
30
|
+
export declare const maybeLogMetroCacheReuse: (config: HarnessConfig, platform: HarnessPlatform, projectRoot: string) => void;
|
|
15
31
|
export declare const waitForAppReady: (options: {
|
|
16
|
-
|
|
32
|
+
metroInstance: MetroInstance;
|
|
17
33
|
serverBridge: BridgeServer;
|
|
18
34
|
platformInstance: HarnessPlatformRunner;
|
|
35
|
+
platformId: string;
|
|
19
36
|
bundleStartTimeout: number;
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
readyTimeout: number;
|
|
38
|
+
maxAppRestarts: number;
|
|
39
|
+
testFilePath: string;
|
|
40
|
+
crashSupervisor: CrashSupervisor;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
appLaunchOptions?: AppLaunchOptions;
|
|
43
|
+
launchApp?: () => Promise<void>;
|
|
22
44
|
}) => Promise<void>;
|
|
23
45
|
export declare const getHarness: (config: HarnessConfig, platform: HarnessPlatform, projectRoot: string) => Promise<Harness>;
|
|
24
46
|
//# sourceMappingURL=harness.d.ts.map
|
package/dist/harness.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,EAGd,oBAAoB,EACpB,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,gBAAgB,EACrB,eAAe,EACf,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;AAE/E,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,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,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,OAAO,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,oBAAoB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3E,WAAW,EAAE,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IACxD,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,QAAQ,aAAa,EACrB,UAAU,eAAe,EACzB,aAAa,MAAM,KAClB,IAOF,CAAC;AAqBF,eAAO,MAAM,eAAe,GAAU,SAAS;IAC7C,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,qBAAqB,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC,KAAG,OAAO,CAAC,IAAI,CA2Ef,CAAC;AAkeF,eAAO,MAAM,UAAU,GACrB,QAAQ,aAAa,EACrB,UAAU,eAAe,EACzB,aAAa,MAAM,KAClB,OAAO,CAAC,OAAO,CAsBjB,CAAC"}
|