@react-native-harness/jest 1.1.0-rc.1 → 1.1.0-rc.3
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__/errors.test.js +6 -1
- 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.js +484 -47
- 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/__tests__/resource-lock.test.d.ts +2 -0
- package/dist/__tests__/resource-lock.test.d.ts.map +1 -0
- package/dist/__tests__/resource-lock.test.js +103 -0
- package/dist/action-hooks.d.ts +9 -0
- package/dist/action-hooks.d.ts.map +1 -0
- package/dist/action-hooks.js +36 -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.map +1 -1
- package/dist/crash-supervisor.js +7 -6
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -2
- package/dist/harness.d.ts +23 -1
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +574 -136
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -58
- package/dist/logs.d.ts +4 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +12 -0
- package/dist/resource-lock.d.ts +25 -0
- package/dist/resource-lock.d.ts.map +1 -0
- package/dist/resource-lock.js +278 -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 +29 -2
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/src/__tests__/errors.test.ts +18 -6
- package/src/__tests__/harness-cache.test.ts +74 -0
- package/src/__tests__/harness.test.ts +702 -49
- package/src/__tests__/index.test.ts +61 -0
- package/src/__tests__/resource-lock.test.ts +138 -0
- package/src/action-hooks.ts +66 -0
- package/src/cli-args.ts +6 -0
- package/src/crash-supervisor.ts +13 -6
- package/src/env.d.ts +1 -1
- package/src/errors.ts +15 -4
- package/src/harness.ts +743 -157
- package/src/index.ts +276 -64
- package/src/logs.ts +17 -3
- package/src/resource-lock.ts +450 -0
- package/src/run.ts +20 -10
- package/src/setup.ts +42 -1
- package/tsconfig.json +3 -3
- package/tsconfig.lib.json +6 -6
|
@@ -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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-lock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/resource-lock.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { createResourceLockManager, hashResourceLockKey, } from '../resource-lock.js';
|
|
6
|
+
describe('resource lock manager', () => {
|
|
7
|
+
let rootDir;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'react-native-harness-resource-lock-test-'));
|
|
10
|
+
});
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await fs.rm(rootDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('queues access in FIFO order', async () => {
|
|
15
|
+
const manager = createResourceLockManager({
|
|
16
|
+
rootDir,
|
|
17
|
+
pollIntervalMs: 5,
|
|
18
|
+
heartbeatIntervalMs: 20,
|
|
19
|
+
staleLockTimeoutMs: 200,
|
|
20
|
+
});
|
|
21
|
+
const order = [];
|
|
22
|
+
const firstLease = await manager.acquire('ios:simulator:iPhone 17 Pro:26.2');
|
|
23
|
+
const secondAcquire = manager
|
|
24
|
+
.acquire('ios:simulator:iPhone 17 Pro:26.2', {
|
|
25
|
+
onWait: () => {
|
|
26
|
+
order.push('waiting');
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
.then(async (lease) => {
|
|
30
|
+
order.push('acquired');
|
|
31
|
+
await lease.release();
|
|
32
|
+
});
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
34
|
+
expect(order).toEqual(['waiting']);
|
|
35
|
+
await firstLease.release();
|
|
36
|
+
await secondAcquire;
|
|
37
|
+
expect(order).toEqual(['waiting', 'acquired']);
|
|
38
|
+
});
|
|
39
|
+
it('removes the queued ticket when waiting is aborted', async () => {
|
|
40
|
+
const manager = createResourceLockManager({
|
|
41
|
+
rootDir,
|
|
42
|
+
pollIntervalMs: 5,
|
|
43
|
+
heartbeatIntervalMs: 20,
|
|
44
|
+
staleLockTimeoutMs: 200,
|
|
45
|
+
});
|
|
46
|
+
const key = 'android:emulator:Pixel_8_API_35';
|
|
47
|
+
const firstLease = await manager.acquire(key);
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const acquirePromise = manager.acquire(key, {
|
|
50
|
+
signal: controller.signal,
|
|
51
|
+
});
|
|
52
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
53
|
+
controller.abort();
|
|
54
|
+
await expect(acquirePromise).rejects.toMatchObject({
|
|
55
|
+
name: 'AbortError',
|
|
56
|
+
});
|
|
57
|
+
const queueDir = path.join(rootDir, hashResourceLockKey(key), 'queue');
|
|
58
|
+
const queuedEntries = await fs.readdir(queueDir);
|
|
59
|
+
expect(queuedEntries).toHaveLength(0);
|
|
60
|
+
await firstLease.release();
|
|
61
|
+
});
|
|
62
|
+
it('keeps queued tickets alive while the waiting process is still active', async () => {
|
|
63
|
+
const manager = createResourceLockManager({
|
|
64
|
+
rootDir,
|
|
65
|
+
pollIntervalMs: 5,
|
|
66
|
+
heartbeatIntervalMs: 20,
|
|
67
|
+
staleLockTimeoutMs: 30,
|
|
68
|
+
isProcessActive: () => true,
|
|
69
|
+
});
|
|
70
|
+
const key = 'ios:simulator:iPhone 17 Pro:26.2';
|
|
71
|
+
const firstLease = await manager.acquire(key);
|
|
72
|
+
const secondAcquire = manager.acquire(key);
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 80));
|
|
74
|
+
await firstLease.release();
|
|
75
|
+
const secondLease = await secondAcquire;
|
|
76
|
+
await secondLease.release();
|
|
77
|
+
});
|
|
78
|
+
it('reclaims a stale owner before granting the lock', async () => {
|
|
79
|
+
const manager = createResourceLockManager({
|
|
80
|
+
rootDir,
|
|
81
|
+
pollIntervalMs: 5,
|
|
82
|
+
heartbeatIntervalMs: 20,
|
|
83
|
+
staleLockTimeoutMs: 50,
|
|
84
|
+
isProcessActive: () => false,
|
|
85
|
+
});
|
|
86
|
+
const key = 'web:browser:chromium';
|
|
87
|
+
const keyDir = path.join(rootDir, hashResourceLockKey(key));
|
|
88
|
+
const queueDir = path.join(keyDir, 'queue');
|
|
89
|
+
const ownerFilePath = path.join(keyDir, 'owner.json');
|
|
90
|
+
await fs.mkdir(queueDir, { recursive: true });
|
|
91
|
+
await fs.writeFile(ownerFilePath, JSON.stringify({
|
|
92
|
+
ticketId: 'stale-owner',
|
|
93
|
+
key,
|
|
94
|
+
pid: 999999,
|
|
95
|
+
createdAt: Date.now() - 1000,
|
|
96
|
+
heartbeatAt: Date.now() - 1000,
|
|
97
|
+
}), 'utf8');
|
|
98
|
+
const lease = await manager.acquire(key);
|
|
99
|
+
const owner = JSON.parse(await fs.readFile(ownerFilePath, 'utf8'));
|
|
100
|
+
expect(owner.ticketId).not.toBe('stale-owner');
|
|
101
|
+
await lease.release();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type HarnessPlugin } from '@react-native-harness/plugins';
|
|
2
|
+
import type { Config as HarnessConfig } from '@react-native-harness/config';
|
|
3
|
+
import type { HarnessPlatform } from '@react-native-harness/platforms';
|
|
4
|
+
type ActionHookState = {
|
|
5
|
+
_unused?: never;
|
|
6
|
+
};
|
|
7
|
+
export declare const createActionHooksPlugin: () => HarnessPlugin<ActionHookState, HarnessConfig, HarnessPlatform>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=action-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-hooks.d.ts","sourceRoot":"","sources":["../src/action-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGvE,KAAK,eAAe,GAAG;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,CAAC;AAuBF,eAAO,MAAM,uBAAuB,QAAO,aAAa,CACtD,eAAe,EACf,aAAa,EACb,eAAe,CA6Bb,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { definePlugin, } from '@react-native-harness/plugins';
|
|
2
|
+
import { spawn } from '@react-native-harness/tools';
|
|
3
|
+
const getInlineHookScript = (name) => {
|
|
4
|
+
const value = process.env[name]?.trim();
|
|
5
|
+
return value ? value : null;
|
|
6
|
+
};
|
|
7
|
+
const runInlineHook = async (script) => {
|
|
8
|
+
const env = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] != null));
|
|
9
|
+
await spawn('bash', ['-lc', script], {
|
|
10
|
+
env,
|
|
11
|
+
cwd: process.env.HARNESS_PROJECT_ROOT,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export const createActionHooksPlugin = () => definePlugin({
|
|
15
|
+
name: 'github-action-hooks',
|
|
16
|
+
createState: () => ({}),
|
|
17
|
+
hooks: {
|
|
18
|
+
harness: {
|
|
19
|
+
beforeRun: async () => {
|
|
20
|
+
const script = getInlineHookScript('PRE_RUN_HOOK');
|
|
21
|
+
if (!script) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
await runInlineHook(script);
|
|
25
|
+
},
|
|
26
|
+
afterRun: async (ctx) => {
|
|
27
|
+
const script = getInlineHookScript('AFTER_RUN_HOOK');
|
|
28
|
+
if (!script) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
process.env.HARNESS_EXIT_CODE = ctx.status === 'passed' ? '0' : '1';
|
|
32
|
+
await runInlineHook(script);
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
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
|
};
|
|
@@ -1 +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;
|
|
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"}
|
package/dist/crash-supervisor.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NativeCrashError, } from './errors.js';
|
|
2
2
|
import { logger } from '@react-native-harness/tools';
|
|
3
|
+
const crashLogger = logger.child('crash');
|
|
3
4
|
const getCrashPhase = (state) => state === 'running' ? 'execution' : 'startup';
|
|
4
5
|
const mergeCrashDetails = (phase, initialDetails, enrichedDetails, fallbackSummary) => ({
|
|
5
6
|
phase,
|
|
@@ -43,13 +44,13 @@ export const createCrashSupervisor = ({ appMonitor, platformRunner, }) => {
|
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
if (!activeTestFilePath) {
|
|
46
|
-
|
|
47
|
+
crashLogger.debug('ignoring crash signal without active test: %s', reason);
|
|
47
48
|
return;
|
|
48
49
|
}
|
|
49
50
|
isResolvingCrash = true;
|
|
50
|
-
|
|
51
|
+
crashLogger.debug('native crash detected during %s (state=%s reason=%s)', activeTestFilePath, state, reason || '(none)');
|
|
51
52
|
for (const line of details?.rawLines ?? []) {
|
|
52
|
-
|
|
53
|
+
crashLogger.debug('%s', line);
|
|
53
54
|
}
|
|
54
55
|
const phase = getCrashPhase(state);
|
|
55
56
|
const testFilePath = activeTestFilePath;
|
|
@@ -61,7 +62,7 @@ export const createCrashSupervisor = ({ appMonitor, platformRunner, }) => {
|
|
|
61
62
|
occurredAt: Date.now(),
|
|
62
63
|
});
|
|
63
64
|
const mergedDetails = mergeCrashDetails(phase, details, enrichedDetails, reason);
|
|
64
|
-
|
|
65
|
+
crashLogger.debug('crash details: %o', {
|
|
65
66
|
phase: mergedDetails.phase,
|
|
66
67
|
source: mergedDetails.source,
|
|
67
68
|
summary: mergedDetails.summary,
|
|
@@ -83,11 +84,11 @@ export const createCrashSupervisor = ({ appMonitor, platformRunner, }) => {
|
|
|
83
84
|
try {
|
|
84
85
|
const isRunning = await platformRunner.isAppRunning();
|
|
85
86
|
if (!isRunning) {
|
|
86
|
-
handleCrash(reason, details);
|
|
87
|
+
void handleCrash(reason, details);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
catch (error) {
|
|
90
|
-
|
|
91
|
+
crashLogger.debug('crash confirmation failed', error);
|
|
91
92
|
}
|
|
92
93
|
};
|
|
93
94
|
const appMonitorListener = (event) => {
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HarnessError } from '@react-native-harness/tools';
|
|
2
2
|
import type { AppCrashDetails } from '@react-native-harness/platforms';
|
|
3
|
+
export { StartupStallError, type StartupStallCode, type StartupStallDetails, } from '@react-native-harness/bundler-metro';
|
|
3
4
|
export declare class NoRunnerSpecifiedError extends HarnessError {
|
|
4
5
|
constructor();
|
|
5
6
|
}
|
|
@@ -10,6 +11,10 @@ export declare class RunnerNotFoundError extends HarnessError {
|
|
|
10
11
|
export declare class InitializationTimeoutError extends HarnessError {
|
|
11
12
|
constructor();
|
|
12
13
|
}
|
|
14
|
+
export declare class PlatformReadyTimeoutError extends HarnessError {
|
|
15
|
+
readonly timeout: number;
|
|
16
|
+
constructor(timeout: number);
|
|
17
|
+
}
|
|
13
18
|
export type NativeCrashPhase = 'startup' | 'execution';
|
|
14
19
|
export type NativeCrashDetails = AppCrashDetails & {
|
|
15
20
|
phase: NativeCrashPhase;
|
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;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,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,qBAAa,yBAA0B,SAAQ,YAAY;aAC7B,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAM5C;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,CAAC;AAEvD,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG;IACjD,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAmDF,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,6 +20,14 @@ export class InitializationTimeoutError extends HarnessError {
|
|
|
19
20
|
this.name = 'InitializationTimeoutError';
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
export class PlatformReadyTimeoutError extends HarnessError {
|
|
24
|
+
timeout;
|
|
25
|
+
constructor(timeout) {
|
|
26
|
+
super(`The platform did not become ready within ${timeout}ms. Increase "platformReadyTimeout" if your device, simulator, or emulator needs more time to start.`);
|
|
27
|
+
this.timeout = timeout;
|
|
28
|
+
this.name = 'PlatformReadyTimeoutError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
22
31
|
const buildNativeCrashMessage = ({ phase, summary, signal, exceptionType, processName, pid, stackTrace, artifactType, }) => {
|
|
23
32
|
const lines = [
|
|
24
33
|
phase === 'startup'
|
|
@@ -27,8 +36,7 @@ const buildNativeCrashMessage = ({ phase, summary, signal, exceptionType, proces
|
|
|
27
36
|
];
|
|
28
37
|
const hasCrashBlock = summary?.includes('\n') ?? false;
|
|
29
38
|
const shouldRenderSummary = Boolean(summary) &&
|
|
30
|
-
!(!hasCrashBlock &&
|
|
31
|
-
artifactType === 'ios-crash-report');
|
|
39
|
+
!(!hasCrashBlock && artifactType === 'ios-crash-report');
|
|
32
40
|
if (shouldRenderSummary && summary) {
|
|
33
41
|
lines.push('');
|
|
34
42
|
lines.push(summary);
|
package/dist/harness.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { BridgeServer } from '@react-native-harness/bridge/server';
|
|
2
2
|
import { HarnessContext, TestExecutionOptions, TestSuiteResult } from '@react-native-harness/bridge';
|
|
3
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';
|
|
4
6
|
import { Config as HarnessConfig } from '@react-native-harness/config';
|
|
5
7
|
import { type CrashSupervisor } from './crash-supervisor.js';
|
|
6
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
|
+
};
|
|
7
19
|
export type Harness = {
|
|
8
20
|
context: HarnessContext;
|
|
9
21
|
runTests: (path: string, options: HarnessRunTestsOptions) => Promise<TestSuiteResult>;
|
|
@@ -11,14 +23,24 @@ export type Harness = {
|
|
|
11
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: {
|
|
32
|
+
metroInstance: MetroInstance;
|
|
16
33
|
serverBridge: BridgeServer;
|
|
17
34
|
platformInstance: HarnessPlatformRunner;
|
|
18
|
-
|
|
35
|
+
platformId: string;
|
|
36
|
+
bundleStartTimeout: number;
|
|
37
|
+
readyTimeout: number;
|
|
38
|
+
maxAppRestarts: number;
|
|
19
39
|
testFilePath: string;
|
|
20
40
|
crashSupervisor: CrashSupervisor;
|
|
41
|
+
signal?: AbortSignal;
|
|
21
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,
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EACb,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAEL,cAAc,EAGd,oBAAoB,EACpB,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,gBAAgB,EACrB,eAAe,EAEf,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAIL,KAAK,aAAa,EAGnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAQvC,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAc/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,IAIF,CAAC;AAgDF,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;AAilBF,eAAO,MAAM,UAAU,GACrB,QAAQ,aAAa,EACrB,UAAU,eAAe,EACzB,aAAa,MAAM,KAClB,OAAO,CAAC,OAAO,CAYjB,CAAC"}
|