@react-native-harness/jest 1.4.1 → 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.
- package/dist/execute-run.d.ts.map +1 -1
- package/dist/execute-run.js +2 -1
- package/dist/harness-session.d.ts.map +1 -1
- package/dist/harness-session.js +74 -30
- package/dist/resource-lock.d.ts.map +1 -1
- package/dist/resource-lock.js +12 -2
- package/dist/toJestStatus.d.ts +4 -0
- package/dist/toJestStatus.d.ts.map +1 -0
- package/dist/toJestStatus.js +2 -0
- package/dist/toTestResult.d.ts +3 -2
- package/dist/toTestResult.d.ts.map +1 -1
- package/dist/toTestResult.js +2 -1
- package/package.json +8 -8
- package/src/execute-run.ts +2 -1
- package/src/harness-session.ts +86 -27
- package/src/resource-lock.ts +15 -2
- package/src/toJestStatus.ts +6 -0
- package/src/toTestResult.ts +5 -3
- package/dist/crash-supervisor.d.ts +0 -21
- package/dist/crash-supervisor.d.ts.map +0 -1
- package/dist/crash-supervisor.js +0 -210
- package/dist/event-mapper.d.ts +0 -16
- package/dist/event-mapper.d.ts.map +0 -1
- package/dist/event-mapper.js +0 -120
- package/dist/global-setup.d.ts +0 -3
- package/dist/global-setup.d.ts.map +0 -1
- package/dist/global-setup.js +0 -60
- package/dist/global-teardown.d.ts +0 -3
- package/dist/global-teardown.d.ts.map +0 -1
- package/dist/global-teardown.js +0 -11
- package/dist/harness-reporter.d.ts +0 -8
- package/dist/harness-reporter.d.ts.map +0 -1
- package/dist/harness-reporter.js +0 -12
- package/dist/harness.d.ts +0 -47
- package/dist/harness.d.ts.map +0 -1
- package/dist/harness.js +0 -619
- package/dist/jest-options.d.ts +0 -2
- package/dist/jest-options.d.ts.map +0 -1
- package/dist/jest-options.js +0 -1
- package/dist/setup.d.ts +0 -3
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js +0 -80
- package/dist/teardown.d.ts +0 -3
- package/dist/teardown.d.ts.map +0 -1
- package/dist/teardown.js +0 -10
- package/dist/types.d.ts +0 -17
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute-run.d.ts","sourceRoot":"","sources":["../src/execute-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,WAAW,EACX,MAAM,EACN,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"execute-run.d.ts","sourceRoot":"","sources":["../src/execute-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,WAAW,EACX,MAAM,EACN,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AA8BjF,KAAK,aAAa,GAAG,CAAC,IAAI,SAAS,MAAM,UAAU,EACjD,SAAS,EAAE,IAAI,EACf,GAAG,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;AA+FnB,eAAO,MAAM,UAAU,GACrB,SAAS,cAAc,EACvB,OAAO,KAAK,CAAC,IAAI,CAAC,EAClB,SAAS,WAAW,EACpB,WAAW,aAAa,EACxB,cAAc,MAAM,CAAC,YAAY,KAChC,OAAO,CAAC,IAAI,CAsOd,CAAC"}
|
package/dist/execute-run.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createPlatformSkippedTestResult, shouldRunHarnessTestFile, } from './te
|
|
|
8
8
|
import { formatHarnessErrorMessage } from './format-harness-error.js';
|
|
9
9
|
import { logger } from '@react-native-harness/tools';
|
|
10
10
|
import { printSummary, writeTraceFile } from './diagnostics/index.js';
|
|
11
|
+
import { toJestStatus } from './toJestStatus.js';
|
|
11
12
|
const diagnosticsLogger = logger.child('diagnostics');
|
|
12
13
|
const createRunSummary = () => ({ passed: 0, failed: 0, skipped: 0, todo: 0 });
|
|
13
14
|
const applyJestResultToSummary = (summary, result) => {
|
|
@@ -59,7 +60,7 @@ const emitHarnessTestFinished = async (emitEvent, event) => {
|
|
|
59
60
|
location,
|
|
60
61
|
numPassingAsserts: event.status === 'passed' ? 1 : 0,
|
|
61
62
|
startedAt: event.startedAt,
|
|
62
|
-
status: event.status,
|
|
63
|
+
status: toJestStatus(event.status),
|
|
63
64
|
title: event.name,
|
|
64
65
|
};
|
|
65
66
|
await emitEvent('test-case-result', event.file, testCaseResult);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,iCAAiC,CAAC;AASzC,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../src/harness-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,iCAAiC,CAAC;AASzC,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAQL,KAAK,WAAW,EACjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAIL,KAAK,MAAM,IAAI,aAAa,EAE7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAKxD,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQ3E,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAyC5B,KAAK,0BAA0B,GAAG;IAChC,QAAQ,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1C,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAC1D,GAAG,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAAU,oCAIpD;IACD,MAAM,EAAE,0BAA0B,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,OAAO,CAuBlB,CAAC;AAKF,eAAO,MAAM,oBAAoB,GAAI,qBAGlC;IACD,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB,KAAG,OAAO,CAAC,IAAI,CAWZ,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;AAE/E,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC/E,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,eAAe,CAAC;IACvC,QAAQ,EAAE,oBAAoB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3E,WAAW,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IACrD,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,OAAO,eAAe,GAAG,IAAI,KAC5B,MAWF,CAAC;AAYF,eAAO,MAAM,wBAAwB,GAAU,CAAC,EAAE,SAAS;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3C,KAAG,OAAO,CAAC,CAAC,CAaZ,CAAC;AAcF,eAAO,MAAM,mBAAmB,GAAU,8DAKvC;IACD,YAAY,EAAE,YAAY,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;CACrB,mBAcA,CAAC;AA4HF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS;IAChD,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,4BAA4B,CAAC,CAAC;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;CAC3B,KAAG,IAUH,CAAC;AA8CF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,UAAU,CAAC,YAAY,EACrC,kBAA8C;IAAE,WAAW,CAAC,EAAE,mBAAmB,CAAA;CAAO,KACvF,OAAO,CAAC,cAAc,CA4qBxB,CAAC"}
|
package/dist/harness-session.js
CHANGED
|
@@ -3,7 +3,7 @@ import { HARNESS_BRIDGE_PATH, } from '@react-native-harness/bridge';
|
|
|
3
3
|
import { createHarnessCache } from '@react-native-harness/cache';
|
|
4
4
|
import { getMetroInstance, waitForMetroBackedAppReady, } from '@react-native-harness/bundler-metro';
|
|
5
5
|
import { createHarnessPluginManager, } from '@react-native-harness/plugins';
|
|
6
|
-
import { createCrashArtifactWriter, createDiagnostics, logger, getTimeoutSignal, ensureSignalsDeliverable, waitForAbort, } from '@react-native-harness/tools';
|
|
6
|
+
import { createCrashArtifactWriter, createDiagnostics, delay, logger, getTimeoutSignal, ensureSignalsDeliverable, waitForAbort, } from '@react-native-harness/tools';
|
|
7
7
|
import { getConfig, isDiagnosticsEnabled, resolveMetroCacheEnabled, ConfigSchema, } from '@react-native-harness/config';
|
|
8
8
|
import { preRunMessage } from 'jest-util';
|
|
9
9
|
import path from 'node:path';
|
|
@@ -24,6 +24,19 @@ const defaultResourceLockManager = createResourceLockManager();
|
|
|
24
24
|
const ignorePromiseRejection = () => undefined;
|
|
25
25
|
const isBridgeDisconnectError = (error) => error instanceof Error && error.message === 'App bridge disconnected';
|
|
26
26
|
const TEST_RUN_BRIDGE_STABILITY_WAIT_MS = 500;
|
|
27
|
+
const DISPOSE_HOOK_TIMEOUT_MS = 5_000;
|
|
28
|
+
const waitForBoundedSettlement = async (promise, timeoutMs) => {
|
|
29
|
+
const timeout = delay(timeoutMs);
|
|
30
|
+
try {
|
|
31
|
+
return await Promise.race([
|
|
32
|
+
promise.then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason })),
|
|
33
|
+
timeout.promise.then(() => null),
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
timeout.cancel();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
27
40
|
export const waitForBridgeDisconnectOrTimeout = async ({ bridge, connection, timeoutMs, }) => {
|
|
28
41
|
if (bridge.connection !== connection) {
|
|
29
42
|
return true;
|
|
@@ -260,7 +273,11 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
260
273
|
// child the runner threads it into.
|
|
261
274
|
ensureSignalsDeliverable();
|
|
262
275
|
const sessionController = new AbortController();
|
|
263
|
-
|
|
276
|
+
let receivedEarlySignal = false;
|
|
277
|
+
const onEarlySignal = () => {
|
|
278
|
+
receivedEarlySignal = true;
|
|
279
|
+
sessionController.abort();
|
|
280
|
+
};
|
|
264
281
|
process.once('SIGTERM', onEarlySignal);
|
|
265
282
|
process.once('SIGINT', onEarlySignal);
|
|
266
283
|
const resourceLockKey = await (platform.getResourceLockKey?.() ?? getDefaultResourceLockKey(platform));
|
|
@@ -341,6 +358,11 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
341
358
|
sessionLogger.debug('bridge initialized on Metro websocket path %s', HARNESS_BRIDGE_PATH);
|
|
342
359
|
let metroInstance;
|
|
343
360
|
let platformInstance;
|
|
361
|
+
const initialized = {
|
|
362
|
+
metro: null,
|
|
363
|
+
platform: null,
|
|
364
|
+
};
|
|
365
|
+
let initializationCancelled = false;
|
|
344
366
|
// Noop until the Metro instance resolves and the deriver is attached.
|
|
345
367
|
let disposeMetroDiagnostics = () => undefined;
|
|
346
368
|
try {
|
|
@@ -348,11 +370,18 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
348
370
|
diagnostics.measure('metro.init', () => getMetroInstance({
|
|
349
371
|
projectRoot,
|
|
350
372
|
harnessConfig: runtimeConfig,
|
|
373
|
+
metroConfigEnhancer: platform.metroConfigEnhancer,
|
|
374
|
+
platformId: platform.platformId,
|
|
375
|
+
platformConfig: platform.config,
|
|
351
376
|
websocketEndpoints: {
|
|
352
377
|
[HARNESS_BRIDGE_PATH]: bridge.ws,
|
|
353
378
|
},
|
|
354
379
|
watchMode: globalConfig.watch || globalConfig.watchAll,
|
|
355
380
|
}, sessionController.signal).then((instance) => {
|
|
381
|
+
if (initializationCancelled) {
|
|
382
|
+
return instance.dispose().then(() => instance);
|
|
383
|
+
}
|
|
384
|
+
initialized.metro = instance;
|
|
356
385
|
sessionLogger.debug('Metro initialized');
|
|
357
386
|
// Attach the Metro diagnostics deriver before the eager prewarm
|
|
358
387
|
// fires so its bundle build/request events land in the trace.
|
|
@@ -384,6 +413,10 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
384
413
|
crashArtifactWriter,
|
|
385
414
|
diagnostics,
|
|
386
415
|
})).then((instance) => {
|
|
416
|
+
if (initializationCancelled) {
|
|
417
|
+
return instance.dispose().then(() => instance);
|
|
418
|
+
}
|
|
419
|
+
initialized.platform = instance;
|
|
387
420
|
sessionLogger.debug('platform runner initialized');
|
|
388
421
|
return instance;
|
|
389
422
|
});
|
|
@@ -392,10 +425,16 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
392
425
|
]);
|
|
393
426
|
}
|
|
394
427
|
catch (error) {
|
|
395
|
-
//
|
|
396
|
-
//
|
|
428
|
+
// Roll back every resource whose initialization won a concurrent race.
|
|
429
|
+
// Leases are released by the outer catch.
|
|
430
|
+
initializationCancelled = true;
|
|
431
|
+
sessionController.abort();
|
|
397
432
|
disposeMetroDiagnostics();
|
|
398
|
-
await
|
|
433
|
+
await Promise.allSettled([
|
|
434
|
+
bridge.dispose(),
|
|
435
|
+
initialized.metro?.dispose(),
|
|
436
|
+
initialized.platform?.dispose(),
|
|
437
|
+
]);
|
|
399
438
|
throw error;
|
|
400
439
|
}
|
|
401
440
|
const appLaunchOptions = platform.config.appLaunchOptions;
|
|
@@ -483,7 +522,10 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
483
522
|
sessionLogger.debug('disposing session (reason=%s)', reason);
|
|
484
523
|
const disposeSpan = diagnostics.start('dispose.total', { reason });
|
|
485
524
|
let hookError;
|
|
486
|
-
|
|
525
|
+
// Abort only cancels finite/in-flight work. Ownership remains explicit
|
|
526
|
+
// below, so this cannot race a platform or app-session disposal path.
|
|
527
|
+
sessionController.abort();
|
|
528
|
+
const hookWork = (async () => {
|
|
487
529
|
await hooks.drain();
|
|
488
530
|
await pluginManager.callHook('harness:after-run', {
|
|
489
531
|
runId: currentRun?.runId,
|
|
@@ -501,9 +543,13 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
501
543
|
error: currentRun?.error,
|
|
502
544
|
});
|
|
503
545
|
await hooks.drain();
|
|
546
|
+
})();
|
|
547
|
+
const hookResult = await waitForBoundedSettlement(hookWork, DISPOSE_HOOK_TIMEOUT_MS);
|
|
548
|
+
if (hookResult === null) {
|
|
549
|
+
hookError = new Error(`Timed out after ${DISPOSE_HOOK_TIMEOUT_MS}ms while running disposal hooks.`);
|
|
504
550
|
}
|
|
505
|
-
|
|
506
|
-
hookError =
|
|
551
|
+
else if (hookResult.status === 'rejected') {
|
|
552
|
+
hookError = hookResult.reason;
|
|
507
553
|
}
|
|
508
554
|
if (runtimeConfig.forwardClientLogs) {
|
|
509
555
|
metroInstance.events.removeListener(clientLogListener);
|
|
@@ -532,28 +578,23 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
532
578
|
sessionLogger.warn('failed to collect native coverage: %s', error);
|
|
533
579
|
}
|
|
534
580
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
finally {
|
|
554
|
-
await resourceLease.release();
|
|
555
|
-
sessionController.abort();
|
|
556
|
-
}
|
|
581
|
+
// The app session (and its log streams) must finish before platform
|
|
582
|
+
// disposal can remove the underlying device. Run independent cleanup
|
|
583
|
+
// all-settled so one failure cannot strand another owned resource.
|
|
584
|
+
const appCleanup = await Promise.allSettled([
|
|
585
|
+
crashMonitor.dispose(),
|
|
586
|
+
disposeCurrentAppSession(),
|
|
587
|
+
]);
|
|
588
|
+
const infrastructureCleanup = await Promise.allSettled([
|
|
589
|
+
bridge.dispose(),
|
|
590
|
+
platformInstance.dispose(),
|
|
591
|
+
metroInstance.dispose(),
|
|
592
|
+
metroPortLease?.release(),
|
|
593
|
+
]);
|
|
594
|
+
const leaseCleanup = await Promise.allSettled([resourceLease.release()]);
|
|
595
|
+
const cleanupError = [...appCleanup, ...infrastructureCleanup, ...leaseCleanup]
|
|
596
|
+
.find((result) => result.status === 'rejected')
|
|
597
|
+
?.reason;
|
|
557
598
|
sessionLogger.debug('session resources disposed');
|
|
558
599
|
if (hookError) {
|
|
559
600
|
disposeSpan.fail(hookError);
|
|
@@ -786,6 +827,9 @@ export const createHarnessSession = async (globalConfig, { lockManager = default
|
|
|
786
827
|
// (e.g. a platform init call abandoned after a readiness timeout race).
|
|
787
828
|
sessionController.abort();
|
|
788
829
|
await Promise.allSettled([resourceLease.release(), metroPortLease?.release()]);
|
|
830
|
+
if (receivedEarlySignal) {
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
789
833
|
throw error;
|
|
790
834
|
}
|
|
791
835
|
};
|
|
@@ -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,
|
|
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"}
|
package/dist/resource-lock.js
CHANGED
|
@@ -239,8 +239,18 @@ export const createResourceLockManager = (options = {}) => {
|
|
|
239
239
|
if (released) {
|
|
240
240
|
return;
|
|
241
241
|
}
|
|
242
|
-
|
|
243
|
-
|
|
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
|
+
}
|
|
244
254
|
}
|
|
245
255
|
finally {
|
|
246
256
|
heartbeatInFlight = false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toJestStatus.d.ts","sourceRoot":"","sources":["../src/toJestStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,eAAO,MAAM,YAAY,GAAI,QAAQ,gBAAgB,KAAG,MACb,CAAC"}
|
package/dist/toTestResult.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TestResult } from '@jest/test-result';
|
|
2
|
+
import type { TestResultStatus } from '@react-native-harness/bridge';
|
|
2
3
|
export type Options = {
|
|
3
4
|
stats: {
|
|
4
5
|
failures: number;
|
|
@@ -16,7 +17,7 @@ export type Options = {
|
|
|
16
17
|
testPath?: string;
|
|
17
18
|
title?: string;
|
|
18
19
|
fullName?: string;
|
|
19
|
-
status:
|
|
20
|
+
status: TestResultStatus;
|
|
20
21
|
location?: {
|
|
21
22
|
column: number;
|
|
22
23
|
line: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toTestResult.d.ts","sourceRoot":"","sources":["../src/toTestResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"toTestResult.d.ts","sourceRoot":"","sources":["../src/toTestResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;QACX,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,gBAAgB,CAAC;QACzB,QAAQ,CAAC,EAAE;YACT,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;CACnC,CAAC;AAwDF,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,UAiB/C,CAAC"}
|
package/dist/toTestResult.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toJestStatus } from './toJestStatus.js';
|
|
1
2
|
const getPerfStats = ({ stats }) => {
|
|
2
3
|
const start = new Date(stats.start).getTime();
|
|
3
4
|
const end = new Date(stats.end).getTime();
|
|
@@ -38,7 +39,7 @@ const getTestResults = ({ errorMessage, tests, jestTestPath, }) => {
|
|
|
38
39
|
failureMessages: actualErrorMessage ? [actualErrorMessage] : [],
|
|
39
40
|
fullName: test.fullName || test.testPath || jestTestPath || '',
|
|
40
41
|
numPassingAsserts: test.status === 'passed' ? 1 : 0,
|
|
41
|
-
status: test.status,
|
|
42
|
+
status: toJestStatus(test.status),
|
|
42
43
|
title: test.title || '',
|
|
43
44
|
location: test.location,
|
|
44
45
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-harness/jest",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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/bridge": "1.
|
|
43
|
-
"@react-native-harness/
|
|
44
|
-
"@react-native-harness/
|
|
45
|
-
"@react-native-harness/
|
|
46
|
-
"@react-native-harness/
|
|
47
|
-
"@react-native-harness/
|
|
48
|
-
"@react-native-harness/
|
|
42
|
+
"@react-native-harness/bridge": "1.5.0",
|
|
43
|
+
"@react-native-harness/config": "1.5.0",
|
|
44
|
+
"@react-native-harness/plugins": "1.5.0",
|
|
45
|
+
"@react-native-harness/cache": "1.5.0",
|
|
46
|
+
"@react-native-harness/bundler-metro": "1.5.0",
|
|
47
|
+
"@react-native-harness/platforms": "1.5.0",
|
|
48
|
+
"@react-native-harness/tools": "1.5.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/yargs": "^17.0.32",
|
package/src/execute-run.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
import { formatHarnessErrorMessage } from './format-harness-error.js';
|
|
33
33
|
import { logger } from '@react-native-harness/tools';
|
|
34
34
|
import { printSummary, writeTraceFile } from './diagnostics/index.js';
|
|
35
|
+
import { toJestStatus } from './toJestStatus.js';
|
|
35
36
|
|
|
36
37
|
const diagnosticsLogger = logger.child('diagnostics');
|
|
37
38
|
|
|
@@ -109,7 +110,7 @@ const emitHarnessTestFinished = async (
|
|
|
109
110
|
location,
|
|
110
111
|
numPassingAsserts: event.status === 'passed' ? 1 : 0,
|
|
111
112
|
startedAt: event.startedAt,
|
|
112
|
-
status: event.status,
|
|
113
|
+
status: toJestStatus(event.status),
|
|
113
114
|
title: event.name,
|
|
114
115
|
};
|
|
115
116
|
|
package/src/harness-session.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import {
|
|
39
39
|
createCrashArtifactWriter,
|
|
40
40
|
createDiagnostics,
|
|
41
|
+
delay,
|
|
41
42
|
logger,
|
|
42
43
|
getTimeoutSignal,
|
|
43
44
|
ensureSignalsDeliverable,
|
|
@@ -94,6 +95,25 @@ const ignorePromiseRejection = () => undefined;
|
|
|
94
95
|
const isBridgeDisconnectError = (error: unknown) =>
|
|
95
96
|
error instanceof Error && error.message === 'App bridge disconnected';
|
|
96
97
|
const TEST_RUN_BRIDGE_STABILITY_WAIT_MS = 500;
|
|
98
|
+
const DISPOSE_HOOK_TIMEOUT_MS = 5_000;
|
|
99
|
+
|
|
100
|
+
const waitForBoundedSettlement = async <T>(
|
|
101
|
+
promise: Promise<T>,
|
|
102
|
+
timeoutMs: number
|
|
103
|
+
): Promise<PromiseSettledResult<T> | null> => {
|
|
104
|
+
const timeout = delay(timeoutMs);
|
|
105
|
+
try {
|
|
106
|
+
return await Promise.race([
|
|
107
|
+
promise.then(
|
|
108
|
+
(value) => ({ status: 'fulfilled', value }) as const,
|
|
109
|
+
(reason) => ({ status: 'rejected', reason }) as const
|
|
110
|
+
),
|
|
111
|
+
timeout.promise.then(() => null),
|
|
112
|
+
]);
|
|
113
|
+
} finally {
|
|
114
|
+
timeout.cancel();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
97
117
|
|
|
98
118
|
type DisconnectObservableBridge = {
|
|
99
119
|
readonly connection: AppConnection | null;
|
|
@@ -487,7 +507,11 @@ export const createHarnessSession = async (
|
|
|
487
507
|
// child the runner threads it into.
|
|
488
508
|
ensureSignalsDeliverable();
|
|
489
509
|
const sessionController = new AbortController();
|
|
490
|
-
|
|
510
|
+
let receivedEarlySignal = false;
|
|
511
|
+
const onEarlySignal = () => {
|
|
512
|
+
receivedEarlySignal = true;
|
|
513
|
+
sessionController.abort();
|
|
514
|
+
};
|
|
491
515
|
process.once('SIGTERM', onEarlySignal);
|
|
492
516
|
process.once('SIGINT', onEarlySignal);
|
|
493
517
|
|
|
@@ -584,6 +608,11 @@ export const createHarnessSession = async (
|
|
|
584
608
|
|
|
585
609
|
let metroInstance: MetroInstance;
|
|
586
610
|
let platformInstance: HarnessPlatformRunner;
|
|
611
|
+
const initialized = {
|
|
612
|
+
metro: null as MetroInstance | null,
|
|
613
|
+
platform: null as HarnessPlatformRunner | null,
|
|
614
|
+
};
|
|
615
|
+
let initializationCancelled = false;
|
|
587
616
|
// Noop until the Metro instance resolves and the deriver is attached.
|
|
588
617
|
let disposeMetroDiagnostics: () => void = () => undefined;
|
|
589
618
|
|
|
@@ -594,6 +623,9 @@ export const createHarnessSession = async (
|
|
|
594
623
|
{
|
|
595
624
|
projectRoot,
|
|
596
625
|
harnessConfig: runtimeConfig,
|
|
626
|
+
metroConfigEnhancer: platform.metroConfigEnhancer,
|
|
627
|
+
platformId: platform.platformId,
|
|
628
|
+
platformConfig: platform.config,
|
|
597
629
|
websocketEndpoints: {
|
|
598
630
|
[HARNESS_BRIDGE_PATH]: bridge.ws as unknown as MetroWebSocketEndpoint,
|
|
599
631
|
},
|
|
@@ -601,6 +633,10 @@ export const createHarnessSession = async (
|
|
|
601
633
|
},
|
|
602
634
|
sessionController.signal,
|
|
603
635
|
).then((instance) => {
|
|
636
|
+
if (initializationCancelled) {
|
|
637
|
+
return instance.dispose().then(() => instance);
|
|
638
|
+
}
|
|
639
|
+
initialized.metro = instance;
|
|
604
640
|
sessionLogger.debug('Metro initialized');
|
|
605
641
|
// Attach the Metro diagnostics deriver before the eager prewarm
|
|
606
642
|
// fires so its bundle build/request events land in the trace.
|
|
@@ -638,6 +674,10 @@ export const createHarnessSession = async (
|
|
|
638
674
|
diagnostics,
|
|
639
675
|
} satisfies HarnessPlatformInitOptions),
|
|
640
676
|
).then((instance) => {
|
|
677
|
+
if (initializationCancelled) {
|
|
678
|
+
return instance.dispose().then(() => instance);
|
|
679
|
+
}
|
|
680
|
+
initialized.platform = instance;
|
|
641
681
|
sessionLogger.debug('platform runner initialized');
|
|
642
682
|
return instance;
|
|
643
683
|
});
|
|
@@ -647,10 +687,16 @@ export const createHarnessSession = async (
|
|
|
647
687
|
),
|
|
648
688
|
]);
|
|
649
689
|
} catch (error) {
|
|
650
|
-
//
|
|
651
|
-
//
|
|
690
|
+
// Roll back every resource whose initialization won a concurrent race.
|
|
691
|
+
// Leases are released by the outer catch.
|
|
692
|
+
initializationCancelled = true;
|
|
693
|
+
sessionController.abort();
|
|
652
694
|
disposeMetroDiagnostics();
|
|
653
|
-
await
|
|
695
|
+
await Promise.allSettled([
|
|
696
|
+
bridge.dispose(),
|
|
697
|
+
initialized.metro?.dispose(),
|
|
698
|
+
initialized.platform?.dispose(),
|
|
699
|
+
]);
|
|
654
700
|
throw error;
|
|
655
701
|
}
|
|
656
702
|
|
|
@@ -759,7 +805,11 @@ export const createHarnessSession = async (
|
|
|
759
805
|
const disposeSpan = diagnostics.start('dispose.total', { reason });
|
|
760
806
|
let hookError: unknown;
|
|
761
807
|
|
|
762
|
-
|
|
808
|
+
// Abort only cancels finite/in-flight work. Ownership remains explicit
|
|
809
|
+
// below, so this cannot race a platform or app-session disposal path.
|
|
810
|
+
sessionController.abort();
|
|
811
|
+
|
|
812
|
+
const hookWork = (async () => {
|
|
763
813
|
await hooks.drain();
|
|
764
814
|
await pluginManager.callHook('harness:after-run', {
|
|
765
815
|
runId: currentRun?.runId,
|
|
@@ -777,8 +827,17 @@ export const createHarnessSession = async (
|
|
|
777
827
|
error: currentRun?.error,
|
|
778
828
|
});
|
|
779
829
|
await hooks.drain();
|
|
780
|
-
}
|
|
781
|
-
|
|
830
|
+
})();
|
|
831
|
+
const hookResult = await waitForBoundedSettlement(
|
|
832
|
+
hookWork,
|
|
833
|
+
DISPOSE_HOOK_TIMEOUT_MS
|
|
834
|
+
);
|
|
835
|
+
if (hookResult === null) {
|
|
836
|
+
hookError = new Error(
|
|
837
|
+
`Timed out after ${DISPOSE_HOOK_TIMEOUT_MS}ms while running disposal hooks.`
|
|
838
|
+
);
|
|
839
|
+
} else if (hookResult.status === 'rejected') {
|
|
840
|
+
hookError = hookResult.reason;
|
|
782
841
|
}
|
|
783
842
|
|
|
784
843
|
if (runtimeConfig.forwardClientLogs) {
|
|
@@ -809,26 +868,23 @@ export const createHarnessSession = async (
|
|
|
809
868
|
}
|
|
810
869
|
}
|
|
811
870
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
await resourceLease.release();
|
|
830
|
-
sessionController.abort();
|
|
831
|
-
}
|
|
871
|
+
// The app session (and its log streams) must finish before platform
|
|
872
|
+
// disposal can remove the underlying device. Run independent cleanup
|
|
873
|
+
// all-settled so one failure cannot strand another owned resource.
|
|
874
|
+
const appCleanup = await Promise.allSettled([
|
|
875
|
+
crashMonitor.dispose(),
|
|
876
|
+
disposeCurrentAppSession(),
|
|
877
|
+
]);
|
|
878
|
+
const infrastructureCleanup = await Promise.allSettled([
|
|
879
|
+
bridge.dispose(),
|
|
880
|
+
platformInstance.dispose(),
|
|
881
|
+
metroInstance.dispose(),
|
|
882
|
+
metroPortLease?.release(),
|
|
883
|
+
]);
|
|
884
|
+
const leaseCleanup = await Promise.allSettled([resourceLease.release()]);
|
|
885
|
+
const cleanupError = [...appCleanup, ...infrastructureCleanup, ...leaseCleanup]
|
|
886
|
+
.find((result): result is PromiseRejectedResult => result.status === 'rejected')
|
|
887
|
+
?.reason;
|
|
832
888
|
|
|
833
889
|
sessionLogger.debug('session resources disposed');
|
|
834
890
|
|
|
@@ -1102,6 +1158,9 @@ export const createHarnessSession = async (
|
|
|
1102
1158
|
// (e.g. a platform init call abandoned after a readiness timeout race).
|
|
1103
1159
|
sessionController.abort();
|
|
1104
1160
|
await Promise.allSettled([resourceLease.release(), metroPortLease?.release()]);
|
|
1161
|
+
if (receivedEarlySignal) {
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
1105
1164
|
throw error;
|
|
1106
1165
|
}
|
|
1107
1166
|
};
|
package/src/resource-lock.ts
CHANGED
|
@@ -392,8 +392,21 @@ export const createResourceLockManager = (
|
|
|
392
392
|
return;
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
|
|
396
|
-
|
|
395
|
+
try {
|
|
396
|
+
await writeJsonFileAtomic(paths.ownerFilePath, nextMetadata);
|
|
397
|
+
scopedLogger.debug('refreshed heartbeat for ticket %s', ticketId);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
// A failed refresh is not fatal by design: the lock goes stale
|
|
400
|
+
// and another holder reclaims it. Swallow it so a transient
|
|
401
|
+
// write error (e.g. the owner file racing a concurrent release,
|
|
402
|
+
// or an EPERM on Windows when the directory is being torn down)
|
|
403
|
+
// never surfaces as an unhandled rejection from this interval.
|
|
404
|
+
scopedLogger.debug(
|
|
405
|
+
'heartbeat refresh for ticket %s failed: %s',
|
|
406
|
+
ticketId,
|
|
407
|
+
error instanceof Error ? error.message : String(error),
|
|
408
|
+
);
|
|
409
|
+
}
|
|
397
410
|
} finally {
|
|
398
411
|
heartbeatInFlight = false;
|
|
399
412
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Status } from '@jest/test-result';
|
|
2
|
+
import type { TestResultStatus } from '@react-native-harness/bridge';
|
|
3
|
+
|
|
4
|
+
// Jest's own runners expose skipped assertions as `pending` to reporters.
|
|
5
|
+
export const toJestStatus = (status: TestResultStatus): Status =>
|
|
6
|
+
status === 'skipped' ? 'pending' : status;
|
package/src/toTestResult.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TestResult } from '@jest/test-result';
|
|
2
|
+
import type { TestResultStatus } from '@react-native-harness/bridge';
|
|
3
|
+
import { toJestStatus } from './toJestStatus.js';
|
|
2
4
|
|
|
3
5
|
export type Options = {
|
|
4
6
|
stats: {
|
|
@@ -17,7 +19,7 @@ export type Options = {
|
|
|
17
19
|
testPath?: string;
|
|
18
20
|
title?: string;
|
|
19
21
|
fullName?: string;
|
|
20
|
-
status:
|
|
22
|
+
status: TestResultStatus;
|
|
21
23
|
location?: {
|
|
22
24
|
column: number;
|
|
23
25
|
line: number;
|
|
@@ -75,7 +77,7 @@ const getTestResults = ({
|
|
|
75
77
|
failureMessages: actualErrorMessage ? [actualErrorMessage] : [],
|
|
76
78
|
fullName: test.fullName || test.testPath || jestTestPath || '',
|
|
77
79
|
numPassingAsserts: test.status === 'passed' ? 1 : 0,
|
|
78
|
-
status: test.status,
|
|
80
|
+
status: toJestStatus(test.status),
|
|
79
81
|
title: test.title || '',
|
|
80
82
|
location: test.location,
|
|
81
83
|
};
|
|
@@ -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"}
|