@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.
Files changed (86) hide show
  1. package/package.json +8 -8
  2. package/dist/__tests__/bridge.test.d.ts +0 -2
  3. package/dist/__tests__/bridge.test.d.ts.map +0 -1
  4. package/dist/__tests__/bridge.test.js +0 -167
  5. package/dist/__tests__/client-log-handler.test.d.ts +0 -2
  6. package/dist/__tests__/client-log-handler.test.d.ts.map +0 -1
  7. package/dist/__tests__/client-log-handler.test.js +0 -306
  8. package/dist/__tests__/crash-monitor.test.d.ts +0 -2
  9. package/dist/__tests__/crash-monitor.test.d.ts.map +0 -1
  10. package/dist/__tests__/crash-monitor.test.js +0 -230
  11. package/dist/__tests__/crash-supervisor.test.d.ts +0 -2
  12. package/dist/__tests__/crash-supervisor.test.d.ts.map +0 -1
  13. package/dist/__tests__/crash-supervisor.test.js +0 -151
  14. package/dist/__tests__/environment-reset.test.d.ts +0 -2
  15. package/dist/__tests__/environment-reset.test.d.ts.map +0 -1
  16. package/dist/__tests__/environment-reset.test.js +0 -238
  17. package/dist/__tests__/errors.test.d.ts +0 -2
  18. package/dist/__tests__/errors.test.d.ts.map +0 -1
  19. package/dist/__tests__/errors.test.js +0 -98
  20. package/dist/__tests__/execute-run.test.d.ts +0 -2
  21. package/dist/__tests__/execute-run.test.d.ts.map +0 -1
  22. package/dist/__tests__/execute-run.test.js +0 -639
  23. package/dist/__tests__/fixtures/mock-platform-runner.d.ts +0 -7
  24. package/dist/__tests__/fixtures/mock-platform-runner.d.ts.map +0 -1
  25. package/dist/__tests__/fixtures/mock-platform-runner.js +0 -6
  26. package/dist/__tests__/format-harness-error.test.d.ts +0 -2
  27. package/dist/__tests__/format-harness-error.test.d.ts.map +0 -1
  28. package/dist/__tests__/format-harness-error.test.js +0 -27
  29. package/dist/__tests__/harness-cache.test.d.ts +0 -2
  30. package/dist/__tests__/harness-cache.test.d.ts.map +0 -1
  31. package/dist/__tests__/harness-cache.test.js +0 -72
  32. package/dist/__tests__/harness-session-signal.test.d.ts +0 -2
  33. package/dist/__tests__/harness-session-signal.test.d.ts.map +0 -1
  34. package/dist/__tests__/harness-session-signal.test.js +0 -298
  35. package/dist/__tests__/harness-session.test.d.ts +0 -2
  36. package/dist/__tests__/harness-session.test.d.ts.map +0 -1
  37. package/dist/__tests__/harness-session.test.js +0 -233
  38. package/dist/__tests__/harness.test.d.ts +0 -2
  39. package/dist/__tests__/harness.test.d.ts.map +0 -1
  40. package/dist/__tests__/harness.test.js +0 -636
  41. package/dist/__tests__/hook-queue.test.d.ts +0 -2
  42. package/dist/__tests__/hook-queue.test.d.ts.map +0 -1
  43. package/dist/__tests__/hook-queue.test.js +0 -56
  44. package/dist/__tests__/index.test.d.ts +0 -2
  45. package/dist/__tests__/index.test.d.ts.map +0 -1
  46. package/dist/__tests__/index.test.js +0 -150
  47. package/dist/__tests__/metro-port.test.d.ts +0 -2
  48. package/dist/__tests__/metro-port.test.d.ts.map +0 -1
  49. package/dist/__tests__/metro-port.test.js +0 -54
  50. package/dist/__tests__/resource-lock.test.d.ts +0 -2
  51. package/dist/__tests__/resource-lock.test.d.ts.map +0 -1
  52. package/dist/__tests__/resource-lock.test.js +0 -138
  53. package/dist/__tests__/run.test.d.ts +0 -2
  54. package/dist/__tests__/run.test.d.ts.map +0 -1
  55. package/dist/__tests__/run.test.js +0 -48
  56. package/dist/__tests__/test-file-platform-filter.test.d.ts +0 -2
  57. package/dist/__tests__/test-file-platform-filter.test.d.ts.map +0 -1
  58. package/dist/__tests__/test-file-platform-filter.test.js +0 -47
  59. package/dist/cli-args.test.d.ts +0 -2
  60. package/dist/cli-args.test.d.ts.map +0 -1
  61. package/dist/cli-args.test.js +0 -1
  62. package/dist/diagnostics/__tests__/derivers.test.d.ts +0 -2
  63. package/dist/diagnostics/__tests__/derivers.test.d.ts.map +0 -1
  64. package/dist/diagnostics/__tests__/derivers.test.js +0 -248
  65. package/dist/diagnostics/__tests__/report.test.d.ts +0 -2
  66. package/dist/diagnostics/__tests__/report.test.d.ts.map +0 -1
  67. package/dist/diagnostics/__tests__/report.test.js +0 -97
  68. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  69. package/src/__tests__/bridge.test.ts +0 -224
  70. package/src/__tests__/client-log-handler.test.ts +0 -364
  71. package/src/__tests__/crash-monitor.test.ts +0 -323
  72. package/src/__tests__/environment-reset.test.ts +0 -308
  73. package/src/__tests__/errors.test.ts +0 -157
  74. package/src/__tests__/execute-run.test.ts +0 -849
  75. package/src/__tests__/format-harness-error.test.ts +0 -32
  76. package/src/__tests__/harness-cache.test.ts +0 -91
  77. package/src/__tests__/harness-session.test.ts +0 -303
  78. package/src/__tests__/hook-queue.test.ts +0 -77
  79. package/src/__tests__/index.test.ts +0 -221
  80. package/src/__tests__/metro-port.test.ts +0 -63
  81. package/src/__tests__/resource-lock.test.ts +0 -179
  82. package/src/__tests__/run.test.ts +0 -74
  83. package/src/__tests__/test-file-platform-filter.test.ts +0 -83
  84. package/src/diagnostics/__tests__/derivers.test.ts +0 -300
  85. package/src/diagnostics/__tests__/report.test.ts +0 -128
  86. package/tsconfig.tsbuildinfo +0 -1
