|
1 | 1 | import { beforeEach, test, vi } from 'vitest'; |
2 | 2 | import assert from 'node:assert/strict'; |
3 | 3 |
|
| 4 | +const { mockFindBootableIosSimulator, mockListAppleDevices } = vi.hoisted(() => ({ |
| 5 | + mockFindBootableIosSimulator: vi.fn(), |
| 6 | + mockListAppleDevices: vi.fn(), |
| 7 | +})); |
| 8 | + |
4 | 9 | vi.mock('../../platforms/ios/devices.ts', async (importOriginal) => { |
5 | 10 | const actual = await importOriginal<typeof import('../../platforms/ios/devices.ts')>(); |
6 | | - return { ...actual, findBootableIosSimulator: vi.fn() }; |
| 11 | + return { |
| 12 | + ...actual, |
| 13 | + findBootableIosSimulator: mockFindBootableIosSimulator, |
| 14 | + listAppleDevices: mockListAppleDevices, |
| 15 | + }; |
7 | 16 | }); |
8 | 17 |
|
9 | | -import { resolveIosDevice } from '../dispatch-resolve.ts'; |
| 18 | +import { |
| 19 | + resolveIosDevice, |
| 20 | + resolveTargetDevice, |
| 21 | + withResolveTargetDeviceCacheScope, |
| 22 | +} from '../dispatch-resolve.ts'; |
10 | 23 | import type { DeviceInfo } from '../../utils/device.ts'; |
11 | 24 | import { AppError } from '../../utils/errors.ts'; |
12 | | -import { findBootableIosSimulator } from '../../platforms/ios/devices.ts'; |
13 | 25 |
|
14 | 26 | const physical: DeviceInfo = { |
15 | 27 | platform: 'ios', |
@@ -38,11 +50,10 @@ const bootedSimulator: DeviceInfo = { |
38 | 50 | booted: true, |
39 | 51 | }; |
40 | 52 |
|
41 | | -const mockFindBootableIosSimulator = vi.mocked(findBootableIosSimulator); |
42 | | - |
43 | 53 | beforeEach(() => { |
44 | 54 | mockFindBootableIosSimulator.mockReset(); |
45 | 55 | mockFindBootableIosSimulator.mockResolvedValue(null); |
| 56 | + mockListAppleDevices.mockReset(); |
46 | 57 | }); |
47 | 58 |
|
48 | 59 | // --- Physical device rejected in favour of simulator fallback --- |
@@ -112,3 +123,53 @@ test('resolveIosDevice returns simulator directly when present in device list', |
112 | 123 | assert.equal(result.kind, 'simulator'); |
113 | 124 | assert.equal(mockFindBootableIosSimulator.mock.calls.length, 0); |
114 | 125 | }); |
| 126 | + |
| 127 | +test('resolveTargetDevice reuses request-scoped device resolution cache for identical selectors', async () => { |
| 128 | + mockListAppleDevices.mockResolvedValue([bootedSimulator]); |
| 129 | + |
| 130 | + const [first, second] = await withResolveTargetDeviceCacheScope(async () => [ |
| 131 | + await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }), |
| 132 | + await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }), |
| 133 | + ]); |
| 134 | + |
| 135 | + assert.equal(first.id, 'sim-2'); |
| 136 | + assert.equal(second.id, 'sim-2'); |
| 137 | + assert.equal(mockListAppleDevices.mock.calls.length, 1); |
| 138 | +}); |
| 139 | + |
| 140 | +test('resolveTargetDevice request cache key separates device selectors', async () => { |
| 141 | + mockListAppleDevices.mockResolvedValue([simulator, bootedSimulator]); |
| 142 | + |
| 143 | + await withResolveTargetDeviceCacheScope(async () => { |
| 144 | + await resolveTargetDevice({ platform: 'ios', device: 'iPhone 16' }); |
| 145 | + await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }); |
| 146 | + }); |
| 147 | + |
| 148 | + assert.equal(mockListAppleDevices.mock.calls.length, 2); |
| 149 | +}); |
| 150 | + |
| 151 | +test('resolveTargetDevice does not reuse cache across request scopes', async () => { |
| 152 | + mockListAppleDevices.mockResolvedValue([bootedSimulator]); |
| 153 | + |
| 154 | + await withResolveTargetDeviceCacheScope( |
| 155 | + async () => await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }), |
| 156 | + ); |
| 157 | + await withResolveTargetDeviceCacheScope( |
| 158 | + async () => await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }), |
| 159 | + ); |
| 160 | + |
| 161 | + assert.equal(mockListAppleDevices.mock.calls.length, 2); |
| 162 | +}); |
| 163 | + |
| 164 | +test('resolveTargetDevice reuses cache across nested request scopes', async () => { |
| 165 | + mockListAppleDevices.mockResolvedValue([bootedSimulator]); |
| 166 | + |
| 167 | + await withResolveTargetDeviceCacheScope(async () => { |
| 168 | + await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }); |
| 169 | + await withResolveTargetDeviceCacheScope( |
| 170 | + async () => await resolveTargetDevice({ platform: 'ios', device: 'iPhone 15' }), |
| 171 | + ); |
| 172 | + }); |
| 173 | + |
| 174 | + assert.equal(mockListAppleDevices.mock.calls.length, 1); |
| 175 | +}); |
0 commit comments