@react-native-harness/jest 1.6.0 → 1.7.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 (82) hide show
  1. package/dist/cli-args.d.ts +3 -4
  2. package/dist/cli-args.d.ts.map +1 -1
  3. package/dist/cli-args.js +3 -8
  4. package/dist/diagnostics/index.d.ts +4 -1
  5. package/dist/diagnostics/index.d.ts.map +1 -1
  6. package/dist/diagnostics/index.js +2 -1
  7. package/dist/diagnostics/report.d.ts +81 -1
  8. package/dist/diagnostics/report.d.ts.map +1 -1
  9. package/dist/diagnostics/report.js +225 -3
  10. package/dist/diagnostics/telemetry.d.ts +42 -0
  11. package/dist/diagnostics/telemetry.d.ts.map +1 -0
  12. package/dist/diagnostics/telemetry.js +120 -0
  13. package/dist/errors.d.ts +8 -2
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +29 -3
  16. package/dist/execute-run.d.ts.map +1 -1
  17. package/dist/execute-run.js +73 -21
  18. package/dist/format-harness-error.d.ts.map +1 -1
  19. package/dist/format-harness-error.js +3 -0
  20. package/dist/harness-session.d.ts +8 -1
  21. package/dist/harness-session.d.ts.map +1 -1
  22. package/dist/harness-session.js +106 -22
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -7
  25. package/dist/jest-preset.d.ts +3 -0
  26. package/dist/jest-preset.d.ts.map +1 -1
  27. package/dist/jest-preset.js +18 -0
  28. package/dist/relative-path.d.ts +8 -0
  29. package/dist/relative-path.d.ts.map +1 -0
  30. package/dist/relative-path.js +8 -0
  31. package/dist/resource-lock.d.ts.map +1 -1
  32. package/dist/resource-lock.js +47 -27
  33. package/dist/run.d.ts.map +1 -1
  34. package/dist/run.js +4 -4
  35. package/dist/watch-session.d.ts +6 -0
  36. package/dist/watch-session.d.ts.map +1 -0
  37. package/dist/watch-session.js +37 -0
  38. package/package.json +8 -8
  39. package/src/cli-args.ts +5 -13
  40. package/src/diagnostics/index.ts +12 -1
  41. package/src/diagnostics/report.ts +326 -5
  42. package/src/diagnostics/telemetry.ts +186 -0
  43. package/src/errors.ts +42 -5
  44. package/src/execute-run.ts +93 -23
  45. package/src/format-harness-error.ts +5 -0
  46. package/src/harness-session.ts +139 -21
  47. package/src/index.ts +8 -7
  48. package/src/jest-preset.ts +18 -0
  49. package/src/relative-path.ts +10 -0
  50. package/src/resource-lock.ts +64 -33
  51. package/src/run.ts +4 -4
  52. package/src/watch-session.ts +65 -0
  53. package/dist/crash-supervisor.d.ts +0 -21
  54. package/dist/crash-supervisor.d.ts.map +0 -1
  55. package/dist/crash-supervisor.js +0 -210
  56. package/dist/event-mapper.d.ts +0 -16
  57. package/dist/event-mapper.d.ts.map +0 -1
  58. package/dist/event-mapper.js +0 -120
  59. package/dist/global-setup.d.ts +0 -3
  60. package/dist/global-setup.d.ts.map +0 -1
  61. package/dist/global-setup.js +0 -60
  62. package/dist/global-teardown.d.ts +0 -3
  63. package/dist/global-teardown.d.ts.map +0 -1
  64. package/dist/global-teardown.js +0 -11
  65. package/dist/harness-reporter.d.ts +0 -8
  66. package/dist/harness-reporter.d.ts.map +0 -1
  67. package/dist/harness-reporter.js +0 -12
  68. package/dist/harness.d.ts +0 -47
  69. package/dist/harness.d.ts.map +0 -1
  70. package/dist/harness.js +0 -619
  71. package/dist/jest-options.d.ts +0 -2
  72. package/dist/jest-options.d.ts.map +0 -1
  73. package/dist/jest-options.js +0 -1
  74. package/dist/setup.d.ts +0 -3
  75. package/dist/setup.d.ts.map +0 -1
  76. package/dist/setup.js +0 -80
  77. package/dist/teardown.d.ts +0 -3
  78. package/dist/teardown.d.ts.map +0 -1
  79. package/dist/teardown.js +0 -10
  80. package/dist/types.d.ts +0 -17
  81. package/dist/types.d.ts.map +0 -1
  82. package/dist/types.js +0 -1
