@react-native-harness/jest 1.4.0 → 1.5.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.
Files changed (133) hide show
  1. package/dist/execute-run.d.ts.map +1 -1
  2. package/dist/execute-run.js +2 -1
  3. package/dist/harness-session.d.ts.map +1 -1
  4. package/dist/harness-session.js +74 -30
  5. package/dist/resource-lock.d.ts.map +1 -1
  6. package/dist/resource-lock.js +12 -2
  7. package/dist/toJestStatus.d.ts +4 -0
  8. package/dist/toJestStatus.d.ts.map +1 -0
  9. package/dist/toJestStatus.js +2 -0
  10. package/dist/toTestResult.d.ts +3 -2
  11. package/dist/toTestResult.d.ts.map +1 -1
  12. package/dist/toTestResult.js +2 -1
  13. package/package.json +8 -8
  14. package/src/execute-run.ts +2 -1
  15. package/src/harness-session.ts +86 -27
  16. package/src/resource-lock.ts +15 -2
  17. package/src/toJestStatus.ts +6 -0
  18. package/src/toTestResult.ts +5 -3
  19. package/dist/__tests__/bridge.test.d.ts +0 -2
  20. package/dist/__tests__/bridge.test.d.ts.map +0 -1
  21. package/dist/__tests__/bridge.test.js +0 -167
  22. package/dist/__tests__/client-log-handler.test.d.ts +0 -2
  23. package/dist/__tests__/client-log-handler.test.d.ts.map +0 -1
  24. package/dist/__tests__/client-log-handler.test.js +0 -306
  25. package/dist/__tests__/crash-monitor.test.d.ts +0 -2
  26. package/dist/__tests__/crash-monitor.test.d.ts.map +0 -1
  27. package/dist/__tests__/crash-monitor.test.js +0 -230
  28. package/dist/__tests__/crash-supervisor.test.d.ts +0 -2
  29. package/dist/__tests__/crash-supervisor.test.d.ts.map +0 -1
  30. package/dist/__tests__/crash-supervisor.test.js +0 -151
  31. package/dist/__tests__/environment-reset.test.d.ts +0 -2
  32. package/dist/__tests__/environment-reset.test.d.ts.map +0 -1
  33. package/dist/__tests__/environment-reset.test.js +0 -238
  34. package/dist/__tests__/errors.test.d.ts +0 -2
  35. package/dist/__tests__/errors.test.d.ts.map +0 -1
  36. package/dist/__tests__/errors.test.js +0 -98
  37. package/dist/__tests__/execute-run.test.d.ts +0 -2
  38. package/dist/__tests__/execute-run.test.d.ts.map +0 -1
  39. package/dist/__tests__/execute-run.test.js +0 -639
  40. package/dist/__tests__/fixtures/mock-platform-runner.d.ts +0 -7
  41. package/dist/__tests__/fixtures/mock-platform-runner.d.ts.map +0 -1
  42. package/dist/__tests__/fixtures/mock-platform-runner.js +0 -6
  43. package/dist/__tests__/format-harness-error.test.d.ts +0 -2
  44. package/dist/__tests__/format-harness-error.test.d.ts.map +0 -1
  45. package/dist/__tests__/format-harness-error.test.js +0 -27
  46. package/dist/__tests__/harness-cache.test.d.ts +0 -2
  47. package/dist/__tests__/harness-cache.test.d.ts.map +0 -1
  48. package/dist/__tests__/harness-cache.test.js +0 -72
  49. package/dist/__tests__/harness-session-signal.test.d.ts +0 -2
  50. package/dist/__tests__/harness-session-signal.test.d.ts.map +0 -1
  51. package/dist/__tests__/harness-session-signal.test.js +0 -298
  52. package/dist/__tests__/harness-session.test.d.ts +0 -2
  53. package/dist/__tests__/harness-session.test.d.ts.map +0 -1
  54. package/dist/__tests__/harness-session.test.js +0 -233
  55. package/dist/__tests__/harness.test.d.ts +0 -2
  56. package/dist/__tests__/harness.test.d.ts.map +0 -1
  57. package/dist/__tests__/harness.test.js +0 -636
  58. package/dist/__tests__/hook-queue.test.d.ts +0 -2
  59. package/dist/__tests__/hook-queue.test.d.ts.map +0 -1
  60. package/dist/__tests__/hook-queue.test.js +0 -56
  61. package/dist/__tests__/index.test.d.ts +0 -2
  62. package/dist/__tests__/index.test.d.ts.map +0 -1
  63. package/dist/__tests__/index.test.js +0 -150
  64. package/dist/__tests__/metro-port.test.d.ts +0 -2
  65. package/dist/__tests__/metro-port.test.d.ts.map +0 -1
  66. package/dist/__tests__/metro-port.test.js +0 -54
  67. package/dist/__tests__/resource-lock.test.d.ts +0 -2
  68. package/dist/__tests__/resource-lock.test.d.ts.map +0 -1
  69. package/dist/__tests__/resource-lock.test.js +0 -138
  70. package/dist/__tests__/run.test.d.ts +0 -2
  71. package/dist/__tests__/run.test.d.ts.map +0 -1
  72. package/dist/__tests__/run.test.js +0 -48
  73. package/dist/__tests__/test-file-platform-filter.test.d.ts +0 -2
  74. package/dist/__tests__/test-file-platform-filter.test.d.ts.map +0 -1
  75. package/dist/__tests__/test-file-platform-filter.test.js +0 -47
  76. package/dist/cli-args.test.d.ts +0 -2
  77. package/dist/cli-args.test.d.ts.map +0 -1
  78. package/dist/cli-args.test.js +0 -1
  79. package/dist/crash-supervisor.d.ts +0 -21
  80. package/dist/crash-supervisor.d.ts.map +0 -1
  81. package/dist/crash-supervisor.js +0 -210
  82. package/dist/diagnostics/__tests__/derivers.test.d.ts +0 -2
  83. package/dist/diagnostics/__tests__/derivers.test.d.ts.map +0 -1
  84. package/dist/diagnostics/__tests__/derivers.test.js +0 -248
  85. package/dist/diagnostics/__tests__/report.test.d.ts +0 -2
  86. package/dist/diagnostics/__tests__/report.test.d.ts.map +0 -1
  87. package/dist/diagnostics/__tests__/report.test.js +0 -97
  88. package/dist/event-mapper.d.ts +0 -16
  89. package/dist/event-mapper.d.ts.map +0 -1
  90. package/dist/event-mapper.js +0 -120
  91. package/dist/global-setup.d.ts +0 -3
  92. package/dist/global-setup.d.ts.map +0 -1
  93. package/dist/global-setup.js +0 -60
  94. package/dist/global-teardown.d.ts +0 -3
  95. package/dist/global-teardown.d.ts.map +0 -1
  96. package/dist/global-teardown.js +0 -11
  97. package/dist/harness-reporter.d.ts +0 -8
  98. package/dist/harness-reporter.d.ts.map +0 -1
  99. package/dist/harness-reporter.js +0 -12
  100. package/dist/harness.d.ts +0 -47
  101. package/dist/harness.d.ts.map +0 -1
  102. package/dist/harness.js +0 -619
  103. package/dist/jest-options.d.ts +0 -2
  104. package/dist/jest-options.d.ts.map +0 -1
  105. package/dist/jest-options.js +0 -1
  106. package/dist/setup.d.ts +0 -3
  107. package/dist/setup.d.ts.map +0 -1
  108. package/dist/setup.js +0 -80
  109. package/dist/teardown.d.ts +0 -3
  110. package/dist/teardown.d.ts.map +0 -1
  111. package/dist/teardown.js +0 -10
  112. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  113. package/dist/types.d.ts +0 -17
  114. package/dist/types.d.ts.map +0 -1
  115. package/dist/types.js +0 -1
  116. package/src/__tests__/bridge.test.ts +0 -224
  117. package/src/__tests__/client-log-handler.test.ts +0 -364
  118. package/src/__tests__/crash-monitor.test.ts +0 -323
  119. package/src/__tests__/environment-reset.test.ts +0 -308
  120. package/src/__tests__/errors.test.ts +0 -157
  121. package/src/__tests__/execute-run.test.ts +0 -849
  122. package/src/__tests__/format-harness-error.test.ts +0 -32
  123. package/src/__tests__/harness-cache.test.ts +0 -91
  124. package/src/__tests__/harness-session.test.ts +0 -303
  125. package/src/__tests__/hook-queue.test.ts +0 -77
  126. package/src/__tests__/index.test.ts +0 -221
  127. package/src/__tests__/metro-port.test.ts +0 -63
  128. package/src/__tests__/resource-lock.test.ts +0 -179
  129. package/src/__tests__/run.test.ts +0 -74
  130. package/src/__tests__/test-file-platform-filter.test.ts +0 -83
  131. package/src/diagnostics/__tests__/derivers.test.ts +0 -300
  132. package/src/diagnostics/__tests__/report.test.ts +0 -128
  133. package/tsconfig.tsbuildinfo +0 -1
