@react-native-harness/jest 1.1.0-rc.4 → 1.2.0-rc.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.
- package/dist/__tests__/harness.test.js +35 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +6 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/__tests__/harness.test.ts +93 -40
- package/src/harness.ts +28 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-harness/jest",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"p-limit": "^7.1.1",
|
|
41
41
|
"tslib": "^2.3.0",
|
|
42
42
|
"yargs": "^17.7.2",
|
|
43
|
-
"@react-native-harness/
|
|
44
|
-
"@react-native-harness/
|
|
45
|
-
"@react-native-harness/platforms": "1.
|
|
46
|
-
"@react-native-harness/tools": "1.
|
|
47
|
-
"@react-native-harness/
|
|
48
|
-
"@react-native-harness/
|
|
43
|
+
"@react-native-harness/bundler-metro": "1.2.0-rc.1",
|
|
44
|
+
"@react-native-harness/config": "1.2.0-rc.1",
|
|
45
|
+
"@react-native-harness/platforms": "1.2.0-rc.1",
|
|
46
|
+
"@react-native-harness/tools": "1.2.0-rc.1",
|
|
47
|
+
"@react-native-harness/bridge": "1.2.0-rc.1",
|
|
48
|
+
"@react-native-harness/plugins": "1.2.0-rc.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/yargs": "^17.0.32",
|
|
@@ -125,7 +125,7 @@ const createReporter = (): Reporter => {
|
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
const createMetroInstance = (
|
|
128
|
-
overrides: Partial<MetroInstance> = {}
|
|
128
|
+
overrides: Partial<MetroInstance> = {},
|
|
129
129
|
): MetroInstance => ({
|
|
130
130
|
events: createReporter(),
|
|
131
131
|
httpServer: {} as never,
|
|
@@ -163,7 +163,7 @@ const createAppMonitor = (): {
|
|
|
163
163
|
};
|
|
164
164
|
|
|
165
165
|
const createPlatformRunner = (
|
|
166
|
-
overrides: Partial<HarnessPlatformRunner> = {}
|
|
166
|
+
overrides: Partial<HarnessPlatformRunner> = {},
|
|
167
167
|
): HarnessPlatformRunner => ({
|
|
168
168
|
startApp: vi.fn(async () => undefined),
|
|
169
169
|
restartApp: vi.fn(async () => undefined),
|
|
@@ -175,7 +175,7 @@ const createPlatformRunner = (
|
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
const createHarnessConfig = (
|
|
178
|
-
overrides: Partial<HarnessConfig> = {}
|
|
178
|
+
overrides: Partial<HarnessConfig> = {},
|
|
179
179
|
): HarnessConfig =>
|
|
180
180
|
({
|
|
181
181
|
appRegistryComponentName: 'App',
|
|
@@ -196,7 +196,7 @@ const createHarnessConfig = (
|
|
|
196
196
|
unstable__skipAlreadyIncludedModules: false,
|
|
197
197
|
webSocketPort: 3001,
|
|
198
198
|
...overrides,
|
|
199
|
-
} as HarnessConfig
|
|
199
|
+
}) as HarnessConfig;
|
|
200
200
|
|
|
201
201
|
beforeEach(() => {
|
|
202
202
|
vi.clearAllMocks();
|
|
@@ -230,7 +230,7 @@ describe('waitForAppReady', () => {
|
|
|
230
230
|
emitReady();
|
|
231
231
|
await readyPromise;
|
|
232
232
|
options.onAttemptReset?.();
|
|
233
|
-
}
|
|
233
|
+
},
|
|
234
234
|
);
|
|
235
235
|
|
|
236
236
|
await waitForAppReady({
|
|
@@ -255,7 +255,7 @@ describe('waitForAppReady', () => {
|
|
|
255
255
|
startAttempt: expect.any(Function),
|
|
256
256
|
waitForReady: expect.any(Function),
|
|
257
257
|
waitForCrash: expect.any(Function),
|
|
258
|
-
})
|
|
258
|
+
}),
|
|
259
259
|
);
|
|
260
260
|
expect(crashSupervisor.isReady()).toBe(true);
|
|
261
261
|
|
|
@@ -276,7 +276,7 @@ describe('waitForAppReady', () => {
|
|
|
276
276
|
mocks.waitForMetroBackedAppReady.mockImplementationOnce(
|
|
277
277
|
async (options: WaitForMetroBackedAppReadyOptions) => {
|
|
278
278
|
await options.startAttempt();
|
|
279
|
-
}
|
|
279
|
+
},
|
|
280
280
|
);
|
|
281
281
|
|
|
282
282
|
await waitForAppReady({
|
|
@@ -324,7 +324,7 @@ describe('getHarness', () => {
|
|
|
324
324
|
setTimeout(() => {
|
|
325
325
|
reject(new DOMException('The operation was aborted', 'AbortError'));
|
|
326
326
|
}, 20);
|
|
327
|
-
})
|
|
327
|
+
}),
|
|
328
328
|
);
|
|
329
329
|
|
|
330
330
|
const platform: HarnessPlatform = {
|
|
@@ -333,7 +333,7 @@ describe('getHarness', () => {
|
|
|
333
333
|
name: 'ios',
|
|
334
334
|
platformId: 'ios',
|
|
335
335
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
336
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
336
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
337
337
|
)}`,
|
|
338
338
|
};
|
|
339
339
|
|
|
@@ -343,8 +343,8 @@ describe('getHarness', () => {
|
|
|
343
343
|
platformReadyTimeout: 10,
|
|
344
344
|
}),
|
|
345
345
|
platform,
|
|
346
|
-
'/tmp/project'
|
|
347
|
-
)
|
|
346
|
+
'/tmp/project',
|
|
347
|
+
),
|
|
348
348
|
).rejects.toBeInstanceOf(PlatformReadyTimeoutError);
|
|
349
349
|
});
|
|
350
350
|
|
|
@@ -372,14 +372,14 @@ describe('getHarness', () => {
|
|
|
372
372
|
name: 'ios',
|
|
373
373
|
platformId: 'ios',
|
|
374
374
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
375
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
375
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
376
376
|
)}`,
|
|
377
377
|
};
|
|
378
378
|
|
|
379
379
|
const harness = await getHarness(
|
|
380
380
|
createHarnessConfig(),
|
|
381
381
|
platform,
|
|
382
|
-
'/tmp/project'
|
|
382
|
+
'/tmp/project',
|
|
383
383
|
);
|
|
384
384
|
|
|
385
385
|
expect(runner).toHaveBeenCalledWith(
|
|
@@ -389,7 +389,7 @@ describe('getHarness', () => {
|
|
|
389
389
|
}),
|
|
390
390
|
expect.objectContaining({
|
|
391
391
|
signal: expect.any(AbortSignal),
|
|
392
|
-
})
|
|
392
|
+
}),
|
|
393
393
|
);
|
|
394
394
|
|
|
395
395
|
await harness.dispose();
|
|
@@ -422,14 +422,14 @@ describe('getHarness', () => {
|
|
|
422
422
|
name: 'android',
|
|
423
423
|
platformId: 'android',
|
|
424
424
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
425
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
425
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
426
426
|
)}`,
|
|
427
427
|
};
|
|
428
428
|
|
|
429
429
|
const harness = await getHarness(
|
|
430
430
|
createHarnessConfig(),
|
|
431
431
|
platform,
|
|
432
|
-
'/tmp/project'
|
|
432
|
+
'/tmp/project',
|
|
433
433
|
);
|
|
434
434
|
|
|
435
435
|
expect(harness.config.metroPort).toBe(8082);
|
|
@@ -439,7 +439,7 @@ describe('getHarness', () => {
|
|
|
439
439
|
metroPort: 8082,
|
|
440
440
|
}),
|
|
441
441
|
}),
|
|
442
|
-
expect.any(AbortSignal)
|
|
442
|
+
expect.any(AbortSignal),
|
|
443
443
|
);
|
|
444
444
|
expect(runner).toHaveBeenCalledWith(
|
|
445
445
|
platform.config,
|
|
@@ -448,7 +448,7 @@ describe('getHarness', () => {
|
|
|
448
448
|
}),
|
|
449
449
|
expect.objectContaining({
|
|
450
450
|
signal: expect.any(AbortSignal),
|
|
451
|
-
})
|
|
451
|
+
}),
|
|
452
452
|
);
|
|
453
453
|
expect(mocks.logMetroPortFallback).toHaveBeenCalledWith(8081, 8082);
|
|
454
454
|
|
|
@@ -467,7 +467,7 @@ describe('getHarness', () => {
|
|
|
467
467
|
};
|
|
468
468
|
|
|
469
469
|
await expect(
|
|
470
|
-
getHarness(createHarnessConfig(), platform, '/tmp/project')
|
|
470
|
+
getHarness(createHarnessConfig(), platform, '/tmp/project'),
|
|
471
471
|
).rejects.toBeInstanceOf(MetroPortRangeExhaustedError);
|
|
472
472
|
|
|
473
473
|
expect(mocks.getBridgeServer).not.toHaveBeenCalled();
|
|
@@ -496,14 +496,14 @@ describe('getHarness', () => {
|
|
|
496
496
|
name: 'legacy-ios',
|
|
497
497
|
platformId: 'ios',
|
|
498
498
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
499
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
499
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
500
500
|
)}`,
|
|
501
501
|
};
|
|
502
502
|
|
|
503
503
|
const harness = await getHarness(
|
|
504
504
|
createHarnessConfig(),
|
|
505
505
|
platform,
|
|
506
|
-
'/tmp/project'
|
|
506
|
+
'/tmp/project',
|
|
507
507
|
);
|
|
508
508
|
|
|
509
509
|
await harness.dispose();
|
|
@@ -527,7 +527,7 @@ describe('getHarness', () => {
|
|
|
527
527
|
const readyPromise = options.waitForReady(new AbortController().signal);
|
|
528
528
|
emitReady();
|
|
529
529
|
await readyPromise;
|
|
530
|
-
}
|
|
530
|
+
},
|
|
531
531
|
);
|
|
532
532
|
|
|
533
533
|
(
|
|
@@ -547,7 +547,7 @@ describe('getHarness', () => {
|
|
|
547
547
|
name: 'ios',
|
|
548
548
|
platformId: 'ios',
|
|
549
549
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
550
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
550
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
551
551
|
)}`,
|
|
552
552
|
getResourceLockKey: () => 'ios:simulator:iPhone 17 Pro:26.2',
|
|
553
553
|
};
|
|
@@ -557,7 +557,7 @@ describe('getHarness', () => {
|
|
|
557
557
|
bridgeTimeout: 1,
|
|
558
558
|
}),
|
|
559
559
|
platform,
|
|
560
|
-
'/tmp/project'
|
|
560
|
+
'/tmp/project',
|
|
561
561
|
);
|
|
562
562
|
|
|
563
563
|
await harness.ensureAppReady('/tmp/example.harness.ts');
|
|
@@ -593,7 +593,7 @@ describe('getHarness', () => {
|
|
|
593
593
|
const readyPromise = options.waitForReady(new AbortController().signal);
|
|
594
594
|
emitReady();
|
|
595
595
|
await readyPromise;
|
|
596
|
-
}
|
|
596
|
+
},
|
|
597
597
|
);
|
|
598
598
|
|
|
599
599
|
(
|
|
@@ -613,7 +613,7 @@ describe('getHarness', () => {
|
|
|
613
613
|
name: 'ios',
|
|
614
614
|
platformId: 'ios',
|
|
615
615
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
616
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
616
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
617
617
|
)}`,
|
|
618
618
|
getResourceLockKey: () => 'ios:simulator:iPhone 17 Pro:26.2',
|
|
619
619
|
};
|
|
@@ -621,13 +621,13 @@ describe('getHarness', () => {
|
|
|
621
621
|
const harness = await getHarness(
|
|
622
622
|
createHarnessConfig(),
|
|
623
623
|
platform,
|
|
624
|
-
'/tmp/project'
|
|
624
|
+
'/tmp/project',
|
|
625
625
|
);
|
|
626
626
|
|
|
627
627
|
expect(mocks.getBridgeServer).toHaveBeenCalledWith(
|
|
628
628
|
expect.objectContaining({
|
|
629
629
|
noServer: true,
|
|
630
|
-
})
|
|
630
|
+
}),
|
|
631
631
|
);
|
|
632
632
|
expect(mocks.getMetroInstance).toHaveBeenCalledWith(
|
|
633
633
|
expect.objectContaining({
|
|
@@ -635,7 +635,7 @@ describe('getHarness', () => {
|
|
|
635
635
|
[HARNESS_BRIDGE_PATH]: serverBridge.ws,
|
|
636
636
|
},
|
|
637
637
|
}),
|
|
638
|
-
expect.any(AbortSignal)
|
|
638
|
+
expect.any(AbortSignal),
|
|
639
639
|
);
|
|
640
640
|
await harness.restart('/tmp/restart.harness.ts');
|
|
641
641
|
|
|
@@ -691,7 +691,7 @@ describe('plugins', () => {
|
|
|
691
691
|
ctx.appLaunchOptions == null
|
|
692
692
|
? 'no-launch-options'
|
|
693
693
|
: 'launch-options'
|
|
694
|
-
}
|
|
694
|
+
}`,
|
|
695
695
|
);
|
|
696
696
|
},
|
|
697
697
|
beforeRun: (ctx) => {
|
|
@@ -700,24 +700,24 @@ describe('plugins', () => {
|
|
|
700
700
|
ctx.appLaunchOptions == null
|
|
701
701
|
? 'no-launch-options'
|
|
702
702
|
: 'launch-options'
|
|
703
|
-
}
|
|
703
|
+
}`,
|
|
704
704
|
);
|
|
705
705
|
},
|
|
706
706
|
afterRun: (ctx) => {
|
|
707
707
|
observedHooks.push(
|
|
708
|
-
`afterRun:${ctx.state.creationCount}:${ctx.reason}
|
|
708
|
+
`afterRun:${ctx.state.creationCount}:${ctx.reason}`,
|
|
709
709
|
);
|
|
710
710
|
},
|
|
711
711
|
beforeDispose: (ctx) => {
|
|
712
712
|
observedHooks.push(
|
|
713
|
-
`beforeDispose:${ctx.state.creationCount}:${ctx.reason}
|
|
713
|
+
`beforeDispose:${ctx.state.creationCount}:${ctx.reason}`,
|
|
714
714
|
);
|
|
715
715
|
},
|
|
716
716
|
},
|
|
717
717
|
runtime: {
|
|
718
718
|
ready: (ctx) => {
|
|
719
719
|
observedHooks.push(
|
|
720
|
-
`runtime.ready:${ctx.runId}:${ctx.device.platform}
|
|
720
|
+
`runtime.ready:${ctx.runId}:${ctx.device.platform}`,
|
|
721
721
|
);
|
|
722
722
|
},
|
|
723
723
|
disconnected: (ctx) => {
|
|
@@ -743,7 +743,7 @@ describe('plugins', () => {
|
|
|
743
743
|
name: 'ios',
|
|
744
744
|
platformId: 'ios',
|
|
745
745
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
746
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
746
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
747
747
|
)}`,
|
|
748
748
|
getResourceLockKey: () => 'ios:simulator:iPhone 17 Pro:26.2',
|
|
749
749
|
};
|
|
@@ -753,7 +753,7 @@ describe('plugins', () => {
|
|
|
753
753
|
plugins: [plugin],
|
|
754
754
|
}),
|
|
755
755
|
platform,
|
|
756
|
-
'/tmp/project'
|
|
756
|
+
'/tmp/project',
|
|
757
757
|
);
|
|
758
758
|
|
|
759
759
|
harness.setRunState({
|
|
@@ -791,6 +791,59 @@ describe('plugins', () => {
|
|
|
791
791
|
]);
|
|
792
792
|
});
|
|
793
793
|
|
|
794
|
+
it('disposes lifecycle hooks and platform resources only once', async () => {
|
|
795
|
+
const { serverBridge } = createBridgeServer();
|
|
796
|
+
const platformInstance = createPlatformRunner();
|
|
797
|
+
const observedHooks: string[] = [];
|
|
798
|
+
|
|
799
|
+
mocks.getBridgeServer.mockResolvedValue(serverBridge);
|
|
800
|
+
mocks.getMetroInstance.mockResolvedValue(createMetroInstance());
|
|
801
|
+
|
|
802
|
+
(
|
|
803
|
+
globalThis as typeof globalThis & {
|
|
804
|
+
__HARNESS_PLATFORM_RUNNER__?: (...args: unknown[]) => Promise<unknown>;
|
|
805
|
+
}
|
|
806
|
+
).__HARNESS_PLATFORM_RUNNER__ = vi.fn(async () => platformInstance);
|
|
807
|
+
|
|
808
|
+
const plugin = definePlugin({
|
|
809
|
+
name: 'dispose-plugin',
|
|
810
|
+
hooks: {
|
|
811
|
+
harness: {
|
|
812
|
+
afterRun: () => {
|
|
813
|
+
observedHooks.push('afterRun');
|
|
814
|
+
},
|
|
815
|
+
beforeDispose: () => {
|
|
816
|
+
observedHooks.push('beforeDispose');
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
},
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
const platform: HarnessPlatform = {
|
|
823
|
+
config: {},
|
|
824
|
+
name: 'ios',
|
|
825
|
+
platformId: 'ios',
|
|
826
|
+
runner: `data:text/javascript,${encodeURIComponent(
|
|
827
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
828
|
+
)}`,
|
|
829
|
+
getResourceLockKey: () => 'ios:simulator:iPhone 17 Pro:26.2',
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
const harness = await getHarness(
|
|
833
|
+
createHarnessConfig({
|
|
834
|
+
plugins: [plugin],
|
|
835
|
+
}),
|
|
836
|
+
platform,
|
|
837
|
+
'/tmp/project',
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
await harness.dispose();
|
|
841
|
+
await harness.dispose();
|
|
842
|
+
|
|
843
|
+
expect(observedHooks).toEqual(['afterRun', 'beforeDispose']);
|
|
844
|
+
expect(platformInstance.dispose).toHaveBeenCalledTimes(1);
|
|
845
|
+
});
|
|
846
|
+
|
|
794
847
|
it('waits in queue before starting Metro and releases the lock on dispose', async () => {
|
|
795
848
|
const resourceKey = 'ios:simulator:iPhone 17 Pro:26.2';
|
|
796
849
|
const firstPlatformRunner = createPlatformRunner();
|
|
@@ -828,7 +881,7 @@ describe('plugins', () => {
|
|
|
828
881
|
name: 'ios',
|
|
829
882
|
platformId: 'ios',
|
|
830
883
|
runner: `data:text/javascript,${encodeURIComponent(
|
|
831
|
-
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);'
|
|
884
|
+
'export default (...args) => globalThis.__HARNESS_PLATFORM_RUNNER__(...args);',
|
|
832
885
|
)}`,
|
|
833
886
|
getResourceLockKey: () => resourceKey,
|
|
834
887
|
};
|
|
@@ -836,13 +889,13 @@ describe('plugins', () => {
|
|
|
836
889
|
const firstHarness = await getHarness(
|
|
837
890
|
createHarnessConfig(),
|
|
838
891
|
platform,
|
|
839
|
-
'/tmp/project'
|
|
892
|
+
'/tmp/project',
|
|
840
893
|
);
|
|
841
894
|
|
|
842
895
|
const secondHarnessPromise = getHarness(
|
|
843
896
|
createHarnessConfig(),
|
|
844
897
|
platform,
|
|
845
|
-
'/tmp/project'
|
|
898
|
+
'/tmp/project',
|
|
846
899
|
);
|
|
847
900
|
|
|
848
901
|
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
@@ -864,7 +917,7 @@ describe('plugins', () => {
|
|
|
864
917
|
describe('StartupStallError', () => {
|
|
865
918
|
it('includes the configured timeout and attempt count', () => {
|
|
866
919
|
expect(new StartupStallError(1_500, 4).message).toBe(
|
|
867
|
-
'The app did not request its Metro bundle after 4 launch attempts within 1500ms. Last Metro status: unknown.'
|
|
920
|
+
'The app did not request its Metro bundle after 4 launch attempts within 1500ms. Last Metro status: unknown.',
|
|
868
921
|
);
|
|
869
922
|
});
|
|
870
923
|
});
|
package/src/harness.ts
CHANGED
|
@@ -78,7 +78,7 @@ export type Harness = {
|
|
|
78
78
|
config: HarnessConfig;
|
|
79
79
|
runTests: (
|
|
80
80
|
path: string,
|
|
81
|
-
options: HarnessRunTestsOptions
|
|
81
|
+
options: HarnessRunTestsOptions,
|
|
82
82
|
) => Promise<TestSuiteResult>;
|
|
83
83
|
ensureAppReady: (testFilePath: string) => Promise<void>;
|
|
84
84
|
restart: (testFilePath?: string) => Promise<void>;
|
|
@@ -92,7 +92,7 @@ export type Harness = {
|
|
|
92
92
|
export const maybeLogMetroCacheReuse = (
|
|
93
93
|
config: HarnessConfig,
|
|
94
94
|
platform: HarnessPlatform,
|
|
95
|
-
projectRoot: string
|
|
95
|
+
projectRoot: string,
|
|
96
96
|
): void => {
|
|
97
97
|
if (config.unstable__enableMetroCache && isMetroCacheReusable(projectRoot)) {
|
|
98
98
|
logMetroCacheReused(platform);
|
|
@@ -116,7 +116,7 @@ const waitForAbort = (signal: AbortSignal): Promise<never> => {
|
|
|
116
116
|
() => {
|
|
117
117
|
reject(signal.reason ?? createAbortError());
|
|
118
118
|
},
|
|
119
|
-
{ once: true }
|
|
119
|
+
{ once: true },
|
|
120
120
|
);
|
|
121
121
|
});
|
|
122
122
|
};
|
|
@@ -239,7 +239,7 @@ const getHarnessInternal = async (
|
|
|
239
239
|
config: HarnessConfig,
|
|
240
240
|
platform: HarnessPlatform,
|
|
241
241
|
projectRoot: string,
|
|
242
|
-
signal: AbortSignal
|
|
242
|
+
signal: AbortSignal,
|
|
243
243
|
): Promise<Harness> => {
|
|
244
244
|
const context: HarnessContext = {
|
|
245
245
|
platform,
|
|
@@ -247,7 +247,7 @@ const getHarnessInternal = async (
|
|
|
247
247
|
harnessLogger.debug(
|
|
248
248
|
'creating Harness internals for runner=%s platform=%s',
|
|
249
249
|
platform.name,
|
|
250
|
-
platform.platformId
|
|
250
|
+
platform.platformId,
|
|
251
251
|
);
|
|
252
252
|
const resourceLockKey = await (platform.getResourceLockKey?.() ??
|
|
253
253
|
getDefaultResourceLockKey(platform));
|
|
@@ -261,7 +261,7 @@ const getHarnessInternal = async (
|
|
|
261
261
|
harnessLogger.debug(
|
|
262
262
|
'waiting in queue for runner=%s key=%s',
|
|
263
263
|
platform.name,
|
|
264
|
-
resourceLockKey
|
|
264
|
+
resourceLockKey,
|
|
265
265
|
);
|
|
266
266
|
},
|
|
267
267
|
onStillWaiting: (elapsedMs) => {
|
|
@@ -275,7 +275,7 @@ const getHarnessInternal = async (
|
|
|
275
275
|
'still waiting in queue for runner=%s key=%s elapsedMs=%d',
|
|
276
276
|
platform.name,
|
|
277
277
|
resourceLockKey,
|
|
278
|
-
elapsedMs
|
|
278
|
+
elapsedMs,
|
|
279
279
|
);
|
|
280
280
|
},
|
|
281
281
|
});
|
|
@@ -285,7 +285,7 @@ const getHarnessInternal = async (
|
|
|
285
285
|
harnessLogger.debug(
|
|
286
286
|
'resource lock acquired for runner=%s key=%s',
|
|
287
287
|
platform.name,
|
|
288
|
-
resourceLockKey
|
|
288
|
+
resourceLockKey,
|
|
289
289
|
);
|
|
290
290
|
try {
|
|
291
291
|
const {
|
|
@@ -322,7 +322,6 @@ const getHarnessInternal = async (
|
|
|
322
322
|
let activeTestFilePath: string | undefined;
|
|
323
323
|
const pendingHookPromises = new Set<Promise<void>>();
|
|
324
324
|
let pendingHookError: unknown;
|
|
325
|
-
|
|
326
325
|
const getCurrentRunId = () => currentRun?.runId;
|
|
327
326
|
const toRelativeTestFilePath = (testFilePath?: string) =>
|
|
328
327
|
testFilePath == null
|
|
@@ -358,7 +357,7 @@ const getHarnessInternal = async (
|
|
|
358
357
|
object,
|
|
359
358
|
HarnessConfig,
|
|
360
359
|
HarnessPlatform
|
|
361
|
-
|
|
360
|
+
>,
|
|
362
361
|
>(
|
|
363
362
|
name: TName,
|
|
364
363
|
payload: Omit<
|
|
@@ -373,7 +372,7 @@ const getHarnessInternal = async (
|
|
|
373
372
|
| 'timestamp'
|
|
374
373
|
| 'abortSignal'
|
|
375
374
|
| 'meta'
|
|
376
|
-
|
|
375
|
+
>,
|
|
377
376
|
) => {
|
|
378
377
|
trackHook(pluginManager.callHook(name, payload));
|
|
379
378
|
};
|
|
@@ -384,11 +383,11 @@ const getHarnessInternal = async (
|
|
|
384
383
|
context,
|
|
385
384
|
});
|
|
386
385
|
harnessLogger.debug(
|
|
387
|
-
'starting Metro, platform runner, and bridge initialization'
|
|
386
|
+
'starting Metro, platform runner, and bridge initialization',
|
|
388
387
|
);
|
|
389
388
|
harnessLogger.debug(
|
|
390
389
|
'bridge server initialized on Metro websocket path %s',
|
|
391
|
-
HARNESS_BRIDGE_PATH
|
|
390
|
+
HARNESS_BRIDGE_PATH,
|
|
392
391
|
);
|
|
393
392
|
const [metroInstance, platformInstance] = await (async () => {
|
|
394
393
|
try {
|
|
@@ -402,7 +401,7 @@ const getHarnessInternal = async (
|
|
|
402
401
|
serverBridge.ws as unknown as MetroWebSocketEndpoint,
|
|
403
402
|
},
|
|
404
403
|
},
|
|
405
|
-
signal
|
|
404
|
+
signal,
|
|
406
405
|
).then((instance) => {
|
|
407
406
|
harnessLogger.debug('Metro initialized');
|
|
408
407
|
return instance;
|
|
@@ -415,7 +414,7 @@ const getHarnessInternal = async (
|
|
|
415
414
|
.then((module) =>
|
|
416
415
|
module.default(platform.config, runtimeConfig, {
|
|
417
416
|
signal,
|
|
418
|
-
} satisfies HarnessPlatformInitOptions)
|
|
417
|
+
} satisfies HarnessPlatformInitOptions),
|
|
419
418
|
)
|
|
420
419
|
.then((instance) => {
|
|
421
420
|
harnessLogger.debug('platform runner initialized');
|
|
@@ -650,7 +649,10 @@ const getHarnessInternal = async (
|
|
|
650
649
|
harnessLogger.debug('client log forwarding enabled');
|
|
651
650
|
}
|
|
652
651
|
|
|
653
|
-
|
|
652
|
+
let disposePromise: Promise<void> | null = null;
|
|
653
|
+
const disposeOnce = async (
|
|
654
|
+
reason: 'normal' | 'abort' | 'error' = 'normal'
|
|
655
|
+
) => {
|
|
654
656
|
harnessLogger.debug('disposing Harness (reason=%s)', reason);
|
|
655
657
|
let hookError: unknown;
|
|
656
658
|
|
|
@@ -709,6 +711,10 @@ const getHarnessInternal = async (
|
|
|
709
711
|
throw cleanupError;
|
|
710
712
|
}
|
|
711
713
|
};
|
|
714
|
+
const dispose = (reason: 'normal' | 'abort' | 'error' = 'normal') => {
|
|
715
|
+
disposePromise ??= disposeOnce(reason);
|
|
716
|
+
return disposePromise;
|
|
717
|
+
};
|
|
712
718
|
|
|
713
719
|
if (signal.aborted) {
|
|
714
720
|
await dispose('abort');
|
|
@@ -737,7 +743,7 @@ const getHarnessInternal = async (
|
|
|
737
743
|
await dispose(
|
|
738
744
|
error instanceof DOMException && error.name === 'AbortError'
|
|
739
745
|
? 'abort'
|
|
740
|
-
: 'error'
|
|
746
|
+
: 'error',
|
|
741
747
|
);
|
|
742
748
|
throw error;
|
|
743
749
|
}
|
|
@@ -758,7 +764,7 @@ const getHarnessInternal = async (
|
|
|
758
764
|
|
|
759
765
|
crashSupervisor.reset();
|
|
760
766
|
harnessLogger.debug(
|
|
761
|
-
'app not ready, waiting for launch and runtime readiness'
|
|
767
|
+
'app not ready, waiting for launch and runtime readiness',
|
|
762
768
|
);
|
|
763
769
|
await waitForAppReady({
|
|
764
770
|
metroInstance,
|
|
@@ -783,7 +789,7 @@ const getHarnessInternal = async (
|
|
|
783
789
|
harnessLogger.debug(
|
|
784
790
|
'restarting app (testFile=%s mode=%s)',
|
|
785
791
|
testFilePath ?? 'n/a',
|
|
786
|
-
testFilePath ? 'stop-and-ensure-ready' : 'direct-restart'
|
|
792
|
+
testFilePath ? 'stop-and-ensure-ready' : 'direct-restart',
|
|
787
793
|
);
|
|
788
794
|
|
|
789
795
|
if (testFilePath) {
|
|
@@ -848,17 +854,17 @@ const getHarnessInternal = async (
|
|
|
848
854
|
export const getHarness = async (
|
|
849
855
|
config: HarnessConfig,
|
|
850
856
|
platform: HarnessPlatform,
|
|
851
|
-
projectRoot: string
|
|
857
|
+
projectRoot: string,
|
|
852
858
|
): Promise<Harness> => {
|
|
853
859
|
harnessLogger.debug(
|
|
854
860
|
'creating Harness with platform ready timeout %dms',
|
|
855
|
-
config.platformReadyTimeout
|
|
861
|
+
config.platformReadyTimeout,
|
|
856
862
|
);
|
|
857
863
|
|
|
858
864
|
return await getHarnessInternal(
|
|
859
865
|
config,
|
|
860
866
|
platform,
|
|
861
867
|
projectRoot,
|
|
862
|
-
new AbortController().signal
|
|
868
|
+
new AbortController().signal,
|
|
863
869
|
);
|
|
864
870
|
};
|