@react-native-harness/jest 1.1.0-rc.3 → 1.1.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 (55) hide show
  1. package/dist/__tests__/harness.test.js +59 -1
  2. package/dist/__tests__/metro-port.test.d.ts +2 -0
  3. package/dist/__tests__/metro-port.test.d.ts.map +1 -0
  4. package/dist/__tests__/metro-port.test.js +52 -0
  5. package/dist/__tests__/resource-lock.test.js +36 -1
  6. package/dist/errors.d.ts +5 -0
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +11 -0
  9. package/dist/harness.d.ts +1 -0
  10. package/dist/harness.d.ts.map +1 -1
  11. package/dist/harness.js +26 -13
  12. package/dist/logs.d.ts +1 -0
  13. package/dist/logs.d.ts.map +1 -1
  14. package/dist/logs.js +3 -0
  15. package/dist/metro-port.d.ts +16 -0
  16. package/dist/metro-port.d.ts.map +1 -0
  17. package/dist/metro-port.js +48 -0
  18. package/dist/resource-lock.d.ts.map +1 -1
  19. package/dist/resource-lock.js +32 -11
  20. package/dist/setup.js +1 -1
  21. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  22. package/package.json +7 -7
  23. package/src/__tests__/harness.test.ts +89 -1
  24. package/src/__tests__/metro-port.test.ts +61 -0
  25. package/src/__tests__/resource-lock.test.ts +58 -4
  26. package/src/errors.ts +13 -0
  27. package/src/harness.ts +34 -12
  28. package/src/logs.ts +11 -0
  29. package/src/metro-port.ts +76 -0
  30. package/src/resource-lock.ts +59 -37
  31. package/src/setup.ts +1 -1
  32. package/dist/cli-args.test.d.ts +0 -2
  33. package/dist/cli-args.test.d.ts.map +0 -1
  34. package/dist/cli-args.test.js +0 -1
  35. package/dist/crash-monitor.d.ts +0 -16
  36. package/dist/crash-monitor.d.ts.map +0 -1
  37. package/dist/crash-monitor.js +0 -75
  38. package/dist/event-mapper.d.ts +0 -16
  39. package/dist/event-mapper.d.ts.map +0 -1
  40. package/dist/event-mapper.js +0 -120
  41. package/dist/global-setup.d.ts +0 -3
  42. package/dist/global-setup.d.ts.map +0 -1
  43. package/dist/global-setup.js +0 -60
  44. package/dist/global-teardown.d.ts +0 -3
  45. package/dist/global-teardown.d.ts.map +0 -1
  46. package/dist/global-teardown.js +0 -11
  47. package/dist/harness-reporter.d.ts +0 -8
  48. package/dist/harness-reporter.d.ts.map +0 -1
  49. package/dist/harness-reporter.js +0 -12
  50. package/dist/jest-options.d.ts +0 -2
  51. package/dist/jest-options.d.ts.map +0 -1
  52. package/dist/jest-options.js +0 -1
  53. package/dist/types.d.ts +0 -17
  54. package/dist/types.d.ts.map +0 -1
  55. package/dist/types.js +0 -1
@@ -32,7 +32,7 @@ export type ResourceLease = {
32
32
  export type ResourceLockManager = {
33
33
  acquire: (
34
34
  key: string,
35
- options?: ResourceLockAcquireOptions
35
+ options?: ResourceLockAcquireOptions,
36
36
  ) => Promise<ResourceLease>;
37
37
  };
38
38
 
@@ -115,6 +115,21 @@ const readJsonFile = async <T>(filePath: string): Promise<T | null> => {
115
115
  }
116
116
  };
117
117
 
