@react-native-harness/jest 1.4.0-rc.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/bridge.test.js +6 -2
- package/dist/__tests__/crash-monitor.test.js +64 -0
- package/dist/__tests__/environment-reset.test.d.ts +2 -0
- package/dist/__tests__/environment-reset.test.d.ts.map +1 -0
- package/dist/__tests__/environment-reset.test.js +238 -0
- package/dist/__tests__/execute-run.test.js +250 -4
- package/dist/__tests__/format-harness-error.test.d.ts +2 -0
- package/dist/__tests__/format-harness-error.test.d.ts.map +1 -0
- package/dist/__tests__/format-harness-error.test.js +27 -0
- package/dist/__tests__/harness-cache.test.js +62 -41
- package/dist/__tests__/harness-session-signal.test.d.ts +2 -0
- package/dist/__tests__/harness-session-signal.test.d.ts.map +1 -0
- package/dist/__tests__/harness-session-signal.test.js +298 -0
- package/dist/__tests__/harness-session.test.js +161 -2
- package/dist/__tests__/index.test.js +3 -1
- package/dist/__tests__/metro-port.test.js +2 -0
- package/dist/__tests__/run.test.d.ts +2 -0
- package/dist/__tests__/run.test.d.ts.map +1 -0
- package/dist/__tests__/run.test.js +48 -0
- package/dist/crash-monitor.d.ts +7 -0
- package/dist/crash-monitor.d.ts.map +1 -1
- package/dist/crash-monitor.js +19 -0
- package/dist/diagnostics/__tests__/derivers.test.d.ts +2 -0
- package/dist/diagnostics/__tests__/derivers.test.d.ts.map +1 -0
- package/dist/diagnostics/__tests__/derivers.test.js +248 -0
- package/dist/diagnostics/__tests__/report.test.d.ts +2 -0
- package/dist/diagnostics/__tests__/report.test.d.ts.map +1 -0
- package/dist/diagnostics/__tests__/report.test.js +97 -0
- package/dist/diagnostics/derivers.d.ts +24 -0
- package/dist/diagnostics/derivers.d.ts.map +1 -0
- package/dist/diagnostics/derivers.js +146 -0
- package/dist/diagnostics/index.d.ts +3 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +2 -0
- package/dist/diagnostics/report.d.ts +7 -0
- package/dist/diagnostics/report.d.ts.map +1 -0
- package/dist/diagnostics/report.js +84 -0
- package/dist/environment-reset.d.ts +51 -0
- package/dist/environment-reset.d.ts.map +1 -0
- package/dist/environment-reset.js +83 -0
- package/dist/execute-run.d.ts.map +1 -1
- package/dist/execute-run.js +121 -69
- package/dist/format-harness-error.d.ts +5 -0
- package/dist/format-harness-error.d.ts.map +1 -0
- package/dist/format-harness-error.js +43 -0
- package/dist/harness-session.d.ts +26 -1
- package/dist/harness-session.d.ts.map +1 -1
- package/dist/harness-session.js +260 -87
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +21 -4
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/__tests__/bridge.test.ts +8 -3
- package/src/__tests__/crash-monitor.test.ts +84 -0
- package/src/__tests__/environment-reset.test.ts +308 -0
- package/src/__tests__/execute-run.test.ts +327 -4
- package/src/__tests__/format-harness-error.test.ts +32 -0
- package/src/__tests__/harness-cache.test.ts +91 -0
- package/src/__tests__/harness-session.test.ts +211 -1
- package/src/__tests__/index.test.ts +3 -1
- package/src/__tests__/metro-port.test.ts +2 -0
- package/src/__tests__/run.test.ts +74 -0
- package/src/crash-monitor.ts +28 -0
- package/src/diagnostics/__tests__/derivers.test.ts +300 -0
- package/src/diagnostics/__tests__/report.test.ts +128 -0
- package/src/diagnostics/derivers.ts +170 -0
- package/src/diagnostics/index.ts +2 -0
- package/src/diagnostics/report.ts +124 -0
- package/src/environment-reset.ts +138 -0
- package/src/execute-run.ts +136 -77
- package/src/format-harness-error.ts +73 -0
- package/src/harness-session.ts +369 -131
- package/src/run.ts +23 -4
- package/tsconfig.json +3 -0
- package/tsconfig.lib.json +3 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DiagnosticEntry } from '@react-native-harness/tools';
|
|
2
|
+
export declare const writeTraceFile: (entries: DiagnosticEntry[], options: {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
runId: string;
|
|
5
|
+
}) => Promise<string>;
|
|
6
|
+
export declare const printSummary: (entries: DiagnosticEntry[], log: (line: string) => void) => void;
|
|
7
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/diagnostics/report.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAyCnE,eAAO,MAAM,cAAc,GACzB,SAAS,eAAe,EAAE,EAC1B,SAAS;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,KAC9C,OAAO,CAAC,MAAM,CAUhB,CAAC;AAkBF,eAAO,MAAM,YAAY,GACvB,SAAS,eAAe,EAAE,EAC1B,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAC1B,IA8CF,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getHarnessRootPath } from '@react-native-harness/tools';
|
|
4
|
+
const TRACE_PID = 1;
|
|
5
|
+
const TRACE_TID = 1;
|
|
6
|
+
const toTraceEvent = (entry) => {
|
|
7
|
+
if (entry.kind === 'mark') {
|
|
8
|
+
return {
|
|
9
|
+
name: entry.label,
|
|
10
|
+
ph: 'i',
|
|
11
|
+
s: 'g',
|
|
12
|
+
ts: entry.startTime * 1000,
|
|
13
|
+
pid: TRACE_PID,
|
|
14
|
+
tid: TRACE_TID,
|
|
15
|
+
args: entry.attrs,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
name: entry.label,
|
|
20
|
+
ph: 'X',
|
|
21
|
+
ts: entry.startTime * 1000,
|
|
22
|
+
dur: entry.duration * 1000,
|
|
23
|
+
pid: TRACE_PID,
|
|
24
|
+
tid: TRACE_TID,
|
|
25
|
+
args: entry.attrs,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export const writeTraceFile = async (entries, options) => {
|
|
29
|
+
const dir = path.join(getHarnessRootPath(options.projectRoot), 'diagnostics');
|
|
30
|
+
await fs.mkdir(dir, { recursive: true });
|
|
31
|
+
const filePath = path.join(dir, `${options.runId}.json`);
|
|
32
|
+
const trace = { traceEvents: entries.map(toTraceEvent) };
|
|
33
|
+
await fs.writeFile(filePath, JSON.stringify(trace), 'utf-8');
|
|
34
|
+
return filePath;
|
|
35
|
+
};
|
|
36
|
+
const formatDuration = (ms) => {
|
|
37
|
+
if (ms >= 1000) {
|
|
38
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
39
|
+
}
|
|
40
|
+
return `${Math.round(ms)}ms`;
|
|
41
|
+
};
|
|
42
|
+
const getNamespace = (label) => label.split('.')[0] ?? label;
|
|
43
|
+
export const printSummary = (entries, log) => {
|
|
44
|
+
if (entries.length === 0) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const statsByLabel = new Map();
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
const existing = statsByLabel.get(entry.label);
|
|
50
|
+
if (existing) {
|
|
51
|
+
existing.count += 1;
|
|
52
|
+
existing.totalMs += entry.duration;
|
|
53
|
+
existing.maxMs = Math.max(existing.maxMs, entry.duration);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
statsByLabel.set(entry.label, {
|
|
57
|
+
label: entry.label,
|
|
58
|
+
count: 1,
|
|
59
|
+
totalMs: entry.duration,
|
|
60
|
+
maxMs: entry.duration,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const byNamespace = new Map();
|
|
65
|
+
for (const stats of statsByLabel.values()) {
|
|
66
|
+
const namespace = getNamespace(stats.label);
|
|
67
|
+
const group = byNamespace.get(namespace);
|
|
68
|
+
if (group) {
|
|
69
|
+
group.push(stats);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
byNamespace.set(namespace, [stats]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
log('Diagnostics summary:');
|
|
76
|
+
const sortedNamespaces = [...byNamespace.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
77
|
+
for (const [namespace, group] of sortedNamespaces) {
|
|
78
|
+
log(` ${namespace}`);
|
|
79
|
+
group.sort((a, b) => b.totalMs - a.totalMs);
|
|
80
|
+
for (const stats of group) {
|
|
81
|
+
log(` ${stats.label}: count=${stats.count} total=${formatDuration(stats.totalMs)} max=${formatDuration(stats.maxMs)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Config as HarnessConfig } from '@react-native-harness/config';
|
|
2
|
+
export type ResetStrategyKind = 'process' | 'runtime';
|
|
3
|
+
export type EnvironmentResetStrategy = {
|
|
4
|
+
readonly kind: ResetStrategyKind;
|
|
5
|
+
reset(ctx: {
|
|
6
|
+
testFilePath: string;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Normalizes the `resetEnvironmentBetweenTestFiles` config value into a
|
|
11
|
+
* strategy kind, or `null` when no reset should happen between test files
|
|
12
|
+
* (i.e. the value is `false`).
|
|
13
|
+
*/
|
|
14
|
+
export declare const resolveResetStrategyKind: (value: HarnessConfig["resetEnvironmentBetweenTestFiles"]) => ResetStrategyKind | null;
|
|
15
|
+
export type ProcessResetStrategyDeps = {
|
|
16
|
+
restart: (testFilePath: string) => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The 'process' strategy: kill and cold-restart the app process. This is
|
|
20
|
+
* the same implementation `restartApp` uses for crash/timeout recovery.
|
|
21
|
+
*/
|
|
22
|
+
export declare const createProcessResetStrategy: (deps: ProcessResetStrategyDeps) => EnvironmentResetStrategy;
|
|
23
|
+
export type RuntimeResetConnection = {
|
|
24
|
+
resetEnvironment: () => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export type RuntimeResetStrategyDeps = {
|
|
27
|
+
getConnection: () => RuntimeResetConnection | null;
|
|
28
|
+
expectDisconnect: () => () => void;
|
|
29
|
+
waitForReconnect: (signal: AbortSignal) => Promise<void>;
|
|
30
|
+
isDisconnectError: (error: unknown) => boolean;
|
|
31
|
+
timeoutMs: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The 'runtime' strategy: reload the JS runtime in place (DevSettings.reload()
|
|
35
|
+
* on native, window.location.reload() on web) via the resetEnvironment RPC,
|
|
36
|
+
* and wait for the bridge to reconnect. The app process stays alive
|
|
37
|
+
* throughout, so the crash monitor's expected-disconnect window is used to
|
|
38
|
+
* avoid misclassifying the intentional disconnect as a runtime failure.
|
|
39
|
+
*/
|
|
40
|
+
export declare const createRuntimeResetStrategy: (deps: RuntimeResetStrategyDeps) => EnvironmentResetStrategy;
|
|
41
|
+
export type EscalationOptions = {
|
|
42
|
+
onEscalate?: (error: unknown) => void;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Wraps a primary strategy so that any failure (no active connection, RPC
|
|
46
|
+
* error, reconnect timeout, ...) falls back to running `fallback` instead of
|
|
47
|
+
* propagating the error. The composite reports the primary's `kind`, since
|
|
48
|
+
* that's the strategy that was requested.
|
|
49
|
+
*/
|
|
50
|
+
export declare const withEscalation: (primary: EnvironmentResetStrategy, fallback: EnvironmentResetStrategy, options?: EscalationOptions) => EnvironmentResetStrategy;
|
|
51
|
+
//# sourceMappingURL=environment-reset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-reset.d.ts","sourceRoot":"","sources":["../src/environment-reset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAI5E,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAEjC,KAAK,CAAC,GAAG,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,CAAC,kCAAkC,CAAC,KACvD,iBAAiB,GAAG,IAQtB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IAErC,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,MAAM,wBAAwB,KAC7B,wBAGD,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG;IACnC,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IAErC,aAAa,EAAE,MAAM,sBAAsB,GAAG,IAAI,CAAC;IAGnD,gBAAgB,EAAE,MAAM,MAAM,IAAI,CAAC;IAGnC,gBAAgB,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAIzD,iBAAiB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAE/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACrC,MAAM,wBAAwB,KAC7B,wBAkCD,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAE9B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,SAAS,wBAAwB,EACjC,UAAU,wBAAwB,EAClC,UAAS,iBAAsB,KAC9B,wBAUD,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getTimeoutSignal } from '@react-native-harness/tools';
|
|
2
|
+
const ignorePromiseRejection = () => undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes the `resetEnvironmentBetweenTestFiles` config value into a
|
|
5
|
+
* strategy kind, or `null` when no reset should happen between test files
|
|
6
|
+
* (i.e. the value is `false`).
|
|
7
|
+
*/
|
|
8
|
+
export const resolveResetStrategyKind = (value) => {
|
|
9
|
+
if (value === false) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (value === true) {
|
|
13
|
+
return 'process';
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The 'process' strategy: kill and cold-restart the app process. This is
|
|
19
|
+
* the same implementation `restartApp` uses for crash/timeout recovery.
|
|
20
|
+
*/
|
|
21
|
+
export const createProcessResetStrategy = (deps) => ({
|
|
22
|
+
kind: 'process',
|
|
23
|
+
reset: ({ testFilePath }) => deps.restart(testFilePath),
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* The 'runtime' strategy: reload the JS runtime in place (DevSettings.reload()
|
|
27
|
+
* on native, window.location.reload() on web) via the resetEnvironment RPC,
|
|
28
|
+
* and wait for the bridge to reconnect. The app process stays alive
|
|
29
|
+
* throughout, so the crash monitor's expected-disconnect window is used to
|
|
30
|
+
* avoid misclassifying the intentional disconnect as a runtime failure.
|
|
31
|
+
*/
|
|
32
|
+
export const createRuntimeResetStrategy = (deps) => ({
|
|
33
|
+
kind: 'runtime',
|
|
34
|
+
reset: async () => {
|
|
35
|
+
const connection = deps.getConnection();
|
|
36
|
+
if (!connection) {
|
|
37
|
+
throw new Error('No active app connection to reset the runtime on');
|
|
38
|
+
}
|
|
39
|
+
const closeWindow = deps.expectDisconnect();
|
|
40
|
+
try {
|
|
41
|
+
// Start listening for the reconnect before firing the RPC, so a fast
|
|
42
|
+
// reload can't reconnect before we're watching for it.
|
|
43
|
+
const reconnected = deps.waitForReconnect(getTimeoutSignal(deps.timeoutMs));
|
|
44
|
+
// Observe the rejection even when the RPC throws first, so the timeout
|
|
45
|
+
// abort firing later can't become an unhandled rejection. (.catch()
|
|
46
|
+
// returns a new promise; the original is what gets awaited below.)
|
|
47
|
+
reconnected.catch(ignorePromiseRejection);
|
|
48
|
+
try {
|
|
49
|
+
await connection.resetEnvironment();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// The reload can tear the runtime down before the RPC ack flushes.
|
|
53
|
+
// A disconnect-flavored rejection means the reload is in flight, so
|
|
54
|
+
// keep waiting for the reconnect; anything else is a real failure.
|
|
55
|
+
if (!deps.isDisconnectError(error)) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await reconnected;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
closeWindow();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Wraps a primary strategy so that any failure (no active connection, RPC
|
|
68
|
+
* error, reconnect timeout, ...) falls back to running `fallback` instead of
|
|
69
|
+
* propagating the error. The composite reports the primary's `kind`, since
|
|
70
|
+
* that's the strategy that was requested.
|
|
71
|
+
*/
|
|
72
|
+
export const withEscalation = (primary, fallback, options = {}) => ({
|
|
73
|
+
kind: primary.kind,
|
|
74
|
+
reset: async (ctx) => {
|
|
75
|
+
try {
|
|
76
|
+
await primary.reset(ctx);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
options.onEscalate?.(error);
|
|
80
|
+
await fallback.reset(ctx);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -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;
|
|
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"}
|
package/dist/execute-run.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { runHarnessTestFile } from './run.js';
|
|
4
|
+
import { resolveResetStrategyKind } from './environment-reset.js';
|
|
4
5
|
import { NativeCrashError, RuntimeDisconnectError, StartupStallError, } from './errors.js';
|
|
5
6
|
import { AppBridgeDisconnectedError, DeviceNotRespondingError, } from '@react-native-harness/bridge/server';
|
|
6
7
|
import { createPlatformSkippedTestResult, shouldRunHarnessTestFile, } from './test-file-platform-filter.js';
|
|
8
|
+
import { formatHarnessErrorMessage } from './format-harness-error.js';
|
|
9
|
+
import { logger } from '@react-native-harness/tools';
|
|
10
|
+
import { printSummary, writeTraceFile } from './diagnostics/index.js';
|
|
11
|
+
const diagnosticsLogger = logger.child('diagnostics');
|
|
7
12
|
const createRunSummary = () => ({ passed: 0, failed: 0, skipped: 0, todo: 0 });
|
|
8
13
|
const applyJestResultToSummary = (summary, result) => {
|
|
9
14
|
summary.passed += result.numPassingTests;
|
|
@@ -38,8 +43,10 @@ const emitHarnessTestStarted = async (emitEvent, event) => {
|
|
|
38
43
|
});
|
|
39
44
|
};
|
|
40
45
|
const emitHarnessTestFinished = async (emitEvent, event) => {
|
|
41
|
-
const failureMessage = event.error?.message;
|
|
42
46
|
const codeFrame = event.error?.codeFrame;
|
|
47
|
+
const failureMessage = formatHarnessErrorMessage(event.error, {
|
|
48
|
+
testStartedAt: event.startedAt,
|
|
49
|
+
});
|
|
43
50
|
const location = codeFrame?.location
|
|
44
51
|
? { column: codeFrame.location.column, line: codeFrame.location.row }
|
|
45
52
|
: null;
|
|
@@ -47,9 +54,7 @@ const emitHarnessTestFinished = async (emitEvent, event) => {
|
|
|
47
54
|
ancestorTitles: event.ancestorTitles,
|
|
48
55
|
duration: event.duration,
|
|
49
56
|
failureDetails: [],
|
|
50
|
-
failureMessages: failureMessage
|
|
51
|
-
? [`${failureMessage}${codeFrame ? `\n\n${codeFrame.content}` : ''}`]
|
|
52
|
-
: [],
|
|
57
|
+
failureMessages: failureMessage ? [failureMessage] : [],
|
|
53
58
|
fullName: event.fullName,
|
|
54
59
|
location,
|
|
55
60
|
numPassingAsserts: event.status === 'passed' ? 1 : 0,
|
|
@@ -60,8 +65,17 @@ const emitHarnessTestFinished = async (emitEvent, event) => {
|
|
|
60
65
|
await emitEvent('test-case-result', event.file, testCaseResult);
|
|
61
66
|
};
|
|
62
67
|
const isHarnessCaseEvent = (event) => event.type === 'test-started' || event.type === 'test-finished';
|
|
68
|
+
const TIMEOUT_ERROR_NAMES = new Set([
|
|
69
|
+
'SuiteHookTimeoutError',
|
|
70
|
+
'TestCaseTimeoutError',
|
|
71
|
+
]);
|
|
72
|
+
const hasRuntimeTimeout = (result) => (result.error ? TIMEOUT_ERROR_NAMES.has(result.error.name) : false) ||
|
|
73
|
+
result.tests.some((test) => test.error ? TIMEOUT_ERROR_NAMES.has(test.error.name) : false) ||
|
|
74
|
+
result.suites.some(hasRuntimeTimeout);
|
|
63
75
|
export const executeRun = async (session, tests, watcher, emitEvent, globalConfig) => {
|
|
64
76
|
const runId = randomUUID();
|
|
77
|
+
const diagnostics = session.diagnostics.child({ runId });
|
|
78
|
+
const runSpan = diagnostics.start('run.total', { runId });
|
|
65
79
|
const startTime = Date.now();
|
|
66
80
|
const watchMode = globalConfig.watch || globalConfig.watchAll;
|
|
67
81
|
const rootDir = globalConfig.rootDir ?? process.cwd();
|
|
@@ -82,6 +96,7 @@ export const executeRun = async (session, tests, watcher, emitEvent, globalConfi
|
|
|
82
96
|
testFiles,
|
|
83
97
|
watchMode,
|
|
84
98
|
coverageEnabled: globalConfig.collectCoverage,
|
|
99
|
+
completed: false,
|
|
85
100
|
summary,
|
|
86
101
|
status: summary.failed > 0 ? 'failed' : 'passed',
|
|
87
102
|
...overrides,
|
|
@@ -102,10 +117,11 @@ export const executeRun = async (session, tests, watcher, emitEvent, globalConfi
|
|
|
102
117
|
port: session.config.metroPort,
|
|
103
118
|
host: session.config.host?.trim() || undefined,
|
|
104
119
|
});
|
|
105
|
-
const
|
|
120
|
+
const resetStrategyKind = resolveResetStrategyKind(session.config.resetEnvironmentBetweenTestFiles);
|
|
106
121
|
const platformId = session.context.platform.platformId;
|
|
107
122
|
const knownPlatformIds = new Set(session.config.runners.map((runner) => runner.platformId));
|
|
108
123
|
let isFirstTest = true;
|
|
124
|
+
let shouldRestartAfterTimeout = false;
|
|
109
125
|
let runError;
|
|
110
126
|
try {
|
|
111
127
|
for (const test of tests) {
|
|
@@ -125,20 +141,82 @@ export const executeRun = async (session, tests, watcher, emitEvent, globalConfi
|
|
|
125
141
|
});
|
|
126
142
|
};
|
|
127
143
|
await session.callHook('test-file:started', { runId, file: relativeTestPath });
|
|
128
|
-
|
|
144
|
+
const fileSpan = diagnostics.start('run.file', { file: relativeTestPath });
|
|
145
|
+
let fileStatus = 'failed';
|
|
146
|
+
try {
|
|
147
|
+
if (!shouldRunHarnessTestFile(test.path, platformId, knownPlatformIds)) {
|
|
148
|
+
try {
|
|
149
|
+
await emitEvent('test-file-start', test);
|
|
150
|
+
const skippedResult = createPlatformSkippedTestResult(test.path);
|
|
151
|
+
applyJestResultToSummary(summary, skippedResult);
|
|
152
|
+
updateRunState();
|
|
153
|
+
fileStatus = 'skipped';
|
|
154
|
+
await emitTestFileFinished({
|
|
155
|
+
status: 'skipped',
|
|
156
|
+
duration: Date.now() - fileStartedAt,
|
|
157
|
+
result: null,
|
|
158
|
+
});
|
|
159
|
+
await emitEvent('test-file-success', test, skippedResult);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
fileStatus = 'failed';
|
|
163
|
+
if (!emittedTestFileFinished) {
|
|
164
|
+
await emitTestFileFinished({
|
|
165
|
+
status: 'failed',
|
|
166
|
+
duration: Date.now() - fileStartedAt,
|
|
167
|
+
result: null,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
updateRunState({ error: err });
|
|
171
|
+
await emitEvent('test-file-failure', test, buildTestFailure(err));
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
129
175
|
try {
|
|
176
|
+
if (shouldRestartAfterTimeout) {
|
|
177
|
+
// Recovery from a runtime timeout always uses the (stronger)
|
|
178
|
+
// process restart, never the configured reset strategy.
|
|
179
|
+
await session.restartApp(test.path);
|
|
180
|
+
shouldRestartAfterTimeout = false;
|
|
181
|
+
}
|
|
182
|
+
else if (resetStrategyKind !== null && !isFirstTest) {
|
|
183
|
+
await session.resetEnvironment(test.path);
|
|
184
|
+
}
|
|
185
|
+
isFirstTest = false;
|
|
186
|
+
session.flushClientLogs();
|
|
130
187
|
await emitEvent('test-file-start', test);
|
|
131
|
-
|
|
132
|
-
|
|
188
|
+
await session.ensureAppReady(test.path);
|
|
189
|
+
// Crash detection is handled inside session.runTestFile; NativeCrashError
|
|
190
|
+
// propagates here if a crash wins the race.
|
|
191
|
+
const result = await runHarnessTestFile({
|
|
192
|
+
testPath: test.path,
|
|
193
|
+
session,
|
|
194
|
+
globalConfig,
|
|
195
|
+
projectConfig: test.context.config,
|
|
196
|
+
});
|
|
197
|
+
applyJestResultToSummary(summary, result.jestResult);
|
|
198
|
+
const clientLogs = session.flushClientLogs();
|
|
199
|
+
if (clientLogs.length > 0) {
|
|
200
|
+
result.jestResult.console = clientLogs;
|
|
201
|
+
}
|
|
133
202
|
updateRunState();
|
|
203
|
+
fileStatus = result.harnessResult.status;
|
|
134
204
|
await emitTestFileFinished({
|
|
135
|
-
status:
|
|
136
|
-
duration:
|
|
137
|
-
result:
|
|
205
|
+
status: result.harnessResult.status,
|
|
206
|
+
duration: result.duration,
|
|
207
|
+
result: result.harnessResult,
|
|
138
208
|
});
|
|
139
|
-
|
|
209
|
+
const didRuntimeTimeout = hasRuntimeTimeout(result.harnessResult);
|
|
210
|
+
shouldRestartAfterTimeout = didRuntimeTimeout;
|
|
211
|
+
await caseEventChain;
|
|
212
|
+
await emitEvent('test-file-success', test, result.jestResult);
|
|
213
|
+
if (didRuntimeTimeout) {
|
|
214
|
+
await session.restartApp(test.path);
|
|
215
|
+
shouldRestartAfterTimeout = false;
|
|
216
|
+
}
|
|
140
217
|
}
|
|
141
218
|
catch (err) {
|
|
219
|
+
fileStatus = 'failed';
|
|
142
220
|
if (!emittedTestFileFinished) {
|
|
143
221
|
await emitTestFileFinished({
|
|
144
222
|
status: 'failed',
|
|
@@ -146,64 +224,25 @@ export const executeRun = async (session, tests, watcher, emitEvent, globalConfi
|
|
|
146
224
|
result: null,
|
|
147
225
|
});
|
|
148
226
|
}
|
|
149
|
-
|
|
227
|
+
const isRuntimeFailure = err instanceof NativeCrashError ||
|
|
228
|
+
err instanceof RuntimeDisconnectError ||
|
|
229
|
+
err instanceof StartupStallError ||
|
|
230
|
+
err instanceof AppBridgeDisconnectedError ||
|
|
231
|
+
err instanceof DeviceNotRespondingError;
|
|
232
|
+
if (isRuntimeFailure) {
|
|
233
|
+
summary.failed += 1;
|
|
234
|
+
updateRunState();
|
|
235
|
+
}
|
|
236
|
+
if (err instanceof NativeCrashError || err instanceof RuntimeDisconnectError) {
|
|
237
|
+
session.resetCrashState();
|
|
238
|
+
}
|
|
239
|
+
updateRunState({ error: isRuntimeFailure ? undefined : err });
|
|
240
|
+
await caseEventChain;
|
|
150
241
|
await emitEvent('test-file-failure', test, buildTestFailure(err));
|
|
151
242
|
}
|
|
152
|
-
continue;
|
|
153
243
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
await session.restartApp(test.path);
|
|
157
|
-
}
|
|
158
|
-
isFirstTest = false;
|
|
159
|
-
session.flushClientLogs();
|
|
160
|
-
await emitEvent('test-file-start', test);
|
|
161
|
-
await session.ensureAppReady(test.path);
|
|
162
|
-
// Crash detection is handled inside session.runTestFile; NativeCrashError
|
|
163
|
-
// propagates here if a crash wins the race.
|
|
164
|
-
const result = await runHarnessTestFile({
|
|
165
|
-
testPath: test.path,
|
|
166
|
-
session,
|
|
167
|
-
globalConfig,
|
|
168
|
-
projectConfig: test.context.config,
|
|
169
|
-
});
|
|
170
|
-
applyJestResultToSummary(summary, result.jestResult);
|
|
171
|
-
const clientLogs = session.flushClientLogs();
|
|
172
|
-
if (clientLogs.length > 0) {
|
|
173
|
-
result.jestResult.console = clientLogs;
|
|
174
|
-
}
|
|
175
|
-
updateRunState();
|
|
176
|
-
await emitTestFileFinished({
|
|
177
|
-
status: result.harnessResult.status,
|
|
178
|
-
duration: result.duration,
|
|
179
|
-
result: result.harnessResult,
|
|
180
|
-
});
|
|
181
|
-
await caseEventChain;
|
|
182
|
-
await emitEvent('test-file-success', test, result.jestResult);
|
|
183
|
-
}
|
|
184
|
-
catch (err) {
|
|
185
|
-
if (!emittedTestFileFinished) {
|
|
186
|
-
await emitTestFileFinished({
|
|
187
|
-
status: 'failed',
|
|
188
|
-
duration: Date.now() - fileStartedAt,
|
|
189
|
-
result: null,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
const isRuntimeFailure = err instanceof NativeCrashError ||
|
|
193
|
-
err instanceof RuntimeDisconnectError ||
|
|
194
|
-
err instanceof StartupStallError ||
|
|
195
|
-
err instanceof AppBridgeDisconnectedError ||
|
|
196
|
-
err instanceof DeviceNotRespondingError;
|
|
197
|
-
if (isRuntimeFailure) {
|
|
198
|
-
summary.failed += 1;
|
|
199
|
-
updateRunState();
|
|
200
|
-
}
|
|
201
|
-
if (err instanceof NativeCrashError || err instanceof RuntimeDisconnectError) {
|
|
202
|
-
session.resetCrashState();
|
|
203
|
-
}
|
|
204
|
-
updateRunState({ error: isRuntimeFailure ? undefined : err });
|
|
205
|
-
await caseEventChain;
|
|
206
|
-
await emitEvent('test-file-failure', test, buildTestFailure(err));
|
|
244
|
+
finally {
|
|
245
|
+
fileSpan.end({ status: fileStatus });
|
|
207
246
|
}
|
|
208
247
|
}
|
|
209
248
|
}
|
|
@@ -213,17 +252,30 @@ export const executeRun = async (session, tests, watcher, emitEvent, globalConfi
|
|
|
213
252
|
throw err;
|
|
214
253
|
}
|
|
215
254
|
finally {
|
|
216
|
-
const runState = updateRunState(runError != null ? { error: runError, status: 'failed' } : {});
|
|
255
|
+
const runState = updateRunState(runError != null ? { completed: true, error: runError, status: 'failed' } : { completed: true });
|
|
256
|
+
const finalStatus = runState.status ?? (runError != null ? 'failed' : 'passed');
|
|
217
257
|
await session.callHook('run:finished', {
|
|
218
258
|
runId,
|
|
219
259
|
startTime,
|
|
220
260
|
duration: Date.now() - startTime,
|
|
221
261
|
testFiles,
|
|
222
262
|
summary,
|
|
223
|
-
status:
|
|
263
|
+
status: finalStatus,
|
|
224
264
|
...(runError != null ? { error: runError } : {}),
|
|
225
265
|
});
|
|
226
266
|
await caseEventChain;
|
|
227
267
|
unsubscribe();
|
|
268
|
+
runSpan.end({ runId, status: finalStatus });
|
|
269
|
+
if (session.diagnostics.enabled) {
|
|
270
|
+
const entries = session.diagnostics.flush();
|
|
271
|
+
try {
|
|
272
|
+
printSummary(entries, (line) => diagnosticsLogger.info(line));
|
|
273
|
+
const tracePath = await writeTraceFile(entries, { projectRoot: rootDir, runId });
|
|
274
|
+
diagnosticsLogger.info(`wrote diagnostics trace to ${tracePath}`);
|
|
275
|
+
}
|
|
276
|
+
catch (reportError) {
|
|
277
|
+
diagnosticsLogger.warn('failed to write diagnostics report: %s', reportError);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
228
280
|
}
|
|
229
281
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SerializedError } from '@react-native-harness/bridge';
|
|
2
|
+
export declare const formatHarnessErrorMessage: (error: SerializedError | undefined, options?: {
|
|
3
|
+
testStartedAt?: number;
|
|
4
|
+
}) => string | undefined;
|
|
5
|
+
//# sourceMappingURL=format-harness-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-harness-error.d.ts","sourceRoot":"","sources":["../src/format-harness-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA6CpE,eAAO,MAAM,yBAAyB,GACpC,OAAO,eAAe,GAAG,SAAS,EAClC,UAAS;IACP,aAAa,CAAC,EAAE,MAAM,CAAC;CACnB,KACL,MAAM,GAAG,SAsBX,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const formatPendingPromiseStack = (stack) => stack
|
|
2
|
+
.split('\n')
|
|
3
|
+
.map((line) => ` ${line}`)
|
|
4
|
+
.join('\n');
|
|
5
|
+
const formatPendingPromises = (pendingPromises, testStartedAt) => {
|
|
6
|
+
if (pendingPromises.total === 0) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const lines = [`Pending promises at timeout: ${pendingPromises.total}`];
|
|
10
|
+
if (pendingPromises.items.length < pendingPromises.total) {
|
|
11
|
+
lines.push(`Showing ${pendingPromises.items.length} of ${pendingPromises.total} pending promises.`);
|
|
12
|
+
}
|
|
13
|
+
for (const promise of pendingPromises.items) {
|
|
14
|
+
const age = testStartedAt !== undefined
|
|
15
|
+
? `, created ${Math.max(0, promise.createdAt - testStartedAt)}ms after test start`
|
|
16
|
+
: '';
|
|
17
|
+
lines.push('');
|
|
18
|
+
lines.push(`Promise #${promise.id}${age}:`);
|
|
19
|
+
if (promise.stack) {
|
|
20
|
+
lines.push(formatPendingPromiseStack(promise.stack));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
lines.push(' <stack unavailable>');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return lines.join('\n');
|
|
27
|
+
};
|
|
28
|
+
export const formatHarnessErrorMessage = (error, options = {}) => {
|
|
29
|
+
if (!error) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const parts = [error.message];
|
|
33
|
+
const pendingPromiseDetails = error.diagnostics?.pendingPromises
|
|
34
|
+
? formatPendingPromises(error.diagnostics.pendingPromises, options.testStartedAt)
|
|
35
|
+
: null;
|
|
36
|
+
if (pendingPromiseDetails) {
|
|
37
|
+
parts.push(pendingPromiseDetails);
|
|
38
|
+
}
|
|
39
|
+
if (error.codeFrame) {
|
|
40
|
+
parts.push(error.codeFrame.content);
|
|
41
|
+
}
|
|
42
|
+
return parts.join('\n\n');
|
|
43
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type AppConnection } from '@react-native-harness/bridge/server';
|
|
1
|
+
import { type HarnessBridge, type AppConnection } from '@react-native-harness/bridge/server';
|
|
2
2
|
import { type HarnessContext, type TestRunnerEvents, type TestExecutionOptions, type TestSuiteResult } from '@react-native-harness/bridge';
|
|
3
3
|
import { type HarnessPlatform } from '@react-native-harness/platforms';
|
|
4
4
|
import { type HarnessPluginManager, type HarnessRunStatus, type HarnessRunSummary } from '@react-native-harness/plugins';
|
|
5
|
+
import { type Diagnostics } from '@react-native-harness/tools';
|
|
5
6
|
import { type Config as HarnessConfig } from '@react-native-harness/config';
|
|
6
7
|
import type { Config as JestConfig } from 'jest-runner';
|
|
7
8
|
import { type CrashMonitor } from './crash-monitor.js';
|
|
@@ -17,12 +18,17 @@ export declare const waitForBridgeDisconnectOrTimeout: ({ bridge, connection, ti
|
|
|
17
18
|
connection: AppConnection;
|
|
18
19
|
timeoutMs: number;
|
|
19
20
|
}) => Promise<boolean>;
|
|
21
|
+
export declare const waitForNextConnected: ({ bridge, signal, }: {
|
|
22
|
+
bridge: Pick<HarnessBridge, "on" | "off">;
|
|
23
|
+
signal: AbortSignal;
|
|
24
|
+
}) => Promise<void>;
|
|
20
25
|
export type HarnessRunState = {
|
|
21
26
|
readonly runId: string;
|
|
22
27
|
readonly startTime: number;
|
|
23
28
|
readonly testFiles: string[];
|
|
24
29
|
readonly watchMode: boolean;
|
|
25
30
|
readonly coverageEnabled: boolean;
|
|
31
|
+
readonly completed?: boolean;
|
|
26
32
|
readonly summary?: HarnessRunSummary;
|
|
27
33
|
readonly status?: HarnessRunStatus;
|
|
28
34
|
readonly error?: unknown;
|
|
@@ -31,22 +37,41 @@ export type HarnessRunTestsOptions = Exclude<TestExecutionOptions, 'platform'>;
|
|
|
31
37
|
export type HarnessSession = {
|
|
32
38
|
readonly config: HarnessConfig;
|
|
33
39
|
readonly context: HarnessContext;
|
|
40
|
+
readonly diagnostics: Diagnostics;
|
|
34
41
|
onTestRunnerEvent: (listener: (event: TestRunnerEvents) => void) => () => void;
|
|
35
42
|
runTestFile: (path: string, options: HarnessRunTestsOptions) => Promise<TestSuiteResult>;
|
|
36
43
|
ensureAppReady: (testFilePath: string) => Promise<void>;
|
|
37
44
|
restartApp: (testFilePath?: string) => Promise<void>;
|
|
45
|
+
resetEnvironment: (testFilePath: string) => Promise<void>;
|
|
38
46
|
resetCrashState: () => void;
|
|
39
47
|
flushClientLogs: () => ClientLogBuffer;
|
|
40
48
|
callHook: HarnessPluginManager<HarnessConfig, HarnessPlatform>['callHook'];
|
|
41
49
|
setRunState: (state: HarnessRunState | null) => void;
|
|
42
50
|
dispose: (reason?: 'normal' | 'abort' | 'error') => Promise<void>;
|
|
43
51
|
};
|
|
52
|
+
export declare const getSignalExitCodeForRunState: (state: HarnessRunState | null) => number;
|
|
53
|
+
export declare const withPlatformReadyTimeout: <T>(options: {
|
|
54
|
+
timeout: number;
|
|
55
|
+
signal: AbortSignal;
|
|
56
|
+
work: (signal: AbortSignal) => Promise<T>;
|
|
57
|
+
}) => Promise<T>;
|
|
44
58
|
export declare const waitForStartupCrash: ({ crashMonitor, detectNativeCrashes, testFilePath, signal, }: {
|
|
45
59
|
crashMonitor: CrashMonitor;
|
|
46
60
|
detectNativeCrashes: boolean;
|
|
47
61
|
testFilePath: string;
|
|
48
62
|
signal: AbortSignal;
|
|
49
63
|
}) => Promise<never>;
|
|
64
|
+
/**
|
|
65
|
+
* Logs the "Reusing Metro cache" message when persistent Metro caching is
|
|
66
|
+
* enabled and the cache is warm. `projectRoot` must be the config-resolved
|
|
67
|
+
* project root so the warmth check looks at the same directory Metro caches
|
|
68
|
+
* into.
|
|
69
|
+
*/
|
|
70
|
+
export declare const maybeLogMetroCacheReused: (options: {
|
|
71
|
+
config: Pick<HarnessConfig, "cache" | "unstable__enableMetroCache">;
|
|
72
|
+
projectRoot: string;
|
|
73
|
+
platform: HarnessPlatform;
|
|
74
|
+
}) => void;
|
|
50
75
|
export declare const createHarnessSession: (globalConfig: JestConfig.GlobalConfig, { lockManager }?: {
|
|
51
76
|
lockManager?: ResourceLockManager;
|
|
52
77
|
}) => Promise<HarnessSession>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,iCAAiC,CAAC;AASzC,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"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"}
|