@react-native-harness/jest 1.4.0 → 1.4.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/package.json +8 -8
- package/dist/__tests__/bridge.test.d.ts +0 -2
- package/dist/__tests__/bridge.test.d.ts.map +0 -1
- package/dist/__tests__/bridge.test.js +0 -167
- package/dist/__tests__/client-log-handler.test.d.ts +0 -2
- package/dist/__tests__/client-log-handler.test.d.ts.map +0 -1
- package/dist/__tests__/client-log-handler.test.js +0 -306
- package/dist/__tests__/crash-monitor.test.d.ts +0 -2
- package/dist/__tests__/crash-monitor.test.d.ts.map +0 -1
- package/dist/__tests__/crash-monitor.test.js +0 -230
- package/dist/__tests__/crash-supervisor.test.d.ts +0 -2
- package/dist/__tests__/crash-supervisor.test.d.ts.map +0 -1
- package/dist/__tests__/crash-supervisor.test.js +0 -151
- package/dist/__tests__/environment-reset.test.d.ts +0 -2
- package/dist/__tests__/environment-reset.test.d.ts.map +0 -1
- package/dist/__tests__/environment-reset.test.js +0 -238
- package/dist/__tests__/errors.test.d.ts +0 -2
- package/dist/__tests__/errors.test.d.ts.map +0 -1
- package/dist/__tests__/errors.test.js +0 -98
- package/dist/__tests__/execute-run.test.d.ts +0 -2
- package/dist/__tests__/execute-run.test.d.ts.map +0 -1
- package/dist/__tests__/execute-run.test.js +0 -639
- package/dist/__tests__/fixtures/mock-platform-runner.d.ts +0 -7
- package/dist/__tests__/fixtures/mock-platform-runner.d.ts.map +0 -1
- package/dist/__tests__/fixtures/mock-platform-runner.js +0 -6
- package/dist/__tests__/format-harness-error.test.d.ts +0 -2
- package/dist/__tests__/format-harness-error.test.d.ts.map +0 -1
- package/dist/__tests__/format-harness-error.test.js +0 -27
- package/dist/__tests__/harness-cache.test.d.ts +0 -2
- package/dist/__tests__/harness-cache.test.d.ts.map +0 -1
- package/dist/__tests__/harness-cache.test.js +0 -72
- package/dist/__tests__/harness-session-signal.test.d.ts +0 -2
- package/dist/__tests__/harness-session-signal.test.d.ts.map +0 -1
- package/dist/__tests__/harness-session-signal.test.js +0 -298
- package/dist/__tests__/harness-session.test.d.ts +0 -2
- package/dist/__tests__/harness-session.test.d.ts.map +0 -1
- package/dist/__tests__/harness-session.test.js +0 -233
- package/dist/__tests__/harness.test.d.ts +0 -2
- package/dist/__tests__/harness.test.d.ts.map +0 -1
- package/dist/__tests__/harness.test.js +0 -636
- package/dist/__tests__/hook-queue.test.d.ts +0 -2
- package/dist/__tests__/hook-queue.test.d.ts.map +0 -1
- package/dist/__tests__/hook-queue.test.js +0 -56
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -150
- package/dist/__tests__/metro-port.test.d.ts +0 -2
- package/dist/__tests__/metro-port.test.d.ts.map +0 -1
- package/dist/__tests__/metro-port.test.js +0 -54
- package/dist/__tests__/resource-lock.test.d.ts +0 -2
- package/dist/__tests__/resource-lock.test.d.ts.map +0 -1
- package/dist/__tests__/resource-lock.test.js +0 -138
- package/dist/__tests__/run.test.d.ts +0 -2
- package/dist/__tests__/run.test.d.ts.map +0 -1
- package/dist/__tests__/run.test.js +0 -48
- package/dist/__tests__/test-file-platform-filter.test.d.ts +0 -2
- package/dist/__tests__/test-file-platform-filter.test.d.ts.map +0 -1
- package/dist/__tests__/test-file-platform-filter.test.js +0 -47
- 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/diagnostics/__tests__/derivers.test.d.ts +0 -2
- package/dist/diagnostics/__tests__/derivers.test.d.ts.map +0 -1
- package/dist/diagnostics/__tests__/derivers.test.js +0 -248
- package/dist/diagnostics/__tests__/report.test.d.ts +0 -2
- package/dist/diagnostics/__tests__/report.test.d.ts.map +0 -1
- package/dist/diagnostics/__tests__/report.test.js +0 -97
- package/dist/tsconfig.lib.tsbuildinfo +0 -1
- package/src/__tests__/bridge.test.ts +0 -224
- package/src/__tests__/client-log-handler.test.ts +0 -364
- package/src/__tests__/crash-monitor.test.ts +0 -323
- package/src/__tests__/environment-reset.test.ts +0 -308
- package/src/__tests__/errors.test.ts +0 -157
- package/src/__tests__/execute-run.test.ts +0 -849
- package/src/__tests__/format-harness-error.test.ts +0 -32
- package/src/__tests__/harness-cache.test.ts +0 -91
- package/src/__tests__/harness-session.test.ts +0 -303
- package/src/__tests__/hook-queue.test.ts +0 -77
- package/src/__tests__/index.test.ts +0 -221
- package/src/__tests__/metro-port.test.ts +0 -63
- package/src/__tests__/resource-lock.test.ts +0 -179
- package/src/__tests__/run.test.ts +0 -74
- package/src/__tests__/test-file-platform-filter.test.ts +0 -83
- package/src/diagnostics/__tests__/derivers.test.ts +0 -300
- package/src/diagnostics/__tests__/report.test.ts +0 -128
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { formatHarnessErrorMessage } from '../format-harness-error.js';
|
|
3
|
-
describe('formatHarnessErrorMessage', () => {
|
|
4
|
-
it('formats pending promise diagnostics for timeout errors', () => {
|
|
5
|
-
const message = formatHarnessErrorMessage({
|
|
6
|
-
name: 'TestCaseTimeoutError',
|
|
7
|
-
message: 'Test timed out after 50ms: suite hangs',
|
|
8
|
-
diagnostics: {
|
|
9
|
-
pendingPromises: {
|
|
10
|
-
total: 2,
|
|
11
|
-
items: [
|
|
12
|
-
{
|
|
13
|
-
id: 7,
|
|
14
|
-
createdAt: 110,
|
|
15
|
-
stack: 'Error: Promise created\n at hangs (example.ts:10:5)',
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
}, { testStartedAt: 100 });
|
|
21
|
-
expect(message).toContain('Test timed out after 50ms: suite hangs');
|
|
22
|
-
expect(message).toContain('Pending promises at timeout: 2');
|
|
23
|
-
expect(message).toContain('Showing 1 of 2 pending promises.');
|
|
24
|
-
expect(message).toContain('Promise #7, created 10ms after test start:');
|
|
25
|
-
expect(message).toContain(' Error: Promise created');
|
|
26
|
-
});
|
|
27
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness-cache.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/harness-cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { maybeLogMetroCacheReused } from '../harness-session.js';
|
|
3
|
-
const { createHarnessCache, isWarm, logMetroCacheReused } = vi.hoisted(() => {
|
|
4
|
-
const isWarm = vi.fn();
|
|
5
|
-
return {
|
|
6
|
-
isWarm,
|
|
7
|
-
createHarnessCache: vi.fn((options) => ({
|
|
8
|
-
paths: {
|
|
9
|
-
root: `${options.projectRoot}/.harness/cache`,
|
|
10
|
-
metroTransform: `${options.projectRoot}/.harness/cache/metro`,
|
|
11
|
-
metroFileMap: `${options.projectRoot}/.harness/cache/metro-file-map`,
|
|
12
|
-
},
|
|
13
|
-
isWarm,
|
|
14
|
-
ensureDomainDirectories: vi.fn(),
|
|
15
|
-
})),
|
|
16
|
-
logMetroCacheReused: vi.fn(),
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
vi.mock('@react-native-harness/cache', () => ({
|
|
20
|
-
createHarnessCache,
|
|
21
|
-
}));
|
|
22
|
-
vi.mock('../logs.js', async (importOriginal) => ({
|
|
23
|
-
...(await importOriginal()),
|
|
24
|
-
logMetroCacheReused,
|
|
25
|
-
}));
|
|
26
|
-
const platform = { name: 'test-runner' };
|
|
27
|
-
describe('maybeLogMetroCacheReused', () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
vi.clearAllMocks();
|
|
30
|
-
});
|
|
31
|
-
it('logs the reuse message when caching is enabled and the cache is warm', () => {
|
|
32
|
-
isWarm.mockReturnValue(true);
|
|
33
|
-
maybeLogMetroCacheReused({
|
|
34
|
-
config: { cache: { metro: true } },
|
|
35
|
-
projectRoot: '/tmp/app',
|
|
36
|
-
platform,
|
|
37
|
-
});
|
|
38
|
-
expect(createHarnessCache).toHaveBeenCalledWith({
|
|
39
|
-
projectRoot: '/tmp/app',
|
|
40
|
-
});
|
|
41
|
-
expect(isWarm).toHaveBeenCalledWith('metro');
|
|
42
|
-
expect(logMetroCacheReused).toHaveBeenCalledWith(platform);
|
|
43
|
-
});
|
|
44
|
-
it('logs the reuse message with a warm cache when caching is on by default', () => {
|
|
45
|
-
isWarm.mockReturnValue(true);
|
|
46
|
-
maybeLogMetroCacheReused({
|
|
47
|
-
config: {},
|
|
48
|
-
projectRoot: '/tmp/app',
|
|
49
|
-
platform,
|
|
50
|
-
});
|
|
51
|
-
expect(logMetroCacheReused).toHaveBeenCalledWith(platform);
|
|
52
|
-
});
|
|
53
|
-
it('does not log when caching is enabled but the cache is cold', () => {
|
|
54
|
-
isWarm.mockReturnValue(false);
|
|
55
|
-
maybeLogMetroCacheReused({
|
|
56
|
-
config: { cache: { metro: true } },
|
|
57
|
-
projectRoot: '/tmp/app',
|
|
58
|
-
platform,
|
|
59
|
-
});
|
|
60
|
-
expect(logMetroCacheReused).not.toHaveBeenCalled();
|
|
61
|
-
});
|
|
62
|
-
it('does not log when caching is disabled, regardless of cache state', () => {
|
|
63
|
-
isWarm.mockReturnValue(true);
|
|
64
|
-
maybeLogMetroCacheReused({
|
|
65
|
-
config: { cache: { metro: false } },
|
|
66
|
-
projectRoot: '/tmp/app',
|
|
67
|
-
platform,
|
|
68
|
-
});
|
|
69
|
-
expect(createHarnessCache).not.toHaveBeenCalled();
|
|
70
|
-
expect(logMetroCacheReused).not.toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness-session-signal.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/harness-session-signal.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
|
-
const toolMocks = vi.hoisted(() => ({
|
|
7
|
-
createCrashArtifactWriter: vi.fn(() => ({
|
|
8
|
-
persistArtifact: vi.fn(() => '/tmp/harness-artifact'),
|
|
9
|
-
})),
|
|
10
|
-
createHarnessBridge: vi.fn(),
|
|
11
|
-
createHarnessPluginManager: vi.fn(),
|
|
12
|
-
createHookQueue: vi.fn(),
|
|
13
|
-
createClientLogCollector: vi.fn(),
|
|
14
|
-
createCrashMonitor: vi.fn(),
|
|
15
|
-
getConfig: vi.fn(),
|
|
16
|
-
getMetroInstance: vi.fn(),
|
|
17
|
-
getAdditionalCliArgs: vi.fn(),
|
|
18
|
-
logNoop: vi.fn(),
|
|
19
|
-
lockAcquire: vi.fn(),
|
|
20
|
-
processExit: vi.fn(),
|
|
21
|
-
processOff: vi.fn(),
|
|
22
|
-
processOnce: vi.fn(),
|
|
23
|
-
resolveHarnessMetroPort: vi.fn(),
|
|
24
|
-
runnerFactory: vi.fn(),
|
|
25
|
-
}));
|
|
26
|
-
vi.mock('@react-native-harness/config', () => ({
|
|
27
|
-
ConfigSchema: {
|
|
28
|
-
parse: vi.fn((value) => value),
|
|
29
|
-
},
|
|
30
|
-
getConfig: toolMocks.getConfig,
|
|
31
|
-
}));
|
|
32
|
-
vi.mock('@react-native-harness/bundler-metro', () => ({
|
|
33
|
-
getMetroInstance: toolMocks.getMetroInstance,
|
|
34
|
-
isMetroCacheReusable: vi.fn(() => false),
|
|
35
|
-
waitForMetroBackedAppReady: vi.fn(),
|
|
36
|
-
}));
|
|
37
|
-
vi.mock('@react-native-harness/bridge/server', () => ({
|
|
38
|
-
createHarnessBridge: toolMocks.createHarnessBridge,
|
|
39
|
-
}));
|
|
40
|
-
vi.mock('@react-native-harness/plugins', () => ({
|
|
41
|
-
createHarnessPluginManager: toolMocks.createHarnessPluginManager,
|
|
42
|
-
}));
|
|
43
|
-
vi.mock('@react-native-harness/tools', async () => {
|
|
44
|
-
const actual = await vi.importActual('@react-native-harness/tools');
|
|
45
|
-
return {
|
|
46
|
-
...actual,
|
|
47
|
-
createCrashArtifactWriter: toolMocks.createCrashArtifactWriter,
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
vi.mock('jest-util', () => ({
|
|
51
|
-
preRunMessage: {
|
|
52
|
-
remove: vi.fn(),
|
|
53
|
-
},
|
|
54
|
-
}));
|
|
55
|
-
vi.mock('../action-hooks.js', () => ({
|
|
56
|
-
createActionHooksPlugin: vi.fn(() => ({})),
|
|
57
|
-
}));
|
|
58
|
-
vi.mock('../client-log-handler.js', () => ({
|
|
59
|
-
createClientLogCollector: toolMocks.createClientLogCollector,
|
|
60
|
-
}));
|
|
61
|
-
vi.mock('../crash-monitor.js', () => ({
|
|
62
|
-
createCrashMonitor: toolMocks.createCrashMonitor,
|
|
63
|
-
}));
|
|
64
|
-
vi.mock('../hook-queue.js', () => ({
|
|
65
|
-
createHookQueue: toolMocks.createHookQueue,
|
|
66
|
-
}));
|
|
67
|
-
vi.mock('../cli-args.js', () => ({
|
|
68
|
-
getAdditionalCliArgs: toolMocks.getAdditionalCliArgs,
|
|
69
|
-
}));
|
|
70
|
-
vi.mock('../logs.js', () => ({
|
|
71
|
-
logMetroCacheReused: toolMocks.logNoop,
|
|
72
|
-
logMetroPortFallback: toolMocks.logNoop,
|
|
73
|
-
logNativeCoverageCollected: toolMocks.logNoop,
|
|
74
|
-
logRunnerStarting: toolMocks.logNoop,
|
|
75
|
-
logRunnerStillWaitingInQueue: toolMocks.logNoop,
|
|
76
|
-
logRunnerWaitingInQueue: toolMocks.logNoop,
|
|
77
|
-
logTestEnvironmentReady: toolMocks.logNoop,
|
|
78
|
-
logTestRunHeader: toolMocks.logNoop,
|
|
79
|
-
}));
|
|
80
|
-
vi.mock('../metro-port.js', () => ({
|
|
81
|
-
resolveHarnessMetroPort: toolMocks.resolveHarnessMetroPort,
|
|
82
|
-
}));
|
|
83
|
-
vi.mock('../resource-lock.js', () => ({
|
|
84
|
-
createResourceLockManager: vi.fn(() => ({
|
|
85
|
-
acquire: toolMocks.lockAcquire,
|
|
86
|
-
})),
|
|
87
|
-
}));
|
|
88
|
-
import { createHarnessSession, getSignalExitCodeForRunState, handleHarnessSignal, } from '../harness-session.js';
|
|
89
|
-
const signalListeners = new Map();
|
|
90
|
-
const createTempRunnerModule = () => {
|
|
91
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'rn-harness-session-'));
|
|
92
|
-
const runnerPath = path.join(root, 'runner.mjs');
|
|
93
|
-
fs.writeFileSync(runnerPath, [
|
|
94
|
-
'export default async function runner(config, harnessConfig, init) {',
|
|
95
|
-
' return globalThis.__RN_HARNESS_TEST_RUNNER__(config, harnessConfig, init);',
|
|
96
|
-
'}',
|
|
97
|
-
].join('\n'));
|
|
98
|
-
return {
|
|
99
|
-
root,
|
|
100
|
-
runnerUrl: pathToFileURL(runnerPath).href,
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
const createHarnessConfig = (runnerUrl) => {
|
|
104
|
-
const runnerConfig = {
|
|
105
|
-
name: 'ios',
|
|
106
|
-
platformId: 'ios',
|
|
107
|
-
runner: runnerUrl,
|
|
108
|
-
config: {},
|
|
109
|
-
getResourceLockKey: () => 'test-lock',
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
bridgeTimeout: 1000,
|
|
113
|
-
bundleStartTimeout: 1000,
|
|
114
|
-
defaultRunner: 'ios',
|
|
115
|
-
detectNativeCrashes: true,
|
|
116
|
-
forwardClientLogs: false,
|
|
117
|
-
maxAppRestarts: 1,
|
|
118
|
-
metroPort: 8081,
|
|
119
|
-
permissions: false,
|
|
120
|
-
platformReadyTimeout: 1000,
|
|
121
|
-
plugins: [],
|
|
122
|
-
runners: [runnerConfig],
|
|
123
|
-
unstable__enableMetroCache: false,
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
const createAppSession = () => ({
|
|
127
|
-
addListener: vi.fn(),
|
|
128
|
-
dispose: vi.fn(async () => undefined),
|
|
129
|
-
getCrashDetails: vi.fn(async () => null),
|
|
130
|
-
getLogs: vi.fn(() => []),
|
|
131
|
-
getState: vi.fn(async () => ({ status: 'running' })),
|
|
132
|
-
removeListener: vi.fn(),
|
|
133
|
-
});
|
|
134
|
-
beforeEach(() => {
|
|
135
|
-
signalListeners.clear();
|
|
136
|
-
vi.spyOn(process, 'once').mockImplementation((event, listener) => {
|
|
137
|
-
signalListeners.set(String(event), listener);
|
|
138
|
-
return process;
|
|
139
|
-
});
|
|
140
|
-
vi.spyOn(process, 'off').mockImplementation((event, listener) => {
|
|
141
|
-
if (signalListeners.get(String(event)) === listener) {
|
|
142
|
-
signalListeners.delete(String(event));
|
|
143
|
-
}
|
|
144
|
-
return process;
|
|
145
|
-
});
|
|
146
|
-
toolMocks.getAdditionalCliArgs.mockReturnValue({});
|
|
147
|
-
toolMocks.lockAcquire.mockResolvedValue({ release: vi.fn(async () => undefined) });
|
|
148
|
-
toolMocks.resolveHarnessMetroPort.mockImplementation(async ({ config }) => ({
|
|
149
|
-
config,
|
|
150
|
-
didFallback: false,
|
|
151
|
-
initialMetroPort: config.metroPort,
|
|
152
|
-
metroPortLease: {
|
|
153
|
-
release: vi.fn(async () => undefined),
|
|
154
|
-
},
|
|
155
|
-
}));
|
|
156
|
-
toolMocks.createHarnessBridge.mockResolvedValue({
|
|
157
|
-
connection: null,
|
|
158
|
-
dispose: vi.fn(async () => undefined),
|
|
159
|
-
off: vi.fn(),
|
|
160
|
-
on: vi.fn(),
|
|
161
|
-
ws: {},
|
|
162
|
-
});
|
|
163
|
-
toolMocks.getMetroInstance.mockResolvedValue({
|
|
164
|
-
dispose: vi.fn(async () => undefined),
|
|
165
|
-
events: {
|
|
166
|
-
addListener: vi.fn(),
|
|
167
|
-
removeListener: vi.fn(),
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
toolMocks.createHarnessPluginManager.mockReturnValue({
|
|
171
|
-
hasPlugins: () => false,
|
|
172
|
-
callHook: vi.fn(async () => undefined),
|
|
173
|
-
});
|
|
174
|
-
toolMocks.createHookQueue.mockReturnValue({
|
|
175
|
-
drain: vi.fn(async () => undefined),
|
|
176
|
-
schedule: vi.fn((task) => task()),
|
|
177
|
-
});
|
|
178
|
-
toolMocks.createClientLogCollector.mockReturnValue({
|
|
179
|
-
flush: vi.fn(() => []),
|
|
180
|
-
handleEvent: vi.fn(),
|
|
181
|
-
});
|
|
182
|
-
toolMocks.createCrashMonitor.mockReturnValue({
|
|
183
|
-
dispose: vi.fn(async () => undefined),
|
|
184
|
-
handleBridgeDisconnect: vi.fn(),
|
|
185
|
-
isAlive: vi.fn(() => false),
|
|
186
|
-
reset: vi.fn(),
|
|
187
|
-
setAppSession: vi.fn(),
|
|
188
|
-
start: vi.fn(async () => undefined),
|
|
189
|
-
stop: vi.fn(async () => undefined),
|
|
190
|
-
watch: vi.fn(() => ({
|
|
191
|
-
cancel: vi.fn(),
|
|
192
|
-
promise: Promise.resolve({}),
|
|
193
|
-
})),
|
|
194
|
-
});
|
|
195
|
-
toolMocks.runnerFactory.mockReset();
|
|
196
|
-
toolMocks.getConfig.mockResolvedValue({ config: {} });
|
|
197
|
-
});
|
|
198
|
-
afterEach(() => {
|
|
199
|
-
vi.restoreAllMocks();
|
|
200
|
-
delete globalThis.__RN_HARNESS_TEST_RUNNER__;
|
|
201
|
-
});
|
|
202
|
-
describe('createHarnessSession signal flow', () => {
|
|
203
|
-
it('passes session signal into createAppSession and aborts it on dispose', async () => {
|
|
204
|
-
const { root, runnerUrl } = createTempRunnerModule();
|
|
205
|
-
const appSession = createAppSession();
|
|
206
|
-
const createAppSessionMock = vi.fn(async (_options, _context) => {
|
|
207
|
-
void _options;
|
|
208
|
-
void _context;
|
|
209
|
-
return appSession;
|
|
210
|
-
});
|
|
211
|
-
globalThis.__RN_HARNESS_TEST_RUNNER__ = toolMocks.runnerFactory.mockImplementation(async () => ({
|
|
212
|
-
createAppSession: createAppSessionMock,
|
|
213
|
-
dispose: vi.fn(async () => undefined),
|
|
214
|
-
}));
|
|
215
|
-
toolMocks.getConfig.mockResolvedValue({
|
|
216
|
-
config: createHarnessConfig(runnerUrl),
|
|
217
|
-
});
|
|
218
|
-
try {
|
|
219
|
-
const session = await createHarnessSession({ rootDir: root }, {
|
|
220
|
-
lockManager: {
|
|
221
|
-
acquire: toolMocks.lockAcquire,
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
await session.restartApp();
|
|
225
|
-
const runState = {
|
|
226
|
-
coverageEnabled: false,
|
|
227
|
-
runId: 'run-1',
|
|
228
|
-
startTime: Date.now(),
|
|
229
|
-
status: 'passed',
|
|
230
|
-
summary: {
|
|
231
|
-
failed: 0,
|
|
232
|
-
passed: 1,
|
|
233
|
-
skipped: 0,
|
|
234
|
-
todo: 0,
|
|
235
|
-
},
|
|
236
|
-
testFiles: ['foo.test.ts'],
|
|
237
|
-
watchMode: false,
|
|
238
|
-
};
|
|
239
|
-
session.setRunState(runState);
|
|
240
|
-
expect(createAppSessionMock).toHaveBeenCalledWith(undefined, expect.objectContaining({
|
|
241
|
-
signal: expect.any(AbortSignal),
|
|
242
|
-
}));
|
|
243
|
-
const context = createAppSessionMock.mock.calls[0]?.[1];
|
|
244
|
-
expect(context?.signal).toBeInstanceOf(AbortSignal);
|
|
245
|
-
expect(context?.signal?.aborted).toBe(false);
|
|
246
|
-
await session.dispose();
|
|
247
|
-
expect(context?.signal?.aborted).toBe(true);
|
|
248
|
-
}
|
|
249
|
-
finally {
|
|
250
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
it('maps run state to exit code and aborts controller', async () => {
|
|
254
|
-
const controller = new AbortController();
|
|
255
|
-
const dispose = vi.fn(async () => undefined);
|
|
256
|
-
const exit = vi.fn();
|
|
257
|
-
const failedRun = {
|
|
258
|
-
coverageEnabled: false,
|
|
259
|
-
error: new Error('failed'),
|
|
260
|
-
runId: 'run-2',
|
|
261
|
-
startTime: Date.now(),
|
|
262
|
-
status: 'failed',
|
|
263
|
-
summary: {
|
|
264
|
-
failed: 1,
|
|
265
|
-
passed: 0,
|
|
266
|
-
skipped: 0,
|
|
267
|
-
todo: 0,
|
|
268
|
-
},
|
|
269
|
-
testFiles: ['foo.test.ts'],
|
|
270
|
-
watchMode: false,
|
|
271
|
-
};
|
|
272
|
-
await handleHarnessSignal({
|
|
273
|
-
currentRun: {
|
|
274
|
-
coverageEnabled: false,
|
|
275
|
-
completed: true,
|
|
276
|
-
runId: 'run-2',
|
|
277
|
-
startTime: Date.now(),
|
|
278
|
-
status: 'passed',
|
|
279
|
-
summary: {
|
|
280
|
-
failed: 0,
|
|
281
|
-
passed: 1,
|
|
282
|
-
skipped: 0,
|
|
283
|
-
todo: 0,
|
|
284
|
-
},
|
|
285
|
-
testFiles: ['foo.test.ts'],
|
|
286
|
-
watchMode: false,
|
|
287
|
-
},
|
|
288
|
-
dispose,
|
|
289
|
-
exit,
|
|
290
|
-
sessionController: controller,
|
|
291
|
-
});
|
|
292
|
-
expect(controller.signal.aborted).toBe(true);
|
|
293
|
-
expect(dispose).toHaveBeenCalledTimes(1);
|
|
294
|
-
expect(exit).toHaveBeenCalledWith(0);
|
|
295
|
-
expect(getSignalExitCodeForRunState(failedRun)).toBe(1);
|
|
296
|
-
expect(getSignalExitCodeForRunState(null)).toBe(1);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness-session.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/harness-session.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getSignalExitCodeForRunState, waitForBridgeDisconnectOrTimeout, waitForNextConnected, waitForStartupCrash, withPlatformReadyTimeout, } from '../harness-session.js';
|
|
3
|
-
import { PlatformReadyTimeoutError } from '../errors.js';
|
|
4
|
-
const createConnection = () => ({
|
|
5
|
-
device: {
|
|
6
|
-
platform: 'ios',
|
|
7
|
-
manufacturer: 'Apple',
|
|
8
|
-
model: 'iPhone',
|
|
9
|
-
osVersion: '18.0',
|
|
10
|
-
},
|
|
11
|
-
runTests: vi.fn(),
|
|
12
|
-
resetEnvironment: vi.fn(),
|
|
13
|
-
});
|
|
14
|
-
const createBridge = (connection) => {
|
|
15
|
-
let currentConnection = connection;
|
|
16
|
-
let disconnectedListener = null;
|
|
17
|
-
return {
|
|
18
|
-
get connection() {
|
|
19
|
-
return currentConnection;
|
|
20
|
-
},
|
|
21
|
-
on: vi.fn((event, listener) => {
|
|
22
|
-
if (event === 'disconnected') {
|
|
23
|
-
disconnectedListener = listener;
|
|
24
|
-
}
|
|
25
|
-
}),
|
|
26
|
-
off: vi.fn((event, listener) => {
|
|
27
|
-
if (event === 'disconnected' && disconnectedListener === listener) {
|
|
28
|
-
disconnectedListener = null;
|
|
29
|
-
}
|
|
30
|
-
}),
|
|
31
|
-
disconnect: () => {
|
|
32
|
-
currentConnection = null;
|
|
33
|
-
disconnectedListener?.();
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
describe('waitForBridgeDisconnectOrTimeout', () => {
|
|
38
|
-
it('returns true when the bridge disconnects before the timeout', async () => {
|
|
39
|
-
const connection = createConnection();
|
|
40
|
-
const bridge = createBridge(connection);
|
|
41
|
-
const waitPromise = waitForBridgeDisconnectOrTimeout({
|
|
42
|
-
bridge,
|
|
43
|
-
connection,
|
|
44
|
-
timeoutMs: 50,
|
|
45
|
-
});
|
|
46
|
-
bridge.disconnect();
|
|
47
|
-
await expect(waitPromise).resolves.toBe(true);
|
|
48
|
-
});
|
|
49
|
-
it('returns false when the bridge stays connected through the timeout', async () => {
|
|
50
|
-
const connection = createConnection();
|
|
51
|
-
const bridge = createBridge(connection);
|
|
52
|
-
await expect(waitForBridgeDisconnectOrTimeout({
|
|
53
|
-
bridge,
|
|
54
|
-
connection,
|
|
55
|
-
timeoutMs: 10,
|
|
56
|
-
})).resolves.toBe(false);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
describe('waitForNextConnected', () => {
|
|
60
|
-
const createConnectableBridge = () => {
|
|
61
|
-
let listener = null;
|
|
62
|
-
const bridge = {
|
|
63
|
-
on: vi.fn((event, l) => {
|
|
64
|
-
if (event === 'connected')
|
|
65
|
-
listener = l;
|
|
66
|
-
}),
|
|
67
|
-
off: vi.fn((event, l) => {
|
|
68
|
-
if (event === 'connected' && listener === l)
|
|
69
|
-
listener = null;
|
|
70
|
-
}),
|
|
71
|
-
};
|
|
72
|
-
return { bridge, emitConnected: () => listener?.() };
|
|
73
|
-
};
|
|
74
|
-
it('resolves when the bridge emits a connected event', async () => {
|
|
75
|
-
const { bridge, emitConnected } = createConnectableBridge();
|
|
76
|
-
const controller = new AbortController();
|
|
77
|
-
const waitPromise = waitForNextConnected({ bridge, signal: controller.signal });
|
|
78
|
-
emitConnected();
|
|
79
|
-
await expect(waitPromise).resolves.toBeUndefined();
|
|
80
|
-
});
|
|
81
|
-
it('rejects when the signal aborts before a connected event', async () => {
|
|
82
|
-
const { bridge } = createConnectableBridge();
|
|
83
|
-
const controller = new AbortController();
|
|
84
|
-
const waitPromise = waitForNextConnected({ bridge, signal: controller.signal });
|
|
85
|
-
controller.abort(new DOMException('Aborted', 'AbortError'));
|
|
86
|
-
await expect(waitPromise).rejects.toThrow('Aborted');
|
|
87
|
-
});
|
|
88
|
-
it('rejects immediately when the signal is already aborted', async () => {
|
|
89
|
-
const { bridge } = createConnectableBridge();
|
|
90
|
-
const controller = new AbortController();
|
|
91
|
-
controller.abort(new DOMException('Aborted', 'AbortError'));
|
|
92
|
-
await expect(waitForNextConnected({ bridge, signal: controller.signal })).rejects.toThrow('Aborted');
|
|
93
|
-
});
|
|
94
|
-
it('unsubscribes the connected listener once settled', async () => {
|
|
95
|
-
const { bridge, emitConnected } = createConnectableBridge();
|
|
96
|
-
const controller = new AbortController();
|
|
97
|
-
const waitPromise = waitForNextConnected({ bridge, signal: controller.signal });
|
|
98
|
-
emitConnected();
|
|
99
|
-
await waitPromise;
|
|
100
|
-
expect(bridge.off).toHaveBeenCalledOnce();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
describe('waitForStartupCrash', () => {
|
|
104
|
-
it('does not install a startup crash watch when native crash detection is disabled', async () => {
|
|
105
|
-
const watch = vi.fn();
|
|
106
|
-
const crashMonitor = {
|
|
107
|
-
watch,
|
|
108
|
-
};
|
|
109
|
-
const controller = new AbortController();
|
|
110
|
-
const waitPromise = waitForStartupCrash({
|
|
111
|
-
crashMonitor,
|
|
112
|
-
detectNativeCrashes: false,
|
|
113
|
-
testFilePath: '/test.harness.ts',
|
|
114
|
-
signal: controller.signal,
|
|
115
|
-
});
|
|
116
|
-
controller.abort(new DOMException('Aborted', 'AbortError'));
|
|
117
|
-
await expect(waitPromise).rejects.toThrow('Aborted');
|
|
118
|
-
expect(watch).not.toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
|
-
it('removes its abort listener on the session signal once the crash watch wins', async () => {
|
|
121
|
-
let rejectWatch;
|
|
122
|
-
const cancel = vi.fn();
|
|
123
|
-
const watch = vi.fn(() => ({
|
|
124
|
-
promise: new Promise((_, reject) => {
|
|
125
|
-
rejectWatch = reject;
|
|
126
|
-
}),
|
|
127
|
-
cancel,
|
|
128
|
-
}));
|
|
129
|
-
const crashMonitor = { watch };
|
|
130
|
-
const controller = new AbortController();
|
|
131
|
-
const addSpy = vi.spyOn(controller.signal, 'addEventListener');
|
|
132
|
-
const removeSpy = vi.spyOn(controller.signal, 'removeEventListener');
|
|
133
|
-
const waitPromise = waitForStartupCrash({
|
|
134
|
-
crashMonitor,
|
|
135
|
-
detectNativeCrashes: true,
|
|
136
|
-
testFilePath: '/test.harness.ts',
|
|
137
|
-
signal: controller.signal,
|
|
138
|
-
});
|
|
139
|
-
// The crash watch wins the race, not the session signal.
|
|
140
|
-
rejectWatch(new Error('native crash detected'));
|
|
141
|
-
await expect(waitPromise).rejects.toThrow('native crash detected');
|
|
142
|
-
expect(cancel).toHaveBeenCalledTimes(1);
|
|
143
|
-
// Regression: forgetting to cancel the losing waitForAbort() left its
|
|
144
|
-
// listener attached to this session-lifetime signal for the rest of
|
|
145
|
-
// the session — one leaked listener per test file.
|
|
146
|
-
expect(addSpy).toHaveBeenCalledTimes(1);
|
|
147
|
-
expect(removeSpy).toHaveBeenCalledTimes(1);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
describe('withPlatformReadyTimeout', () => {
|
|
151
|
-
afterEach(() => {
|
|
152
|
-
vi.useRealTimers();
|
|
153
|
-
});
|
|
154
|
-
it('passes the real session signal to work, unmodified by the timeout', async () => {
|
|
155
|
-
const controller = new AbortController();
|
|
156
|
-
const work = vi.fn(async (signal) => {
|
|
157
|
-
expect(signal).toBe(controller.signal);
|
|
158
|
-
return 'ready';
|
|
159
|
-
});
|
|
160
|
-
await expect(withPlatformReadyTimeout({
|
|
161
|
-
timeout: 1_000,
|
|
162
|
-
signal: controller.signal,
|
|
163
|
-
work,
|
|
164
|
-
})).resolves.toBe('ready');
|
|
165
|
-
expect(work).toHaveBeenCalledTimes(1);
|
|
166
|
-
});
|
|
167
|
-
it('throws PlatformReadyTimeoutError on timeout without aborting the session signal', async () => {
|
|
168
|
-
vi.useFakeTimers();
|
|
169
|
-
const controller = new AbortController();
|
|
170
|
-
// Never resolves within the test; simulates a hung platform init.
|
|
171
|
-
const work = () => new Promise(() => undefined);
|
|
172
|
-
const resultPromise = withPlatformReadyTimeout({
|
|
173
|
-
timeout: 1_000,
|
|
174
|
-
signal: controller.signal,
|
|
175
|
-
work,
|
|
176
|
-
});
|
|
177
|
-
await vi.advanceTimersByTimeAsync(1_000);
|
|
178
|
-
await expect(resultPromise).rejects.toBeInstanceOf(PlatformReadyTimeoutError);
|
|
179
|
-
expect(controller.signal.aborted).toBe(false);
|
|
180
|
-
});
|
|
181
|
-
it('propagates a genuine abort from the session signal as-is', async () => {
|
|
182
|
-
const controller = new AbortController();
|
|
183
|
-
const abortReason = new DOMException('Aborted', 'AbortError');
|
|
184
|
-
const work = (signal) => new Promise((_, reject) => {
|
|
185
|
-
signal.addEventListener('abort', () => reject(signal.reason));
|
|
186
|
-
});
|
|
187
|
-
const resultPromise = withPlatformReadyTimeout({
|
|
188
|
-
timeout: 1_000,
|
|
189
|
-
signal: controller.signal,
|
|
190
|
-
work,
|
|
191
|
-
});
|
|
192
|
-
controller.abort(abortReason);
|
|
193
|
-
await expect(resultPromise).rejects.toBe(abortReason);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
describe('getSignalExitCodeForRunState', () => {
|
|
197
|
-
const createRunState = (overrides = {}) => ({
|
|
198
|
-
completed: true,
|
|
199
|
-
coverageEnabled: false,
|
|
200
|
-
runId: 'run-1',
|
|
201
|
-
startTime: 0,
|
|
202
|
-
status: 'passed',
|
|
203
|
-
summary: {
|
|
204
|
-
failed: 0,
|
|
205
|
-
passed: 1,
|
|
206
|
-
skipped: 0,
|
|
207
|
-
todo: 0,
|
|
208
|
-
},
|
|
209
|
-
testFiles: ['test.harness.ts'],
|
|
210
|
-
watchMode: false,
|
|
211
|
-
...overrides,
|
|
212
|
-
});
|
|
213
|
-
it('preserves a successful exit after a completed passing run', () => {
|
|
214
|
-
expect(getSignalExitCodeForRunState(createRunState())).toBe(0);
|
|
215
|
-
});
|
|
216
|
-
it('fails when the run has not completed yet', () => {
|
|
217
|
-
expect(getSignalExitCodeForRunState(createRunState({ completed: false }))).toBe(1);
|
|
218
|
-
});
|
|
219
|
-
it('fails when the completed run has failures', () => {
|
|
220
|
-
expect(getSignalExitCodeForRunState(createRunState({
|
|
221
|
-
status: 'failed',
|
|
222
|
-
summary: {
|
|
223
|
-
failed: 1,
|
|
224
|
-
passed: 0,
|
|
225
|
-
skipped: 0,
|
|
226
|
-
todo: 0,
|
|
227
|
-
},
|
|
228
|
-
}))).toBe(1);
|
|
229
|
-
});
|
|
230
|
-
it('fails when no run state is available', () => {
|
|
231
|
-
expect(getSignalExitCodeForRunState(null)).toBe(1);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/harness.test.ts"],"names":[],"mappings":""}
|