118
+ const writeJsonFileAtomic = async <T>(
119
+ filePath: string,
120
+ value: T,
121
+ ): Promise<void> => {
122
+ const tempPath = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
123
+
124
+ try {
125
+ await fs.writeFile(tempPath, JSON.stringify(value), 'utf8');
126
+ await fs.rename(tempPath, filePath);
127
+ } catch (error) {
128
+ await removeFileIfPresent(tempPath);
129
+ throw error;
130
+ }
131
+ };
132
+
118
133
  const removeFileIfPresent = async (filePath: string): Promise<void> => {
119
134
  try {
120
135
  await fs.rm(filePath, { force: true });
@@ -143,7 +158,7 @@ const isMetadataStale = (
143
158
  metadata: ResourceLockMetadata,
144
159
  now: number,
145
160
  staleLockTimeoutMs: number,
146
- isProcessActive: (pid: number) => boolean
161
+ isProcessActive: (pid: number) => boolean,
147
162
  ): boolean => {
148
163
  if (!isProcessActive(metadata.pid)) {
149
164
  return true;
@@ -154,14 +169,14 @@ const isMetadataStale = (
154
169
 
155
170
  const isQueuedTicketStale = (
156
171
  metadata: ResourceLockMetadata,
157
- isProcessActive: (pid: number) => boolean
172
+ isProcessActive: (pid: number) => boolean,
158
173
  ): boolean => {
159
174
  return !isProcessActive(metadata.pid);
160
175
  };
161
176
 
162
177
  const waitForPollInterval = (
163
178
  ms: number,
164
- signal?: AbortSignal
179
+ signal?: AbortSignal,
165
180
  ): Promise<void> => {
166
181
  if (!signal) {
167
182
  return wait(ms);
@@ -187,7 +202,7 @@ const waitForPollInterval = (
187
202
  };
188
203
 
189
204
  const readQueueTickets = async (
190
- queueDir: string
205
+ queueDir: string,
191
206
  ): Promise<ResourceLockMetadata[]> => {
192
207
  const ticketEntries = await fs.readdir(queueDir, { withFileTypes: true });
193
208
  const tickets = await Promise.all(
@@ -196,15 +211,15 @@ const readQueueTickets = async (
196
211
  .map(async (entry) => ({
197
212
  name: entry.name,
198
213
  metadata: await readJsonFile<ResourceLockMetadata>(
199
- path.join(queueDir, entry.name)
214
+ path.join(queueDir, entry.name),
200
215
  ),
201
- }))
216
+ })),
202
217
  );
203
218
 
204
219
  return tickets
205
220
  .filter(
206
221
  (entry): entry is { name: string; metadata: ResourceLockMetadata } =>
207
- entry.metadata !== null
222
+ entry.metadata !== null,
208
223
  )
209
224
  .sort((left, right) => left.name.localeCompare(right.name))
210
225
  .map((entry) => entry.metadata);
@@ -229,10 +244,10 @@ const cleanupQueue = async (options: {
229
244
  logger.debug(
230
245
  'removing stale queued ticket %s for key %s',
231
246
  ticket.ticketId,
232
- ticket.key
247
+ ticket.key,
233
248
  );
234
249
  await removeFileIfPresent(
235
- path.join(paths.queueDir, `${ticket.ticketId}.json`)
250
+ path.join(paths.queueDir, `${ticket.ticketId}.json`),
236
251
  );
237
252
  continue;
238
253
  }
@@ -265,7 +280,7 @@ const maybeClearStaleOwner = async (options: {
265
280
  logger.debug(
266
281
  'removing stale owner ticket %s for key %s',
267
282
  owner.ticketId,
268
- owner.key
283
+ owner.key,
269
284
  );
270
285
  await removeFileIfPresent(ownerFilePath);
271
286
  return null;
@@ -273,7 +288,7 @@ const maybeClearStaleOwner = async (options: {
273
288
 
274
289
  const claimOwnership = async (
275
290
  ownerFilePath: string,
276
- metadata: ResourceLockMetadata
291
+ metadata: ResourceLockMetadata,
277
292
  ): Promise<boolean> => {
278
293
  try {
279
294
  await fs.writeFile(ownerFilePath, JSON.stringify(metadata), {
@@ -291,7 +306,7 @@ const claimOwnership = async (
291
306
  };
292
307
 
293
308
  export const createResourceLockManager = (
294
- options: ResourceLockManagerOptions = {}
309
+ options: ResourceLockManagerOptions = {},
295
310
  ): ResourceLockManager => {
296
311
  const rootDir = options.rootDir ?? DEFAULT_ROOT_DIR;
297
312
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -323,6 +338,7 @@ export const createResourceLockManager = (
323
338
  scopedLogger.debug('queued ticket %s for key %s', ticketId, key);
324
339
 
325
340
  let heartbeatTimer: NodeJS.Timeout | null = null;
341
+ let heartbeatInFlight = false;
326
342
  let released = false;
327
343
  let didNotifyWait = false;
328
344
  const waitStartedAt = Date.now();
@@ -339,7 +355,7 @@ export const createResourceLockManager = (
339
355
  }
340
356
 
341
357
  const owner = await readJsonFile<ResourceLockMetadata>(
342
- paths.ownerFilePath
358
+ paths.ownerFilePath,
343
359
  );
344
360
  if (owner?.ticketId === ticketId) {
345
361
  await removeFileIfPresent(paths.ownerFilePath);
@@ -351,30 +367,36 @@ export const createResourceLockManager = (
351
367
 
352
368
  const startHeartbeat = () => {
353
369
  heartbeatTimer = setInterval(async () => {
354
- const nextHeartbeatAt = Date.now();
355
- const owner = await readJsonFile<ResourceLockMetadata>(
356
- paths.ownerFilePath
357
- );
358
-
359
- if (released || owner?.ticketId !== ticketId) {
370
+ if (heartbeatInFlight) {
360
371
  return;
361
372
  }
362
373
 
363
- const nextMetadata: ResourceLockMetadata = {
364
- ...owner,
365
- heartbeatAt: nextHeartbeatAt,
366
- };
374
+ heartbeatInFlight = true;
367
375
 
368
- if (released) {
369
- return;
370
- }
376
+ try {
377
+ const nextHeartbeatAt = Date.now();
378
+ const owner = await readJsonFile<ResourceLockMetadata>(
379
+ paths.ownerFilePath,
380
+ );
371
381
 
372
- await fs.writeFile(
373
- paths.ownerFilePath,
374
- JSON.stringify(nextMetadata),
375
- 'utf8'
376
- );
377
- scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
382
+ if (released || owner?.ticketId !== ticketId) {
383
+ return;
384
+ }
385
+
386
+ const nextMetadata: ResourceLockMetadata = {
387
+ ...owner,
388
+ heartbeatAt: nextHeartbeatAt,
389
+ };
390
+
391
+ if (released) {
392
+ return;
393
+ }
394
+
395
+ await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
396
+ scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
397
+ } finally {
398
+ heartbeatInFlight = false;
399
+ }
378
400
  }, heartbeatIntervalMs);
379
401
  heartbeatTimer.unref?.();
380
402
  };
@@ -391,12 +413,12 @@ export const createResourceLockManager = (
391
413
  isProcessActive,
392
414
  });
393
415
  const ownIndex = activeTickets.findIndex(
394
- (entry) => entry.ticketId === ticketId
416
+ (entry) => entry.ticketId === ticketId,
395
417
  );
396
418
 
397
419
  if (ownIndex === -1) {
398
420
  throw new Error(
399
- `Queued ticket ${ticketId} disappeared before acquisition.`
421
+ `Queued ticket ${ticketId} disappeared before acquisition.`,
400
422
  );
401
423
  }
402
424
 
@@ -420,7 +442,7 @@ export const createResourceLockManager = (
420
442
  scopedLogger.debug(
421
443
  'acquired lock for key %s with ticket %s',
422
444
  key,
423
- ticketId
445
+ ticketId,
424
446
  );
425
447
  return { release };
426
448
  }
@@ -436,7 +458,7 @@ export const createResourceLockManager = (
436
458
  'waiting for key %s with ticket %s at queue position %d',
437
459
  key,
438
460
  ticketId,
439
- ownIndex + 1
461
+ ownIndex + 1,
440
462
  );
441
463
 
442
464
  await waitForPollInterval(pollIntervalMs, acquireOptions.signal);
package/src/setup.ts CHANGED
@@ -122,6 +122,6 @@ export const setup = async (globalConfig: JestConfig.GlobalConfig) => {
122
122
  logTestEnvironmentReady(selectedRunner);
123
123
  setupLogger.debug('Harness instance is ready');
124
124
 
125
- global.HARNESS_CONFIG = harnessConfig;
125
+ global.HARNESS_CONFIG = harness.config;
126
126
  global.HARNESS = harness;
127
127
  };
@@ -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,16 +0,0 @@
1
- import { HarnessPlatformRunner } from '@react-native-harness/platforms';
2
- import { BridgeServer } from '@react-native-harness/bridge/server';
3
- export type CrashMonitor = {
4
- startMonitoring(testFilePath: string): Promise<never>;
5
- stopMonitoring(): void;
6
- markIntentionalRestart(): void;
7
- clearIntentionalRestart(): void;
8
- dispose(): void;
9
- };
10
- export type CrashMonitorOptions = {
11
- interval: number;
12
- platformRunner: HarnessPlatformRunner;
13
- bridgeServer: BridgeServer;
14
- };
15
- export declare const createCrashMonitor: ({ interval, platformRunner, bridgeServer, }: CrashMonitorOptions) => CrashMonitor;
16
- //# sourceMappingURL=crash-monitor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crash-monitor.d.ts","sourceRoot":"","sources":["../src/crash-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAInE,MAAM,MAAM,YAAY,GAAG;IACzB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACtD,cAAc,IAAI,IAAI,CAAC;IACvB,sBAAsB,IAAI,IAAI,CAAC;IAC/B,uBAAuB,IAAI,IAAI,CAAC;IAChC,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,qBAAqB,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,6CAIhC,mBAAmB,KAAG,YAqFxB,CAAC"}
@@ -1,75 +0,0 @@
1
- import { NativeCrashError } from './errors.js';
2
- import { logger } from '@react-native-harness/tools';
3
- export const createCrashMonitor = ({ interval, platformRunner, bridgeServer, }) => {
4
- let pollingInterval = null;
5
- let isIntentionalRestart = false;
6
- let currentTestFilePath = null;
7
- let rejectFn = null;
8
- const handleDisconnect = () => {
9
- // Verify if it's actually a crash by checking if app is still running
10
- if (!isIntentionalRestart && currentTestFilePath) {
11
- // Capture the value to avoid it being null when setTimeout callback runs
12
- const testFilePath = currentTestFilePath;
13
- logger.debug('Bridge disconnected, checking if app crashed');
14
- // Use a slight delay to allow the OS to clean up the process
15
- setTimeout(async () => {
16
- const isRunning = await platformRunner.isAppRunning();
17
- if (!isRunning && !isIntentionalRestart && rejectFn) {
18
- logger.debug(`Native crash detected during: ${testFilePath}`);
19
- rejectFn(new NativeCrashError(testFilePath));
20
- }
21
- }, 100);
22
- }
23
- };
24
- const startMonitoring = (testFilePath) => {
25
- currentTestFilePath = testFilePath;
26
- return new Promise((_, reject) => {
27
- rejectFn = reject;
28
- // Listen for bridge disconnect as early indicator
29
- bridgeServer.on('disconnect', handleDisconnect);
30
- // Poll for app running status
31
- pollingInterval = setInterval(async () => {
32
- // Skip check during intentional restarts
33
- if (isIntentionalRestart) {
34
- return;
35
- }
36
- try {
37
- const isRunning = await platformRunner.isAppRunning();
38
- if (!isRunning && currentTestFilePath) {
39
- logger.debug(`Native crash detected during: ${currentTestFilePath}`);
40
- stopMonitoring();
41
- reject(new NativeCrashError(currentTestFilePath));
42
- }
43
- }
44
- catch (error) {
45
- logger.error('Error checking app status:', error);
46
- }
47
- }, interval);
48
- });
49
- };
50
- const stopMonitoring = () => {
51
- if (pollingInterval) {
52
- clearInterval(pollingInterval);
53
- pollingInterval = null;
54
- }
55
- bridgeServer.off('disconnect', handleDisconnect);
56
- currentTestFilePath = null;
57
- rejectFn = null;
58
- };
59
- const markIntentionalRestart = () => {
60
- isIntentionalRestart = true;
61
- };
62
- const clearIntentionalRestart = () => {
63
- isIntentionalRestart = false;
64
- };
65
- const dispose = () => {
66
- stopMonitoring();
67
- };
68
- return {
69
- startMonitoring,
70
- stopMonitoring,
71
- markIntentionalRestart,
72
- clearIntentionalRestart,
73
- dispose,
74
- };
75
- };
@@ -1,16 +0,0 @@
1
- import type { TestEvents } from '@jest/test-result';
2
- import type { Test } from 'jest-runner';
3
- import type { BridgeEvents } from '@react-native-harness/bridge';
4
- import type { TestCaseResult } from '@jest/test-result';
5
- export type EventMapperState = {
6
- currentTest?: Test;
7
- suiteHierarchy: string[];
8
- testResults: Map<string, TestCaseResult>;
9
- fileStartTime: number;
10
- };
11
- export type HarnessEventMapper = {
12
- handleBridgeEvent: (event: BridgeEvents, test: Test) => void;
13
- reset: () => void;
14
- };
15
- export declare const getHarnessEventMapper: (emit: <T extends keyof TestEvents>(eventName: T, eventData: TestEvents[T]) => void) => HarnessEventMapper;
16
- //# sourceMappingURL=event-mapper.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-mapper.d.ts","sourceRoot":"","sources":["../src/event-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EASV,YAAY,EACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,iBAAiB,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC7D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,CAAC,CAAC,SAAS,MAAM,UAAU,EAC/B,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,KACrB,IAAI,KACR,kBA8JF,CAAC"}
@@ -1,120 +0,0 @@
1
- export const getHarnessEventMapper = (emit) => {
2
- const state = {
3
- suiteHierarchy: [],
4
- testResults: new Map(),
5
- fileStartTime: 0,
6
- };
7
- const handleBridgeEvent = (event, test) => {
8
- // Filter to only process test runner events
9
- if (event.type.startsWith('test-') ||
10
- event.type.startsWith('suite-') ||
11
- event.type.startsWith('file-')) {
12
- switch (event.type) {
13
- case 'file-started':
14
- handleFileStarted(event, test);
15
- break;
16
- case 'file-finished':
17
- handleFileFinished(event, test);
18
- break;
19
- case 'suite-started':
20
- handleSuiteStarted(event);
21
- break;
22
- case 'suite-finished':
23
- handleSuiteFinished(event);
24
- break;
25
- case 'test-started':
26
- handleTestStarted(event, test);
27
- break;
28
- case 'test-finished':
29
- handleTestFinished(event, test);
30
- break;
31
- }
32
- }
33
- };
34
- const handleFileStarted = (event, test) => {
35
- state.currentTest = test;
36
- state.fileStartTime = Date.now();
37
- state.suiteHierarchy = [];
38
- state.testResults.clear();
39
- // Don't emit test-file-start here - that should be handled by the Jest runner
40
- // when it actually starts running the test file
41
- };
42
- const handleFileFinished = (_event, _test) => {
43
- // File finished - all test results should already be emitted
44
- // The final TestResult will be handled by the runHarnessTestFile function
45
- };
46
- const handleSuiteStarted = (event) => {
47
- state.suiteHierarchy.push(event.name);
48
- };
49
- const handleSuiteFinished = (event) => {
50
- // Remove the suite from hierarchy when it finishes
51
- const index = state.suiteHierarchy.lastIndexOf(event.name);
52
- if (index !== -1) {
53
- state.suiteHierarchy.splice(index, 1);
54
- }
55
- };
56
- const handleTestStarted = (event, test) => {
57
- const testCaseStartInfo = {
58
- ancestorTitles: [...state.suiteHierarchy],
59
- fullName: `${state.suiteHierarchy.join(' ')} ${event.name}`.trim(),
60
- title: event.name,
61
- mode: 'skip', // Default mode, will be updated based on test status
62
- };
63
- emit('test-case-start', [test.path, testCaseStartInfo]);
64
- };
65
- const handleTestFinished = (event, test) => {
66
- const testCaseResult = {
67
- ancestorTitles: [...state.suiteHierarchy],
68
- duration: event.duration,
69
- failureDetails: [],
70
- failureMessages: event.error ? [formatError(event.error)] : [],
71
- fullName: `${state.suiteHierarchy.join(' ')} ${event.name}`.trim(),
72
- numPassingAsserts: event.status === 'passed' ? 1 : 0,
73
- status: mapStatus(event.status),
74
- title: event.name,
75
- startedAt: Date.now() - event.duration,
76
- };
77
- state.testResults.set(event.name, testCaseResult);
78
- emit('test-case-result', [test.path, testCaseResult]);
79
- };
80
- const mapStatus = (status) => {
81
- return status;
82
- };
83
- const formatError = (error) => {
84
- let errorMessage = `${error.name}: ${error.message}`;
85
- if (error.codeFrame) {
86
- errorMessage += '\n\n' + formatCodeFrame(error.codeFrame);
87
- }
88
- return errorMessage;
89
- };
90
- const formatCodeFrame = (codeFrame) => {
91
- const lines = codeFrame.content.split('\n');
92
- const lineNumber = codeFrame.location?.row || 1;
93
- const column = codeFrame.location?.column || 1;
94
- let formatted = `\n${codeFrame.fileName}:${lineNumber}:${column}\n`;
95
- // Add context lines around the error
96
- const startLine = Math.max(0, lineNumber - 3);
97
- const endLine = Math.min(lines.length, lineNumber + 2);
98
- for (let i = startLine; i < endLine; i++) {
99
- const lineNum = i + 1;
100
- const line = lines[i];
101
- const isErrorLine = lineNum === lineNumber;
102
- formatted += `${lineNum.toString().padStart(3)} | ${line}\n`;
103
- if (isErrorLine && column > 0) {
104
- const pointer = ' '.repeat(column + 5) + '^';
105
- formatted += pointer + '\n';
106
- }
107
- }
108
- return formatted;
109
- };
110
- const reset = () => {
111
- state.suiteHierarchy = [];
112
- state.testResults.clear();
113
- state.fileStartTime = 0;
114
- state.currentTest = undefined;
115
- };
116
- return {
117
- handleBridgeEvent,
118
- reset,
119
- };
120
- };
@@ -1,3 +0,0 @@
1
- import type { Config as JestConfig } from 'jest-runner';
2
- export default function (globalConfig: JestConfig.GlobalConfig): Promise<void>;
3
- //# sourceMappingURL=global-setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../src/global-setup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AA4CxD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBA4CnE"}
@@ -1,60 +0,0 @@
1
- import { getConfig, } from '@react-native-harness/config';
2
- import { getHarness as getHarnessExternal, NoRunnerSpecifiedError, RunnerNotFoundError, } from '@react-native-harness/cli/external';
3
- import { preRunMessage } from 'jest-util';
4
- import { getAdditionalCliArgs } from './cli-args.js';
5
- import { logTestEnvironmentReady, logTestRunHeader } from './logs.js';
6
- const getHarnessConfig = async (globalConfig) => {
7
- const projectRoot = globalConfig.rootDir;
8
- const { config: harnessConfig } = await getConfig(projectRoot);
9
- return harnessConfig;
10
- };
11
- const getHarnessRunner = (config, cliArgs) => {
12
- const selectedRunnerName = cliArgs.harnessRunner ?? config.defaultRunner;
13
- if (!selectedRunnerName) {
14
- throw new NoRunnerSpecifiedError(config.runners);
15
- }
16
- const runner = config.runners.find((runner) => runner.name === selectedRunnerName);
17
- if (!runner) {
18
- throw new RunnerNotFoundError(selectedRunnerName, config.runners);
19
- }
20
- return runner;
21
- };
22
- const getHarness = async (runner) => {
23
- return await getHarnessExternal(runner);
24
- };
25
- export default async function (globalConfig) {
26
- preRunMessage.remove(process.stderr);
27
- const harnessConfig = global.HARNESS_CONFIG ?? (await getHarnessConfig(globalConfig));
28
- const isWatchMode = globalConfig.watch || globalConfig.watchAll;
29
- if (global.HARNESS) {
30
- // Do not setup again if HARNESS is already initialized
31
- // This is useful when running tests in watch mode
32
- if (harnessConfig.resetEnvironmentBetweenTestFiles) {
33
- // In watch mode, we want to restart the environment before each test run
34
- await new Promise((resolve) => {
35
- global.HARNESS.bridge.once('ready', resolve);
36
- global.HARNESS.environment.restart();
37
- });
38
- }
39
- return;
40
- }
41
- if (isWatchMode) {
42
- // In watch mode, we want to dispose the Harness when the process exits.
43
- process.on('exit', async () => {
44
- await global.HARNESS.bridge.dispose();
45
- await global.HARNESS.environment.dispose();
46
- });
47
- }
48
- const cliArgs = getAdditionalCliArgs();
49
- const selectedRunner = getHarnessRunner(harnessConfig, cliArgs);
50
- if (globalConfig.collectCoverage) {
51
- // This is going to be used by @react-native-harness/babel-preset
52
- // to enable instrumentation of test files.
53
- process.env.RN_HARNESS_COLLECT_COVERAGE = 'true';
54
- }
55
- logTestRunHeader(selectedRunner);
56
- const harness = await getHarness(selectedRunner);
57
- logTestEnvironmentReady(selectedRunner);
58
- global.HARNESS_CONFIG = harnessConfig;
59
- global.HARNESS = harness;
60
- }
@@ -1,3 +0,0 @@
1
- import type { Config as JestConfig } from 'jest-runner';
2
- export default function (globalConfig: JestConfig.GlobalConfig): Promise<void>;
3
- //# sourceMappingURL=global-teardown.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"global-teardown.d.ts","sourceRoot":"","sources":["../src/global-teardown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAExD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBAYnE"}
@@ -1,11 +0,0 @@
1
- export default async function (globalConfig) {
2
- const isWatchMode = globalConfig.watch || globalConfig.watchAll;
3
- if (isWatchMode) {
4
- // In watch mode, we don't want to dispose the Harness.
5
- return;
6
- }
7
- if (global.HARNESS) {
8
- await global.HARNESS.bridge.dispose();
9
- await global.HARNESS.environment.dispose();
10
- }
11
- }
@@ -1,8 +0,0 @@
1
- import { BaseReporter } from '@jest/reporters';
2
- import { Config } from '@jest/types';
3
- export default class HarnessReporter extends BaseReporter {
4
- #private;
5
- constructor(globalConfig: Config.GlobalConfig);
6
- onRunStart(): void;
7
- }
8
- //# sourceMappingURL=harness-reporter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"harness-reporter.d.ts","sourceRoot":"","sources":["../src/harness-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,YAAY;;gBAE3C,YAAY,EAAE,MAAM,CAAC,YAAY;IAKpC,UAAU,IAAI,IAAI;CAM5B"}
@@ -1,12 +0,0 @@
1
- import { BaseReporter } from '@jest/reporters';
2
- export default class HarnessReporter extends BaseReporter {
3
- #globalConfig;
4
- constructor(globalConfig) {
5
- super();
6
- this.#globalConfig = globalConfig;
7
- }
8
- onRunStart() {
9
- super.onRunStart();
10
- this.log(`Selected runner: ${this.#globalConfig.selectedRunner.name}`);
11
- }
12
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=jest-options.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jest-options.d.ts","sourceRoot":"","sources":["../src/jest-options.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- export {};
package/dist/types.d.ts DELETED
@@ -1,17 +0,0 @@
1
- import type { TestContext, TestResult } from '@jest/test-result';
2
- import type { Config, TestRunnerOptions } from 'jest-runner';
3
- import type { CustomEnvironment } from '@react-native-harness/cli';
4
- export type RunTestOptions<ExtraOptions extends Record<string, unknown> = Record<string, unknown>> = {
5
- config: Config.ProjectConfig;
6
- extraOptions: ExtraOptions;
7
- globalConfig: Config.GlobalConfig;
8
- rawModuleMap: ReturnType<TestContext['moduleMap']['getRawModuleMap']> | null;
9
- options: TestRunnerOptions;
10
- testPath: string;
11
- };
12
- export type RunTest<ExtraOptions extends Record<string, unknown> = Record<string, unknown>> = (options: RunTestOptions<ExtraOptions>, environment: CustomEnvironment) => TestResult | Promise<TestResult>;
13
- export interface TestDetail {
14
- title: string;
15
- path: string;
16
- }
17
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,MAAM,cAAc,CACxB,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACpE;IACF,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClC,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,CACjB,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACpE,CACF,OAAO,EAAE,cAAc,CAAC,YAAY,CAAC,EACrC,WAAW,EAAE,iBAAiB,KAC3B,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd"}
package/dist/types.js DELETED
@@ -1 +0,0 @@
1
- export {};