@@ -1,3 +1,21 @@
1
1
  export default {
2
2
  runner: '@react-native-harness/jest',
3
+ // Tests run serially on the device, so Jest only uses its workers to build
4
+ // the haste map. On a cold cache or after any file change that starts a pool
5
+ // of `availableParallelism() - 1` processes, which roughly doubles Jest's
6
+ // CPU time without making the run faster.
7
+ maxWorkers: 1,
8
+ // Build and cache output never contains tests, but it can hold tens of
9
+ // thousands of files. Jest checks these patterns before descending into a
10
+ // directory, so its crawl skips them entirely. Jest appends a project's own
11
+ // patterns to the preset's.
12
+ modulePathIgnorePatterns: [
13
+ '/\\.harness(/|$)',
14
+ '/ios/(Pods|build)(/|$)',
15
+ '/android/(build|\\.gradle|app/build|app/\\.cxx)(/|$)',
16
+ ],
17
+ // With the build output skipped, Jest's own crawl is cheaper than querying
18
+ // watchman, which costs two CLI process spawns per run and still lists every
19
+ // file. Metro keeps using watchman.
20
+ watchman: false,
3
21
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns `filePath` relative to `rootDir`, always using `/` as the separator.
3
+ *
4
+ * These paths leave the host (they are sent to the device and used in Metro
5
+ * bundle URLs and plugin hooks), so they must not depend on the host OS.
6
+ */
7
+ export declare const toRelativePosixPath: (rootDir: string, filePath: string) => string;
8
+ //# sourceMappingURL=relative-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relative-path.d.ts","sourceRoot":"","sources":["../src/relative-path.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,MACZ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Returns `filePath` relative to `rootDir`, always using `/` as the separator.
4
+ *
5
+ * These paths leave the host (they are sent to the device and used in Metro
6
+ * bundle URLs and plugin hooks), so they must not depend on the host OS.
7
+ */
8
+ export const toRelativePosixPath = (rootDir, filePath) => path.relative(rootDir, filePath).split(path.sep).join('/');
@@ -1 +1 @@
1
- {"version":3,"file":"resource-lock.d.ts","sourceRoot":"","sources":["../src/resource-lock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBzE,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,CACP,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,0BAA0B,KACjC,OAAO,CAAC,aAAa,CAAC,CAAC;CAC7B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAqQF,eAAO,MAAM,yBAAyB,GACpC,UAAS,0BAA+B,KACvC,mBA+KF,CAAC"}
1
+ {"version":3,"file":"resource-lock.d.ts","sourceRoot":"","sources":["../src/resource-lock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBzE,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,CACP,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,0BAA0B,KACjC,OAAO,CAAC,aAAa,CAAC,CAAC;CAC7B,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAsSF,eAAO,MAAM,yBAAyB,GACpC,UAAS,0BAA+B,KACvC,mBA6KF,CAAC"}
@@ -35,6 +35,28 @@ const isMissingFileError = (error) => {
35
35
  typeof error.code === 'string' &&
36
36
  error.code === 'ENOENT');
37
37
  };
38
+ // On Windows, a file being deleted by another process can briefly fail
39
+ // reads and deletes with EPERM/EBUSY instead of ENOENT.
40
+ const isTransientFsError = (error) => {
41
+ return (error instanceof Error &&
42
+ 'code' in error &&
43
+ (error.code === 'EPERM' || error.code === 'EBUSY'));
44
+ };
45
+ const TRANSIENT_FS_RETRIES = 5;
46
+ const TRANSIENT_FS_RETRY_DELAY_MS = 20;
47
+ const retryTransientFsError = async (operation) => {
48
+ for (let attempt = 1;; attempt += 1) {
49
+ try {
50
+ return await operation();
51
+ }
52
+ catch (error) {
53
+ if (!isTransientFsError(error) || attempt >= TRANSIENT_FS_RETRIES) {
54
+ throw error;
55
+ }
56
+ await new Promise((resolve) => setTimeout(resolve, TRANSIENT_FS_RETRY_DELAY_MS));
57
+ }
58
+ }
59
+ };
38
60
  const isExclusiveCreateError = (error) => {
39
61
  return (error instanceof Error &&
40
62
  'code' in error &&
@@ -46,7 +68,7 @@ const ensureLockDirectories = async (paths) => {
46
68
  };
47
69
  const readJsonFile = async (filePath) => {
48
70
  try {
49
- const content = await fs.readFile(filePath, 'utf8');
71
+ const content = await retryTransientFsError(() => fs.readFile(filePath, 'utf8'));
50
72
  return JSON.parse(content);
51
73
  }
52
74
  catch (error) {
@@ -69,7 +91,7 @@ const writeJsonFileAtomic = async (filePath, value) => {
69
91
  };
70
92
  const removeFileIfPresent = async (filePath) => {
71
93
  try {
72
- await fs.rm(filePath, { force: true });
94
+ await retryTransientFsError(() => fs.rm(filePath, { force: true }));
73
95
  }
74
96
  catch (error) {
75
97
  if (!isMissingFileError(error)) {
@@ -200,7 +222,7 @@ export const createResourceLockManager = (options = {}) => {
200
222
  await fs.writeFile(ticketPath, JSON.stringify(metadata), 'utf8');
201
223
  scopedLogger.debug('queued ticket %s for key %s', ticketId, key);
202
224
  let heartbeatTimer = null;
203
- let heartbeatInFlight = false;
225
+ let heartbeatInFlight = null;
204
226
  let released = false;
205
227
  let didNotifyWait = false;
206
228
  const waitStartedAt = Date.now();
@@ -213,6 +235,9 @@ export const createResourceLockManager = (options = {}) => {
213
235
  clearInterval(heartbeatTimer);
214
236
  heartbeatTimer = null;
215
237
  }
238
+ // Let an in-flight refresh finish so it cannot recreate owner.json
239
+ // after we remove it.
240
+ await heartbeatInFlight;
216
241
  const owner = await readJsonFile(paths.ownerFilePath);
217
242
  if (owner?.ticketId === ticketId) {
218
243
  await removeFileIfPresent(paths.ownerFilePath);
@@ -221,40 +246,35 @@ export const createResourceLockManager = (options = {}) => {
221
246
  scopedLogger.debug('released ticket %s for key %s', ticketId, key);
222
247
  };
223
248
  const startHeartbeat = () => {
224
- heartbeatTimer = setInterval(async () => {
225
- if (heartbeatInFlight) {
226
- return;
227
- }
228
- heartbeatInFlight = true;
249
+ const refreshHeartbeat = async () => {
229
250
  try {
230
251
  const nextHeartbeatAt = Date.now();
231
252
  const owner = await readJsonFile(paths.ownerFilePath);
232
253
  if (released || owner?.ticketId !== ticketId) {
233
254
  return;
234
255
  }
235
- const nextMetadata = {
256
+ await writeJsonFileAtomic(paths.ownerFilePath, {
236
257
  ...owner,
237
258
  heartbeatAt: nextHeartbeatAt,
238
- };
239
- if (released) {
240
- return;
241
- }
242
- try {
243
- await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
244
- scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
245
- }
246
- catch (error) {
247
- // A failed refresh is not fatal by design: the lock goes stale
248
- // and another holder reclaims it. Swallow it so a transient
249
- // write error (e.g. the owner file racing a concurrent release,
250
- // or an EPERM on Windows when the directory is being torn down)
251
- // never surfaces as an unhandled rejection from this interval.
252
- scopedLogger.debug('heartbeat refresh for ticket %s failed: %s', ticketId, error instanceof Error ? error.message : String(error));
253
- }
259
+ });
260
+ scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
254
261
  }
255
- finally {
256
- heartbeatInFlight = false;
262
+ catch (error) {
263
+ // A failed refresh is not fatal by design: the lock goes stale
264
+ // and another holder reclaims it. Swallow it so a transient
265
+ // error (e.g. the owner file racing a concurrent release, or an
266
+ // EPERM on Windows while a file is being deleted) never surfaces
267
+ // as an unhandled rejection from this interval.
268
+ scopedLogger.debug('heartbeat refresh for ticket %s failed: %s', ticketId, error instanceof Error ? error.message : String(error));
257
269
  }
270
+ };
271
+ heartbeatTimer = setInterval(() => {
272
+ if (heartbeatInFlight) {
273
+ return;
274
+ }
275
+ heartbeatInFlight = refreshHeartbeat().finally(() => {
276
+ heartbeatInFlight = null;
277
+ });
258
278
  }, heartbeatIntervalMs);
259
279
  heartbeatTimer.unref?.();
260
280
  };
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EACV,eAAe,IAAI,sBAAsB,EAE1C,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAmE3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC;IACZ,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,sBAAsB,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,kBA0FhC,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EACV,eAAe,IAAI,sBAAsB,EAE1C,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAoE3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;IACxC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC;CACvC,KAAK,OAAO,CAAC;IACZ,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,sBAAsB,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,kBA0FhC,CAAC"}
package/dist/run.js CHANGED
@@ -1,7 +1,7 @@
1
- import path from 'node:path';
2
1
  import { formatResultsErrors } from 'jest-message-util';
3
2
  import { toTestResult } from './toTestResult.js';
4
3
  import { formatHarnessErrorMessage } from './format-harness-error.js';
4
+ import { toRelativePosixPath } from './relative-path.js';
5
5
  // Helper function to flatten nested test suites into a flat array of tests with hierarchy
6
6
  const flattenTests = (suiteResult, ancestorTitles = []) => {
7
7
  const tests = [];
@@ -55,10 +55,10 @@ const calculateStats = (tests) => {
55
55
  };
56
56
  export const runHarnessTestFile = async ({ testPath, globalConfig, projectConfig, session, }) => {
57
57
  const start = Date.now();
58
- const relativeTestPath = path.relative(globalConfig.rootDir, testPath);
58
+ const relativeTestPath = toRelativePosixPath(globalConfig.rootDir, testPath);
59
59
  // Extract setup files from Jest config and convert to relative paths
60
- const setupFiles = projectConfig.setupFiles?.map((setupFile) => path.relative(globalConfig.rootDir, setupFile));
61
- const setupFilesAfterEnv = projectConfig.setupFilesAfterEnv?.map((setupFile) => path.relative(globalConfig.rootDir, setupFile));
60
+ const setupFiles = projectConfig.setupFiles?.map((setupFile) => toRelativePosixPath(globalConfig.rootDir, setupFile));
61
+ const setupFilesAfterEnv = projectConfig.setupFilesAfterEnv?.map((setupFile) => toRelativePosixPath(globalConfig.rootDir, setupFile));
62
62
  const testTimeout = session.config.testTimeout ??
63
63
  projectConfig
64
64
  .testTimeout ??
@@ -0,0 +1,6 @@
1
+ import type { Config } from 'jest-runner';
2
+ import { type HarnessSession } from './harness-session.js';
3
+ export declare const getWatchSession: (globalConfig: Config.GlobalConfig) => Promise<HarnessSession>;
4
+ /** @internal Test-only: forget the module-level session without disposing. */
5
+ export declare const resetWatchSessionForTests: () => void;
6
+ //# sourceMappingURL=watch-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-session.d.ts","sourceRoot":"","sources":["../src/watch-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAqB9B,eAAO,MAAM,eAAe,GAC1B,cAAc,MAAM,CAAC,YAAY,KAChC,OAAO,CAAC,cAAc,CAgCxB,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,QAAO,IAE5C,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { createHarnessSession, } from './harness-session.js';
2
+ let current = null;
3
+ // Everything createHarnessSession reads from the Jest config.
4
+ const getSessionKey = (globalConfig) => JSON.stringify([globalConfig.rootDir, Boolean(globalConfig.collectCoverage)]);
5
+ export const getWatchSession = (globalConfig) => {
6
+ const key = getSessionKey(globalConfig);
7
+ if (current?.key === key) {
8
+ return current.promise;
9
+ }
10
+ const stale = current;
11
+ // The entry is installed synchronously so concurrent callers share it, even
12
+ // while a stale session is still being disposed.
13
+ const entry = {
14
+ key,
15
+ promise: (async () => {
16
+ if (stale) {
17
+ // A failed disposal must not fail the run.
18
+ await stale.promise
19
+ .then((session) => session.dispose())
20
+ .catch(() => undefined);
21
+ }
22
+ return createHarnessSession(globalConfig);
23
+ })(),
24
+ };
25
+ current = entry;
26
+ // Never cache a failed creation; the next run retries.
27
+ entry.promise.catch(() => {
28
+ if (current === entry) {
29
+ current = null;
30
+ }
31
+ });
32
+ return entry.promise;
33
+ };
34
+ /** @internal Test-only: forget the module-level session without disposing. */
35
+ export const resetWatchSessionForTests = () => {
36
+ current = null;
37
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-harness/jest",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -39,13 +39,13 @@
39
39
  "jest-util": "^30.2.0",
40
40
  "tslib": "^2.3.0",
41
41
  "yargs": "^17.7.2",
42
- "@react-native-harness/cache": "1.6.0",
43
- "@react-native-harness/config": "1.6.0",
44
- "@react-native-harness/bundler-metro": "1.6.0",
45
- "@react-native-harness/platforms": "1.6.0",
46
- "@react-native-harness/tools": "1.6.0",
47
- "@react-native-harness/plugins": "1.6.0",
48
- "@react-native-harness/bridge": "1.6.0"
42
+ "@react-native-harness/config": "1.7.0",
43
+ "@react-native-harness/tools": "1.7.0",
44
+ "@react-native-harness/platforms": "1.7.0",
45
+ "@react-native-harness/bundler-metro": "1.7.0",
46
+ "@react-native-harness/plugins": "1.7.0",
47
+ "@react-native-harness/cache": "1.7.0",
48
+ "@react-native-harness/bridge": "1.7.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/yargs": "^17.0.32",
package/src/cli-args.ts CHANGED
@@ -1,25 +1,17 @@
1
- import yargs from 'yargs';
1
+ import { harnessCliOptions } from '@react-native-harness/config';
2
+ import yargs, { type InferredOptionTypes } from 'yargs';
2
3
  import { hideBin } from 'yargs/helpers';
3
4
 
4
- export type HarnessCliArgs = {
5
- harnessRunner?: string;
6
- metroPort?: number;
7
- };
5
+ export type HarnessCliArgs = InferredOptionTypes<typeof harnessCliOptions>;
8
6
 
9
7
  export const getAdditionalCliArgs = (): HarnessCliArgs => {
10
8
  const argv = yargs(hideBin(process.argv))
11
- .option('harnessRunner', {
12
- type: 'string',
13
- description: 'Specify which Harness runner to use',
14
- })
15
- .option('metroPort', {
16
- type: 'number',
17
- description: 'Override the Metro bundler port',
18
- })
9
+ .options(harnessCliOptions)
19
10
  .strict(false)
20
11
  .help(false)
21
12
  .version(false)
22
13
  .exitProcess(false)
14
+ .fail(false)
23
15
  .parseSync();
24
16
 
25
17
  return {
@@ -1,2 +1,13 @@
1
1
  export { observeMetroEvents, observeBridgeEvents } from './derivers.js';
2
- export { writeTraceFile, printSummary } from './report.js';
2
+ export {
3
+ writeTraceFile,
4
+ appendToTraceFile,
5
+ printSummary,
6
+ createDiagnosticsAppender,
7
+ summarizeRunKpis,
8
+ writeSummaryFile,
9
+ appendStepSummary,
10
+ } from './report.js';
11
+ export type { DiagnosticsAppender, RunKpis } from './report.js';
12
+ export { startSessionTelemetry } from './telemetry.js';
13
+ export type { SessionTelemetry } from './telemetry.js';