@@ -1,56 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createHookQueue } from '../hook-queue.js';
3
- describe('createHookQueue', () => {
4
- it('executes scheduled work in FIFO order', async () => {
5
- const queue = createHookQueue();
6
- const order = [];
7
- queue.schedule(async () => { order.push(1); });
8
- queue.schedule(async () => { order.push(2); });
9
- queue.schedule(async () => { order.push(3); });
10
- await queue.drain();
11
- expect(order).toEqual([1, 2, 3]);
12
- });
13
- it('drain awaits async work before resolving', async () => {
14
- const queue = createHookQueue();
15
- let resolved = false;
16
- queue.schedule(async () => {
17
- await new Promise((r) => setTimeout(r, 20));
18
- resolved = true;
19
- });
20
- expect(resolved).toBe(false);
21
- await queue.drain();
22
- expect(resolved).toBe(true);
23
- });
24
- it('drain resolves immediately when nothing is scheduled', async () => {
25
- const queue = createHookQueue();
26
- await expect(queue.drain()).resolves.toBeUndefined();
27
- });
28
- it('captures the first error and re-throws it on drain', async () => {
29
- const queue = createHookQueue();
30
- const err = new Error('hook failed');
31
- queue.schedule(async () => { throw err; });
32
- await expect(queue.drain()).rejects.toBe(err);
33
- });
34
- it('continues executing subsequent hooks after one throws', async () => {
35
- const queue = createHookQueue();
36
- const ran = [];
37
- queue.schedule(async () => { throw new Error('first'); });
38
- queue.schedule(async () => { ran.push('second'); });
39
- queue.schedule(async () => { ran.push('third'); });
40
- await expect(queue.drain()).rejects.toThrow('first');
41
- expect(ran).toEqual(['second', 'third']);
42
- });
43
- it('error is cleared after drain re-throws it', async () => {
44
- const queue = createHookQueue();
45
- queue.schedule(async () => { throw new Error('once'); });
46
- await expect(queue.drain()).rejects.toThrow('once');
47
- // Second drain should see no accumulated error.
48
- await expect(queue.drain()).resolves.toBeUndefined();
49
- });
50
- it('surfaces only the first error when multiple hooks throw', async () => {
51
- const queue = createHookQueue();
52
- queue.schedule(async () => { throw new Error('first'); });
53
- queue.schedule(async () => { throw new Error('second'); });
54
- await expect(queue.drain()).rejects.toThrow('first');
55
- });
56
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
@@ -1,150 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from 'vitest';
2
- import { createDiagnostics, HarnessError } from '@react-native-harness/tools';
3
- import JestHarness from '../index.js';
4
- const resolveUndefined = async () => undefined;
5
- // ---------------------------------------------------------------------------
6
- // Module mocks
7
- // ---------------------------------------------------------------------------
8
- const mockSession = {
9
- config: { metroPort: 8081 },
10
- context: {},
11
- diagnostics: createDiagnostics({ enabled: false }),
12
- onTestRunnerEvent: vi.fn(() => () => undefined),
13
- ensureAppReady: vi.fn(resolveUndefined),
14
- runTestFile: vi.fn(async () => ({
15
- name: '',
16
- tests: [],
17
- suites: [],
18
- status: 'passed',
19
- duration: 0,
20
- })),
21
- restartApp: vi.fn(resolveUndefined),
22
- resetEnvironment: vi.fn(resolveUndefined),
23
- resetCrashState: vi.fn(),
24
- flushClientLogs: vi.fn(() => []),
25
- callHook: vi.fn(resolveUndefined),
26
- setRunState: vi.fn(),
27
- dispose: vi.fn(resolveUndefined),
28
- };
29
- const mockCreateHarnessSession = vi.hoisted(() => vi.fn(async () => mockSession));
30
- const mockExecuteRun = vi.hoisted(() => vi.fn(async () => undefined));
31
- vi.mock('../harness-session.js', () => ({
32
- createHarnessSession: mockCreateHarnessSession,
33
- }));
34
- vi.mock('../execute-run.js', () => ({
35
- executeRun: mockExecuteRun,
36
- }));
37
- // ---------------------------------------------------------------------------
38
- // Helpers
39
- // ---------------------------------------------------------------------------
40
- const makeOptions = () => ({ serial: true });
41
- const makeWatcher = () => ({ isInterrupted: () => false });
42
- const makeTest = () => ({
43
- path: '/test/example.ts',
44
- context: { config: {} },
45
- });
46
- const makeGlobalConfig = (overrides = {}) => ({ rootDir: '/project', watch: false, watchAll: false, collectCoverage: false, ...overrides });
47
- const typedMockExecuteRun = mockExecuteRun;
48
- const makeTestCaseStartPayload = () => [
49
- '/test/example.ts',
50
- {
51
- ancestorTitles: [],
52
- fullName: 'suite case',
53
- mode: undefined,
54
- title: 'case',
55
- startedAt: 1,
56
- },
57
- ];
58
- const makeTestCaseResultPayload = () => [
59
- '/test/example.ts',
60
- {
61
- ancestorTitles: [],
62
- duration: 1,
63
- failureDetails: [],
64
- failureMessages: [],
65
- fullName: 'suite case',
66
- numPassingAsserts: 1,
67
- startedAt: 1,
68
- status: 'passed',
69
- title: 'case',
70
- },
71
- ];
72
- // ---------------------------------------------------------------------------
73
- // Tests
74
- // ---------------------------------------------------------------------------
75
- beforeEach(() => {
76
- vi.clearAllMocks();
77
- mockSession.dispose.mockResolvedValue(undefined);
78
- typedMockExecuteRun.mockResolvedValue(undefined);
79
- mockCreateHarnessSession.mockResolvedValue(mockSession);
80
- });
81
- describe('JestHarness', () => {
82
- describe('session lifecycle', () => {
83
- it('creates a session on the first run', async () => {
84
- const runner = new JestHarness(makeGlobalConfig());
85
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
86
- expect(mockCreateHarnessSession).toHaveBeenCalledOnce();
87
- });
88
- it('reuses the session across runs in watch mode', async () => {
89
- const runner = new JestHarness(makeGlobalConfig({ watch: true }));
90
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
91
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
92
- expect(mockCreateHarnessSession).toHaveBeenCalledOnce();
93
- expect(mockSession.dispose).not.toHaveBeenCalled();
94
- });
95
- it('disposes the session after each run in normal mode', async () => {
96
- const runner = new JestHarness(makeGlobalConfig());
97
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
98
- expect(mockSession.dispose).toHaveBeenCalledOnce();
99
- });
100
- it('creates a fresh session for each run in normal mode', async () => {
101
- const runner = new JestHarness(makeGlobalConfig());
102
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
103
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
104
- expect(mockCreateHarnessSession).toHaveBeenCalledTimes(2);
105
- });
106
- it('emits registered Jest events', async () => {
107
- const runner = new JestHarness(makeGlobalConfig());
108
- const listener = vi.fn();
109
- const eventPayload = makeTestCaseStartPayload();
110
- runner.on('test-case-start', listener);
111
- typedMockExecuteRun.mockImplementationOnce(async (_session, _tests, _watcher, emitEvent) => {
112
- await emitEvent('test-case-start', ...eventPayload);
113
- });
114
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
115
- expect(listener).toHaveBeenCalledWith(eventPayload);
116
- });
117
- it('stops emitting after listener unsubscribe', async () => {
118
- const runner = new JestHarness(makeGlobalConfig());
119
- const listener = vi.fn();
120
- const unsubscribe = runner.on('test-case-result', listener);
121
- const eventPayload = makeTestCaseResultPayload();
122
- unsubscribe();
123
- typedMockExecuteRun.mockImplementationOnce(async (_session, _tests, _watcher, emitEvent) => {
124
- await emitEvent('test-case-result', ...eventPayload);
125
- });
126
- await runner.runTests([makeTest()], makeWatcher(), makeOptions());
127
- expect(listener).not.toHaveBeenCalled();
128
- });
129
- });
130
- describe('error handling', () => {
131
- it('converts HarnessError into a formatted string before throwing', async () => {
132
- class TestHarnessError extends HarnessError {
133
- constructor() { super('something went wrong'); this.name = 'TestHarnessError'; }
134
- }
135
- mockCreateHarnessSession.mockRejectedValue(new TestHarnessError());
136
- const runner = new JestHarness(makeGlobalConfig());
137
- await expect(runner.runTests([makeTest()], makeWatcher(), makeOptions())).rejects.toBeTypeOf('string');
138
- });
139
- it('propagates non-HarnessError exceptions as-is', async () => {
140
- const cause = new TypeError('unexpected');
141
- mockCreateHarnessSession.mockRejectedValue(cause);
142
- const runner = new JestHarness(makeGlobalConfig());
143
- await expect(runner.runTests([makeTest()], makeWatcher(), makeOptions())).rejects.toBe(cause);
144
- });
145
- it('throws when called without serial flag', async () => {
146
- const runner = new JestHarness(makeGlobalConfig());
147
- await expect(runner.runTests([makeTest()], makeWatcher(), { serial: false })).rejects.toThrow('Parallel test running is not supported');
148
- });
149
- });
150
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=metro-port.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metro-port.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/metro-port.test.ts"],"names":[],"mappings":""}
@@ -1,54 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { resolveHarnessMetroPort } from '../metro-port.js';
3
- const mocks = vi.hoisted(() => ({
4
- isPortAvailable: vi.fn(async () => true),
5
- }));
6
- vi.mock('@react-native-harness/bundler-metro', () => ({
7
- isPortAvailable: mocks.isPortAvailable,
8
- }));
9
- const createConfig = (overrides = {}) => ({
10
- appRegistryComponentName: 'App',
11
- bridgeTimeout: 60_000,
12
- bundleStartTimeout: 60_000,
13
- crashDetectionInterval: 500,
14
- defaultRunner: 'ios-device',
15
- detectNativeCrashes: true,
16
- disableViewFlattening: false,
17
- entryPoint: 'index.js',
18
- forwardClientLogs: false,
19
- diagnostics: false,
20
- maxAppRestarts: 2,
21
- metroPort: 8081,
22
- platformReadyTimeout: 300_000,
23
- resetEnvironmentBetweenTestFiles: true,
24
- runners: [],
25
- testTimeout: 5_000,
26
- unstable__enableMetroCache: false,
27
- unstable__skipAlreadyIncludedModules: false,
28
- ...overrides,
29
- });
30
- describe('resolveHarnessMetroPort', () => {
31
- it('skips fallback allocation for iOS physical device runners', async () => {
32
- const acquire = vi.fn();
33
- const config = createConfig();
34
- const platform = {
35
- config: {},
36
- name: 'ios-device',
37
- platformId: 'ios',
38
- runner: 'unused',
39
- };
40
- const result = await resolveHarnessMetroPort({
41
- config,
42
- platform,
43
- resourceLockManager: {
44
- acquire,
45
- },
46
- signal: new AbortController().signal,
47
- });
48
- expect(result.config).toBe(config);
49
- expect(result.metroPortLease).toBeNull();
50
- expect(result.didFallback).toBe(false);
51
- expect(acquire).not.toHaveBeenCalled();
52
- expect(mocks.isPortAvailable).not.toHaveBeenCalled();
53
- });
54
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=resource-lock.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resource-lock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/resource-lock.test.ts"],"names":[],"mappings":""}
@@ -1,138 +0,0 @@
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, vi } from 'vitest';
5
- import { createResourceLockManager } 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
- // The aborted waiter must have cleaned up its ticket. Verify by releasing
58
- // the first lock and confirming a fresh acquire completes immediately.
59
- await firstLease.release();
60
- const cleanupLease = await manager.acquire(key);
61
- await cleanupLease.release();
62
- });
63
- it('keeps queued tickets alive while the waiting process is still active', async () => {
64
- const manager = createResourceLockManager({
65
- rootDir,
66
- pollIntervalMs: 5,
67
- heartbeatIntervalMs: 20,
68
- staleLockTimeoutMs: 30,
69
- isProcessActive: () => true,
70
- });
71
- const key = 'ios:simulator:iPhone 17 Pro:26.2';
72
- const firstLease = await manager.acquire(key);
73
- const secondAcquire = manager.acquire(key);
74
- await new Promise((resolve) => setTimeout(resolve, 80));
75
- await firstLease.release();
76
- const secondLease = await secondAcquire;
77
- await secondLease.release();
78
- });
79
- it('reclaims a stale owner before granting the lock', async () => {
80
- const key = 'web:browser:chromium';
81
- // Simulate a live process holding the lock.
82
- const manager1 = createResourceLockManager({
83
- rootDir,
84
- pollIntervalMs: 5,
85
- heartbeatIntervalMs: 20,
86
- });
87
- const staleLease = await manager1.acquire(key);
88
- // A second manager whose isProcessActive always returns false will consider
89
- // any owner — including the live one above — immediately stale and reclaim it.
90
- const manager2 = createResourceLockManager({
91
- rootDir,
92
- pollIntervalMs: 5,
93
- heartbeatIntervalMs: 20,
94
- staleLockTimeoutMs: 50,
95
- isProcessActive: () => false,
96
- });
97
- const lease = await manager2.acquire(key);
98
- await lease.release();
99
- // manager1 was evicted; its release is best-effort.
100
- await staleLease.release();
101
- });
102
- it('keeps owner metadata valid when heartbeat writes overlap', async () => {
103
- const manager = createResourceLockManager({
104
- rootDir,
105
- pollIntervalMs: 5,
106
- heartbeatIntervalMs: 10,
107
- staleLockTimeoutMs: 200,
108
- });
109
- const key = 'ios:simulator:iPhone 17 Pro:26.2';
110
- const actualWriteFile = fs.writeFile.bind(fs);
111
- const writeFileSpy = vi
112
- .spyOn(fs, 'writeFile')
113
- .mockImplementation(async (file, data, options) => {
114
- // Delay atomic temp-file writes to simulate overlapping heartbeat flushes.
115
- if (typeof file === 'string' && file.startsWith(rootDir) && file.endsWith('.tmp')) {
116
- await new Promise((resolve) => setTimeout(resolve, 25));
117
- }
118
- return await actualWriteFile(file, data, options);
119
- });
120
- try {
121
- const lease = await manager.acquire(key);
122
- // Discover the owner file after acquire creates the key directory.
123
- const [keyDirName] = await fs.readdir(rootDir);
124
- const ownerFilePath = path.join(rootDir, keyDirName, 'owner.json');
125
- const initialOwner = JSON.parse(await fs.readFile(ownerFilePath, 'utf8'));
126
- await new Promise((resolve) => setTimeout(resolve, 80));
127
- for (let index = 0; index < 5; index += 1) {
128
- const owner = JSON.parse(await fs.readFile(ownerFilePath, 'utf8'));
129
- expect(owner.ticketId).toBe(initialOwner.ticketId);
130
- await new Promise((resolve) => setTimeout(resolve, 10));
131
- }
132
- await lease.release();
133
- }
134
- finally {
135
- writeFileSpy.mockRestore();
136
- }
137
- });
138
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=run.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/run.test.ts"],"names":[],"mappings":""}
@@ -1,48 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { runHarnessTestFile } from '../run.js';
3
- vi.mock('jest-message-util', () => ({
4
- formatResultsErrors: vi.fn(() => ''),
5
- }));
6
- const createHarnessResult = () => ({
7
- name: 'root',
8
- tests: [],
9
- suites: [],
10
- status: 'passed',
11
- duration: 0,
12
- });
13
- const createSession = (testTimeout) => ({
14
- config: { testTimeout },
15
- context: {
16
- platform: {
17
- runner: './runner.js',
18
- },
19
- },
20
- runTestFile: vi.fn(async () => createHarnessResult()),
21
- });
22
- const createGlobalConfig = (overrides = {}) => ({
23
- rootDir: '/project',
24
- ...overrides,
25
- });
26
- const createProjectConfig = (overrides = {}) => overrides;
27
- describe('runHarnessTestFile', () => {
28
- it('uses Harness config testTimeout when Jest config does not set one', async () => {
29
- const session = createSession(15000);
30
- await runHarnessTestFile({
31
- testPath: '/project/example.harness.ts',
32
- session,
33
- globalConfig: createGlobalConfig(),
34
- projectConfig: createProjectConfig(),
35
- });
36
- expect(session.runTestFile).toHaveBeenCalledWith('example.harness.ts', expect.objectContaining({ testTimeout: 15000 }));
37
- });
38
- it('keeps Harness config testTimeout above Jest config testTimeout', async () => {
39
- const session = createSession(15000);
40
- await runHarnessTestFile({
41
- testPath: '/project/example.harness.ts',
42
- session,
43
- globalConfig: createGlobalConfig({ testTimeout: 20000 }),
44
- projectConfig: createProjectConfig({ testTimeout: 30000 }),
45
- });
46
- expect(session.runTestFile).toHaveBeenCalledWith('example.harness.ts', expect.objectContaining({ testTimeout: 15000 }));
47
- });
48
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=test-file-platform-filter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-file-platform-filter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/test-file-platform-filter.test.ts"],"names":[],"mappings":""}
@@ -1,47 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createPlatformSkippedTestResult, getHarnessTestFilePlatform, shouldRunHarnessTestFile, } from '../test-file-platform-filter.js';
3
- const knownPlatformIds = new Set(['android', 'ios', 'web', 'vega']);
4
- describe('getHarnessTestFilePlatform', () => {
5
- it('returns null for shared harness test files', () => {
6
- expect(getHarnessTestFilePlatform('/tests/smoke.harness.ts', knownPlatformIds)).toBeNull();
7
- expect(getHarnessTestFilePlatform('/tests/smoke.harness.tsx', knownPlatformIds)).toBeNull();
8
- expect(getHarnessTestFilePlatform('/tests/smoke.harness.mjs', knownPlatformIds)).toBeNull();
9
- });
10
- it('returns the platform id for platform-specific harness test files', () => {
11
- expect(getHarnessTestFilePlatform('/tests/kotlin.android.harness.ts', knownPlatformIds)).toBe('android');
12
- expect(getHarnessTestFilePlatform('/tests/swift.ios.harness.ts', knownPlatformIds)).toBe('ios');
13
- expect(getHarnessTestFilePlatform('/tests/browser.web.harness.ts', knownPlatformIds)).toBe('web');
14
- });
15
- it('returns null when the segment before .harness is not a known platform id', () => {
16
- expect(getHarnessTestFilePlatform('/tests/smoke.harness.ts', knownPlatformIds)).toBeNull();
17
- expect(getHarnessTestFilePlatform('/tests/custom.foo.harness.ts', knownPlatformIds)).toBeNull();
18
- });
19
- });
20
- describe('shouldRunHarnessTestFile', () => {
21
- it('runs shared harness test files on every platform', () => {
22
- expect(shouldRunHarnessTestFile('/tests/smoke.harness.ts', 'android', knownPlatformIds)).toBe(true);
23
- expect(shouldRunHarnessTestFile('/tests/smoke.harness.ts', 'ios', knownPlatformIds)).toBe(true);
24
- });
25
- it('runs platform-specific files only on the matching platform', () => {
26
- expect(shouldRunHarnessTestFile('/tests/kotlin.android.harness.ts', 'android', knownPlatformIds)).toBe(true);
27
- expect(shouldRunHarnessTestFile('/tests/kotlin.android.harness.ts', 'ios', knownPlatformIds)).toBe(false);
28
- expect(shouldRunHarnessTestFile('/tests/swift.ios.harness.ts', 'ios', knownPlatformIds)).toBe(true);
29
- expect(shouldRunHarnessTestFile('/tests/swift.ios.harness.ts', 'android', knownPlatformIds)).toBe(false);
30
- });
31
- });
32
- describe('createPlatformSkippedTestResult', () => {
33
- it('marks the entire test file as skipped', () => {
34
- const result = createPlatformSkippedTestResult('/tests/swift.ios.harness.ts');
35
- expect(result.skipped).toBe(true);
36
- expect(result.numPassingTests).toBe(0);
37
- expect(result.numFailingTests).toBe(0);
38
- expect(result.numPendingTests).toBe(1);
39
- expect(result.testResults).toHaveLength(1);
40
- expect(result.testResults[0]).toEqual(expect.objectContaining({
41
- status: 'skipped',
42
- title: 'swift.ios.harness.ts',
43
- fullName: 'swift.ios.harness.ts',
44
- }));
45
- expect(result.testFilePath).toBe('/tests/swift.ios.harness.ts');
46
- });
47
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=cli-args.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-args.test.d.ts","sourceRoot":"","sources":["../src/cli-args.test.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- export {};
@@ -1,21 +0,0 @@
1
- import { type AppMonitor, type HarnessPlatformRunner } from '@react-native-harness/platforms';
2
- export type CrashSupervisorState = 'idle' | 'launching' | 'ready' | 'running' | 'disposing';
3
- export type CrashSupervisor = {
4
- setActiveTestFile: (testFilePath: string | null) => void;
5
- beginLaunch: (testFilePath: string) => void;
6
- markReady: () => void;
7
- beginTestRun: (testFilePath: string) => void;
8
- stop: () => Promise<void>;
9
- start: () => Promise<void>;
10
- waitForCrash: (testFilePath: string) => Promise<never>;
11
- isReady: () => boolean;
12
- cancelCrashWaiters: () => void;
13
- reset: () => void;
14
- dispose: () => Promise<void>;
15
- };
16
- export type CrashSupervisorOptions = {
17
- appMonitor: AppMonitor;
18
- platformRunner: HarnessPlatformRunner;
19
- };
20
- export declare const createCrashSupervisor: ({ appMonitor, platformRunner, }: CrashSupervisorOptions) => CrashSupervisor;
21
- //# sourceMappingURL=crash-supervisor.d.ts.map
@@ -1 +0,0 @@
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"}