@@ -1,639 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from 'vitest';
2
- import { NativeCrashError, StartupStallError } from '../errors.js';
3
- import { AppBridgeDisconnectedError, DeviceNotRespondingError, } from '@react-native-harness/bridge/server';
4
- import { createDiagnostics } from '@react-native-harness/tools';
5
- import { executeRun } from '../execute-run.js';
6
- const resolveUndefined = async () => undefined;
7
- // Mock the file-runner so we control what jestResult/harnessResult each test returns
8
- // without needing a real Jest config or Metro bundler.
9
- const mockRunHarnessTestFile = vi.hoisted(() => vi.fn());
10
- vi.mock('../run.js', () => ({
11
- runHarnessTestFile: mockRunHarnessTestFile,
12
- }));
13
- // Mock the reporting module so diagnostics tests can inspect the entries that
14
- // would have been reported without touching the real filesystem, and so
15
- // asserting on them doesn't race with executeRun's own diagnostics.flush().
16
- const mockPrintSummary = vi.hoisted(() => vi.fn());
17
- const mockWriteTraceFile = vi.hoisted(() => vi.fn(async () => '/fake/trace.json'));
18
- vi.mock('../diagnostics/index.js', () => ({
19
- printSummary: mockPrintSummary,
20
- writeTraceFile: mockWriteTraceFile,
21
- }));
22
- // ---------------------------------------------------------------------------
23
- // Helpers
24
- // ---------------------------------------------------------------------------
25
- const makeJestResult = (overrides = {}) => ({
26
- testFilePath: '/test/example.ts',
27
- testResults: [],
28
- failureMessage: null,
29
- numFailingTests: 0,
30
- numPassingTests: 1,
31
- numPendingTests: 0,
32
- numTodoTests: 0,
33
- leaks: false,
34
- openHandles: [],
35
- skipped: false,
36
- snapshot: {
37
- added: 0, fileDeleted: false, matched: 0, unchecked: 0,
38
- uncheckedKeys: [], unmatched: 0, updated: 0,
39
- },
40
- perfStats: { start: 0, end: 1, runtime: 1, slow: false,
41
- loadTestEnvironmentStart: 0, loadTestEnvironmentEnd: 0,
42
- setupAfterEnvStart: 0, setupAfterEnvEnd: 0,
43
- setupFilesStart: 0, setupFilesEnd: 0 },
44
- coverage: undefined,
45
- ...overrides,
46
- });
47
- const makeHarnessResult = (status = 'passed') => ({
48
- name: 'suite',
49
- tests: [],
50
- suites: [],
51
- status,
52
- duration: 50,
53
- });
54
- const makeFileRunResult = (overrides = {}) => ({
55
- jestResult: makeJestResult(),
56
- harnessResult: makeHarnessResult(),
57
- relativeTestPath: 'example.ts',
58
- duration: 50,
59
- ...overrides,
60
- });
61
- const makeTest = (filePath = '/test/example.ts') => ({
62
- path: filePath,
63
- context: { config: {} },
64
- });
65
- const makeWatcher = (interrupted = false) => ({ isInterrupted: () => interrupted });
66
- const makeGlobalConfig = (overrides = {}) => ({
67
- rootDir: '/project',
68
- watch: false,
69
- watchAll: false,
70
- collectCoverage: false,
71
- ...overrides,
72
- });
73
- const makeSession = (overrides = {}) => ({
74
- config: {
75
- metroPort: 8081,
76
- host: undefined,
77
- resetEnvironmentBetweenTestFiles: false,
78
- detectNativeCrashes: true,
79
- runners: [
80
- { platformId: 'android', name: 'android' },
81
- { platformId: 'ios', name: 'ios' },
82
- ],
83
- },
84
- context: {
85
- platform: {
86
- platformId: 'android',
87
- name: 'android',
88
- runner: '/virtual/android-runner.js',
89
- cli: '/virtual/android-cli.js',
90
- config: {},
91
- },
92
- },
93
- diagnostics: createDiagnostics({ enabled: false }),
94
- ensureAppReady: vi.fn(resolveUndefined),
95
- runTestFile: vi.fn(async () => makeHarnessResult()),
96
- restartApp: vi.fn(resolveUndefined),
97
- resetEnvironment: vi.fn(resolveUndefined),
98
- resetCrashState: vi.fn(),
99
- flushClientLogs: vi.fn(() => []),
100
- callHook: vi.fn(resolveUndefined),
101
- onTestRunnerEvent: vi.fn(() => () => undefined),
102
- setRunState: vi.fn(),
103
- dispose: vi.fn(resolveUndefined),
104
- ...overrides,
105
- });
106
- const makeEmitEvent = () => {
107
- const calls = [];
108
- const emitEvent = (async (...eventData) => {
109
- calls.push(eventData);
110
- });
111
- return { emitEvent, calls };
112
- };
113
- // ---------------------------------------------------------------------------
114
- // Tests
115
- // ---------------------------------------------------------------------------
116
- beforeEach(() => {
117
- vi.clearAllMocks();
118
- mockRunHarnessTestFile.mockResolvedValue(makeFileRunResult());
119
- });
120
- describe('executeRun', () => {
121
- describe('lifecycle hooks', () => {
122
- it('emits run:started then run:finished in order', async () => {
123
- const hookNames = [];
124
- const session = makeSession({
125
- callHook: vi.fn(async (name) => { hookNames.push(name); }),
126
- });
127
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
128
- expect(hookNames[0]).toBe('run:started');
129
- expect(hookNames[hookNames.length - 1]).toBe('run:finished');
130
- });
131
- it('emits test-file:started and test-file:finished around each test', async () => {
132
- const hookNames = [];
133
- const session = makeSession({
134
- callHook: vi.fn(async (name) => { hookNames.push(name); }),
135
- });
136
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
137
- const fileHooks = hookNames.filter((n) => n === 'test-file:started' || n === 'test-file:finished');
138
- expect(fileHooks).toEqual([
139
- 'test-file:started', 'test-file:finished',
140
- 'test-file:started', 'test-file:finished',
141
- ]);
142
- });
143
- it('always emits run:finished even when a test throws', async () => {
144
- const hookNames = [];
145
- const session = makeSession({
146
- callHook: vi.fn(async (name) => { hookNames.push(name); }),
147
- ensureAppReady: vi.fn(async () => { throw new Error('unexpected'); }),
148
- });
149
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
150
- expect(hookNames).toContain('run:finished');
151
- });
152
- });
153
- describe('diagnostics', () => {
154
- beforeEach(() => {
155
- mockPrintSummary.mockClear();
156
- mockWriteTraceFile.mockClear();
157
- });
158
- it('records run.total and run.file spans when diagnostics is enabled', async () => {
159
- const diagnostics = createDiagnostics({ enabled: true });
160
- const session = makeSession({ diagnostics });
161
- await executeRun(session, [makeTest('/a.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
162
- // executeRun flushes and hands entries to the (mocked) reporter in its
163
- // finally block, so we inspect what was reported rather than flushing
164
- // the diagnostics instance ourselves.
165
- expect(mockPrintSummary).toHaveBeenCalledTimes(1);
166
- const entries = mockPrintSummary.mock.calls[0]?.[0];
167
- const runEntry = entries.find((e) => e.label === 'run.total');
168
- const fileEntry = entries.find((e) => e.label === 'run.file');
169
- expect(runEntry).toMatchObject({ status: 'ok', attrs: { status: 'passed' } });
170
- expect(runEntry?.attrs?.runId).toBeTypeOf('string');
171
- expect(fileEntry).toMatchObject({ status: 'ok', attrs: { file: '../a.ts', status: 'passed' } });
172
- expect(fileEntry?.attrs?.runId).toBeTypeOf('string');
173
- expect(mockWriteTraceFile).toHaveBeenCalledWith(entries, expect.objectContaining({ runId: expect.any(String) }));
174
- });
175
- it('still records run.total when a test throws', async () => {
176
- const diagnostics = createDiagnostics({ enabled: true });
177
- const session = makeSession({
178
- diagnostics,
179
- ensureAppReady: vi.fn(async () => { throw new Error('unexpected'); }),
180
- });
181
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
182
- const entries = mockPrintSummary.mock.calls[0]?.[0];
183
- expect(entries.find((e) => e.label === 'run.total')).toBeDefined();
184
- expect(entries.find((e) => e.label === 'run.file')).toMatchObject({
185
- attrs: { status: 'failed' },
186
- });
187
- });
188
- it('is a no-op and does not generate a report when diagnostics is disabled', async () => {
189
- const diagnostics = createDiagnostics({ enabled: false });
190
- const session = makeSession({ diagnostics });
191
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
192
- expect(diagnostics.flush()).toEqual([]);
193
- expect(mockPrintSummary).not.toHaveBeenCalled();
194
- expect(mockWriteTraceFile).not.toHaveBeenCalled();
195
- });
196
- });
197
- describe('happy path', () => {
198
- it('emits file start, ensureAppReady, runTestFile, file success in order', async () => {
199
- const order = [];
200
- const session = makeSession({
201
- ensureAppReady: vi.fn(async () => { order.push('ensureAppReady'); }),
202
- });
203
- const emitEvent = (async (eventName) => {
204
- if (eventName === 'test-file-start') {
205
- order.push('test-file-start');
206
- }
207
- if (eventName === 'test-file-success') {
208
- order.push('test-file-success');
209
- }
210
- });
211
- mockRunHarnessTestFile.mockImplementation(async () => {
212
- order.push('runTestFile');
213
- return makeFileRunResult();
214
- });
215
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
216
- expect(order).toEqual([
217
- 'test-file-start',
218
- 'ensureAppReady',
219
- 'runTestFile',
220
- 'test-file-success',
221
- ]);
222
- });
223
- it('accumulates passing test counts in run:finished summary', async () => {
224
- let finishedPayload;
225
- const session = makeSession({
226
- callHook: vi.fn(async (name, payload) => {
227
- if (name === 'run:finished')
228
- finishedPayload = payload;
229
- }),
230
- });
231
- mockRunHarnessTestFile.mockResolvedValue(makeFileRunResult({ jestResult: makeJestResult({ numPassingTests: 3 }) }));
232
- await executeRun(session, [makeTest(), makeTest('/b.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
233
- const payload = finishedPayload;
234
- expect(payload.summary.passed).toBe(6);
235
- expect(payload.status).toBe('passed');
236
- });
237
- it('marks the final run state as completed', async () => {
238
- const session = makeSession();
239
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
240
- expect(session.setRunState).toHaveBeenLastCalledWith(expect.objectContaining({
241
- completed: true,
242
- status: 'passed',
243
- }));
244
- });
245
- it('attaches buffered client logs to the Jest result', async () => {
246
- const clientLogs = [{ message: 'Loaded screen', origin: '', type: 'warn' }];
247
- const session = makeSession({
248
- flushClientLogs: vi.fn()
249
- .mockReturnValueOnce([])
250
- .mockReturnValueOnce(clientLogs),
251
- });
252
- const { emitEvent, calls } = makeEmitEvent();
253
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
254
- expect(session.flushClientLogs).toHaveBeenCalledTimes(2);
255
- expect(calls).toContainEqual([
256
- 'test-file-success',
257
- expect.anything(),
258
- expect.objectContaining({ console: clientLogs }),
259
- ]);
260
- });
261
- it('emits test-case events before file success', async () => {
262
- let testRunnerListener;
263
- const { emitEvent, calls: emittedEvents } = makeEmitEvent();
264
- const session = makeSession({
265
- onTestRunnerEvent: vi.fn((listener) => {
266
- testRunnerListener = listener;
267
- return () => undefined;
268
- }),
269
- });
270
- mockRunHarnessTestFile.mockImplementation(async () => {
271
- testRunnerListener?.({
272
- type: 'test-started',
273
- file: 'example.ts',
274
- suite: 'suite',
275
- name: 'works',
276
- ancestorTitles: ['suite'],
277
- fullName: 'suite works',
278
- startedAt: 10,
279
- declarationMode: 'only',
280
- });
281
- testRunnerListener?.({
282
- type: 'test-finished',
283
- file: 'example.ts',
284
- suite: 'suite',
285
- name: 'works',
286
- ancestorTitles: ['suite'],
287
- fullName: 'suite works',
288
- startedAt: 10,
289
- declarationMode: 'only',
290
- duration: 5,
291
- status: 'passed',
292
- });
293
- return makeFileRunResult();
294
- });
295
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
296
- expect(emittedEvents).toEqual([
297
- ['test-file-start', expect.anything()],
298
- [
299
- 'test-case-start',
300
- 'example.ts',
301
- expect.objectContaining({
302
- ancestorTitles: ['suite'],
303
- fullName: 'suite works',
304
- mode: 'only',
305
- title: 'works',
306
- startedAt: 10,
307
- }),
308
- ],
309
- [
310
- 'test-case-result',
311
- 'example.ts',
312
- expect.objectContaining({
313
- ancestorTitles: ['suite'],
314
- fullName: 'suite works',
315
- numPassingAsserts: 1,
316
- startedAt: 10,
317
- status: 'passed',
318
- title: 'works',
319
- }),
320
- ],
321
- ['test-file-success', expect.anything(), expect.anything()],
322
- ]);
323
- });
324
- it('includes pending promise diagnostics in live test-case failures', async () => {
325
- let testRunnerListener;
326
- const { emitEvent, calls: emittedEvents } = makeEmitEvent();
327
- const session = makeSession({
328
- onTestRunnerEvent: vi.fn((listener) => {
329
- testRunnerListener = listener;
330
- return () => undefined;
331
- }),
332
- });
333
- mockRunHarnessTestFile.mockImplementation(async () => {
334
- testRunnerListener?.({
335
- type: 'test-finished',
336
- file: 'example.ts',
337
- suite: 'suite',
338
- name: 'hangs',
339
- ancestorTitles: ['suite'],
340
- fullName: 'suite hangs',
341
- startedAt: 100,
342
- duration: 50,
343
- status: 'failed',
344
- error: {
345
- name: 'TestCaseTimeoutError',
346
- message: 'Test timed out after 50ms: suite hangs',
347
- diagnostics: {
348
- pendingPromises: {
349
- total: 1,
350
- items: [
351
- {
352
- id: 7,
353
- createdAt: 110,
354
- stack: 'Error: Promise created\n at hangs (example.ts:10:5)',
355
- },
356
- ],
357
- },
358
- },
359
- },
360
- });
361
- return makeFileRunResult();
362
- });
363
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
364
- expect(emittedEvents).toContainEqual([
365
- 'test-case-result',
366
- 'example.ts',
367
- expect.objectContaining({
368
- failureMessages: [
369
- expect.stringContaining('Pending promises at timeout: 1\n\nPromise #7, created 10ms after test start:'),
370
- ],
371
- }),
372
- ]);
373
- });
374
- });
375
- describe('runtime failures', () => {
376
- it('passes StartupStallError to onFailure with an empty stack', async () => {
377
- const { emitEvent, calls } = makeEmitEvent();
378
- const session = makeSession({
379
- ensureAppReady: vi.fn().mockRejectedValue(new StartupStallError(1500, 3)),
380
- });
381
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
382
- expect(calls).toContainEqual([
383
- 'test-file-failure',
384
- expect.objectContaining({ path: '/test/example.ts' }),
385
- expect.objectContaining({ message: expect.stringContaining('1500'), stack: '' }),
386
- ]);
387
- });
388
- it('passes DeviceNotRespondingError to onFailure with an empty stack', async () => {
389
- const { emitEvent, calls } = makeEmitEvent();
390
- const session = makeSession({
391
- ensureAppReady: vi.fn().mockRejectedValue(new DeviceNotRespondingError('runTests', [])),
392
- });
393
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
394
- expect(calls).toContainEqual([
395
- 'test-file-failure',
396
- expect.anything(),
397
- expect.objectContaining({ stack: '' }),
398
- ]);
399
- });
400
- it('passes AppBridgeDisconnectedError to onFailure with an empty stack', async () => {
401
- const { emitEvent, calls } = makeEmitEvent();
402
- const session = makeSession({
403
- ensureAppReady: vi.fn().mockRejectedValue(new AppBridgeDisconnectedError('app-disconnected')),
404
- });
405
- await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig());
406
- expect(calls).toContainEqual([
407
- 'test-file-failure',
408
- expect.anything(),
409
- expect.objectContaining({
410
- message: expect.stringContaining('The app bridge disconnected during test execution.'),
411
- stack: '',
412
- }),
413
- ]);
414
- });
415
- it('calls resetCrashState after a NativeCrashError', async () => {
416
- const session = makeSession({
417
- ensureAppReady: vi.fn().mockRejectedValue(new NativeCrashError('example.ts', { phase: 'execution', source: 'polling' })),
418
- });
419
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
420
- expect(session.resetCrashState).toHaveBeenCalled();
421
- });
422
- it('counts runtime failures in the summary', async () => {
423
- let finishedPayload;
424
- const session = makeSession({
425
- callHook: vi.fn(async (name, payload) => {
426
- if (name === 'run:finished')
427
- finishedPayload = payload;
428
- }),
429
- ensureAppReady: vi.fn().mockRejectedValue(new StartupStallError(1000, 2)),
430
- });
431
- await executeRun(session, [makeTest()], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
432
- const payload = finishedPayload;
433
- expect(payload.summary.failed).toBe(1);
434
- expect(payload.status).toBe('failed');
435
- });
436
- });
437
- describe('watcher interrupt', () => {
438
- it('stops processing tests and still emits run:finished', async () => {
439
- const hookNames = [];
440
- const session = makeSession({
441
- callHook: vi.fn(async (name) => { hookNames.push(name); }),
442
- });
443
- const { emitEvent, calls } = makeEmitEvent();
444
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts')], makeWatcher(true /* interrupted */), emitEvent, makeGlobalConfig());
445
- // No test was started; watcher was already interrupted.
446
- expect(calls).not.toContainEqual(['test-file-start', expect.anything()]);
447
- expect(hookNames).toContain('run:finished');
448
- });
449
- });
450
- describe('resetEnvironmentBetweenTestFiles', () => {
451
- it('calls resetEnvironment before the second test but not the first', async () => {
452
- const session = makeSession({
453
- config: {
454
- metroPort: 8081,
455
- resetEnvironmentBetweenTestFiles: true,
456
- detectNativeCrashes: false,
457
- runners: [
458
- { platformId: 'android', name: 'android' },
459
- { platformId: 'ios', name: 'ios' },
460
- ],
461
- },
462
- });
463
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts'), makeTest('/c.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
464
- // resetEnvironment should be called for tests 2 and 3, not test 1.
465
- expect(session.resetEnvironment).toHaveBeenCalledTimes(2);
466
- expect(session.restartApp).not.toHaveBeenCalled();
467
- });
468
- it('calls resetEnvironment with the "runtime" strategy configured', async () => {
469
- const session = makeSession({
470
- config: {
471
- metroPort: 8081,
472
- resetEnvironmentBetweenTestFiles: 'runtime',
473
- detectNativeCrashes: false,
474
- runners: [
475
- { platformId: 'android', name: 'android' },
476
- { platformId: 'ios', name: 'ios' },
477
- ],
478
- },
479
- });
480
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
481
- expect(session.resetEnvironment).toHaveBeenCalledTimes(1);
482
- expect(session.resetEnvironment).toHaveBeenCalledWith('/b.ts');
483
- });
484
- it('does not call resetEnvironment when resetEnvironmentBetweenTestFiles is false', async () => {
485
- const session = makeSession({
486
- config: {
487
- metroPort: 8081,
488
- resetEnvironmentBetweenTestFiles: false,
489
- detectNativeCrashes: false,
490
- runners: [
491
- { platformId: 'android', name: 'android' },
492
- { platformId: 'ios', name: 'ios' },
493
- ],
494
- },
495
- });
496
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
497
- expect(session.resetEnvironment).not.toHaveBeenCalled();
498
- });
499
- it('restarts after a test case timeout before the next runnable file', async () => {
500
- const timedOutResult = makeHarnessResult('failed');
501
- timedOutResult.tests = [
502
- {
503
- name: 'hangs',
504
- status: 'failed',
505
- duration: 10,
506
- error: {
507
- name: 'TestCaseTimeoutError',
508
- message: 'Test timed out after 10ms: hangs',
509
- },
510
- },
511
- ];
512
- mockRunHarnessTestFile
513
- .mockResolvedValueOnce(makeFileRunResult({
514
- harnessResult: timedOutResult,
515
- jestResult: makeJestResult({
516
- numFailingTests: 1,
517
- numPassingTests: 0,
518
- }),
519
- }))
520
- .mockResolvedValueOnce(makeFileRunResult());
521
- const session = makeSession({
522
- config: {
523
- metroPort: 8081,
524
- resetEnvironmentBetweenTestFiles: false,
525
- detectNativeCrashes: false,
526
- runners: [
527
- { platformId: 'android', name: 'android' },
528
- { platformId: 'ios', name: 'ios' },
529
- ],
530
- },
531
- });
532
- await executeRun(session, [makeTest('/a.ts'), makeTest('/b.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
533
- expect(session.restartApp).toHaveBeenCalledTimes(1);
534
- expect(session.restartApp).toHaveBeenCalledWith('/a.ts');
535
- });
536
- it('restarts after a timeout in the last runnable file', async () => {
537
- const timedOutResult = makeHarnessResult('failed');
538
- timedOutResult.tests = [
539
- {
540
- name: 'hangs',
541
- status: 'failed',
542
- duration: 10,
543
- error: {
544
- name: 'TestCaseTimeoutError',
545
- message: 'Test timed out after 10ms: hangs',
546
- },
547
- },
548
- ];
549
- mockRunHarnessTestFile.mockResolvedValueOnce(makeFileRunResult({
550
- harnessResult: timedOutResult,
551
- jestResult: makeJestResult({
552
- numFailingTests: 1,
553
- numPassingTests: 0,
554
- }),
555
- }));
556
- const session = makeSession({
557
- config: {
558
- metroPort: 8081,
559
- resetEnvironmentBetweenTestFiles: false,
560
- detectNativeCrashes: false,
561
- runners: [
562
- { platformId: 'android', name: 'android' },
563
- { platformId: 'ios', name: 'ios' },
564
- ],
565
- },
566
- });
567
- await executeRun(session, [makeTest('/a.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig({ watch: true }));
568
- expect(session.restartApp).toHaveBeenCalledTimes(1);
569
- expect(session.restartApp).toHaveBeenCalledWith('/a.ts');
570
- });
571
- it('restarts after a suite hook timeout', async () => {
572
- const timedOutResult = makeHarnessResult('failed');
573
- timedOutResult.suites = [
574
- {
575
- name: 'suite',
576
- tests: [],
577
- suites: [],
578
- status: 'failed',
579
- duration: 10,
580
- error: {
581
- name: 'SuiteHookTimeoutError',
582
- message: 'beforeAll hook timed out after 10ms in suite: suite',
583
- },
584
- },
585
- ];
586
- mockRunHarnessTestFile.mockResolvedValueOnce(makeFileRunResult({
587
- harnessResult: timedOutResult,
588
- jestResult: makeJestResult({
589
- numFailingTests: 1,
590
- numPassingTests: 0,
591
- }),
592
- }));
593
- const session = makeSession();
594
- await executeRun(session, [makeTest('/a.ts')], makeWatcher(), makeEmitEvent().emitEvent, makeGlobalConfig());
595
- expect(session.restartApp).toHaveBeenCalledTimes(1);
596
- expect(session.restartApp).toHaveBeenCalledWith('/a.ts');
597
- });
598
- });
599
- describe('platform-specific test files', () => {
600
- it('skips files for other platforms without running them on the device', async () => {
601
- const session = makeSession();
602
- const { emitEvent, calls } = makeEmitEvent();
603
- await executeRun(session, [
604
- makeTest('/project/smoke.harness.ts'),
605
- makeTest('/project/kotlin.android.harness.ts'),
606
- makeTest('/project/swift.ios.harness.ts'),
607
- ], makeWatcher(), emitEvent, makeGlobalConfig());
608
- expect(mockRunHarnessTestFile).toHaveBeenCalledTimes(2);
609
- expect(mockRunHarnessTestFile).toHaveBeenCalledWith(expect.objectContaining({ testPath: '/project/smoke.harness.ts' }));
610
- expect(mockRunHarnessTestFile).toHaveBeenCalledWith(expect.objectContaining({ testPath: '/project/kotlin.android.harness.ts' }));
611
- expect(session.ensureAppReady).toHaveBeenCalledTimes(2);
612
- expect(calls).toContainEqual([
613
- 'test-file-success',
614
- expect.objectContaining({ path: '/project/swift.ios.harness.ts' }),
615
- expect.objectContaining({ skipped: true }),
616
- ]);
617
- });
618
- it('does not restart the app for skipped platform-specific files', async () => {
619
- const session = makeSession({
620
- config: {
621
- metroPort: 8081,
622
- resetEnvironmentBetweenTestFiles: true,
623
- detectNativeCrashes: false,
624
- runners: [
625
- { platformId: 'android', name: 'android' },
626
- { platformId: 'ios', name: 'ios' },
627
- ],
628
- },
629
- });
630
- await executeRun(session, [
631
- makeTest('/project/swift.ios.harness.ts'),
632
- makeTest('/project/kotlin.android.harness.ts'),
633
- ], makeWatcher(), vi.fn(), makeGlobalConfig());
634
- expect(session.restartApp).not.toHaveBeenCalled();
635
- expect(session.resetEnvironment).not.toHaveBeenCalled();
636
- expect(mockRunHarnessTestFile).toHaveBeenCalledTimes(1);
637
- });
638
- });
639
- });
@@ -1,7 +0,0 @@
1
- import type { HarnessPlatformInitOptions, HarnessPlatformRunner } from '@react-native-harness/platforms';
2
- import type { Config as HarnessConfig } from '@react-native-harness/config';
3
- declare global {
4
- var __HARNESS_TEST_PLATFORM_RUNNER__: HarnessPlatformRunner | undefined;
5
- }
6
- export default function mockPlatformRunner(_platformConfig: Record<string, unknown>, _harnessConfig: HarnessConfig, _init: HarnessPlatformInitOptions): Promise<HarnessPlatformRunner>;
7
- //# sourceMappingURL=mock-platform-runner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mock-platform-runner.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/mock-platform-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,gCAAgC,EAChC,qBAAqB,GACrB,SAAS,CAAC;CACf;AAED,wBAA8B,kBAAkB,CAC9C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,cAAc,EAAE,aAAa,EAC7B,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,qBAAqB,CAAC,CAMhC"}
@@ -1,6 +0,0 @@
1
- export default async function mockPlatformRunner(_platformConfig, _harnessConfig, _init) {
2
- if (!globalThis.__HARNESS_TEST_PLATFORM_RUNNER__) {
3
- throw new Error('Missing __HARNESS_TEST_PLATFORM_RUNNER__ test fixture');
4
- }
5
- return globalThis.__HARNESS_TEST_PLATFORM_RUNNER__;
6
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=format-harness-error.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format-harness-error.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/format-harness-error.test.ts"],"names":[],"mappings":""}