@react-native-harness/platform-android 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.
- package/dist/__tests__/adb.test.js +77 -5
- package/dist/__tests__/environment.test.js +67 -1
- package/dist/__tests__/instance.test.js +1 -1
- package/dist/__tests__/targets.test.d.ts +2 -0
- package/dist/__tests__/targets.test.d.ts.map +1 -0
- package/dist/__tests__/targets.test.js +49 -0
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +47 -13
- package/dist/config.d.ts +16 -16
- package/dist/environment.d.ts +3 -0
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +33 -11
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +2 -4
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +2 -5
- package/dist/targets.d.ts.map +1 -1
- package/dist/targets.js +4 -2
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/__tests__/adb.test.ts +133 -28
- package/src/__tests__/environment.test.ts +135 -10
- package/src/__tests__/instance.test.ts +49 -49
- package/src/__tests__/targets.test.ts +53 -0
- package/src/adb.ts +106 -48
- package/src/environment.ts +99 -55
- package/src/instance.ts +17 -17
- package/src/runner.ts +5 -10
- package/src/targets.ts +9 -2
- package/dist/assertions.d.ts +0 -5
- package/dist/assertions.d.ts.map +0 -1
- package/dist/assertions.js +0 -6
- package/dist/emulator.d.ts +0 -6
- package/dist/emulator.d.ts.map +0 -1
- package/dist/emulator.js +0 -27
- package/dist/reader.d.ts +0 -6
- package/dist/reader.d.ts.map +0 -1
- package/dist/reader.js +0 -57
- package/dist/types.d.ts +0 -381
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -107
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '../adb.js';
|
|
18
18
|
import * as tools from '@react-native-harness/tools';
|
|
19
19
|
import * as environment from '../environment.js';
|
|
20
|
+
import * as avdConfig from '../avd-config.js';
|
|
20
21
|
|
|
21
22
|
const createAbortError = () =>
|
|
22
23
|
new DOMException('The operation was aborted', 'AbortError');
|
|
@@ -48,7 +49,7 @@ describe('getStartAppArgs', () => {
|
|
|
48
49
|
user_id: 42,
|
|
49
50
|
mode: 'debug',
|
|
50
51
|
},
|
|
51
|
-
})
|
|
52
|
+
}),
|
|
52
53
|
).toEqual([
|
|
53
54
|
'shell',
|
|
54
55
|
'am',
|
|
@@ -77,7 +78,7 @@ describe('getStartAppArgs', () => {
|
|
|
77
78
|
extras: {
|
|
78
79
|
count: Number.MAX_SAFE_INTEGER + 1,
|
|
79
80
|
},
|
|
80
|
-
})
|
|
81
|
+
}),
|
|
81
82
|
).toThrow('must be a safe integer');
|
|
82
83
|
});
|
|
83
84
|
|
|
@@ -88,7 +89,7 @@ describe('getStartAppArgs', () => {
|
|
|
88
89
|
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
89
90
|
|
|
90
91
|
await expect(getAppUid('emulator-5554', 'com.example.app')).resolves.toBe(
|
|
91
|
-
10234
|
|
92
|
+
10234,
|
|
92
93
|
);
|
|
93
94
|
|
|
94
95
|
expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
|
|
@@ -108,7 +109,7 @@ describe('getStartAppArgs', () => {
|
|
|
108
109
|
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
109
110
|
|
|
110
111
|
await expect(getLogcatTimestamp('emulator-5554')).resolves.toBe(
|
|
111
|
-
'03-12 11:35:08.000'
|
|
112
|
+
'03-12 11:35:08.000',
|
|
112
113
|
);
|
|
113
114
|
|
|
114
115
|
expect(spawnSpy).toHaveBeenCalledWith(expect.stringMatching(/adb$/), [
|
|
@@ -146,14 +147,19 @@ describe('getStartAppArgs', () => {
|
|
|
146
147
|
});
|
|
147
148
|
|
|
148
149
|
it('creates an AVD and appends config overrides', async () => {
|
|
150
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
149
151
|
const spawnSpy = vi
|
|
150
152
|
.spyOn(tools, 'spawn')
|
|
151
|
-
.mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
|
|
153
|
+
.mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
|
|
154
|
+
.mockResolvedValueOnce({
|
|
155
|
+
stdout:
|
|
156
|
+
'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
|
|
157
|
+
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
152
158
|
const verifyAndroidEmulatorSdk = vi
|
|
153
159
|
.spyOn(environment, 'ensureAndroidSdkPackages')
|
|
154
160
|
.mockResolvedValue('/tmp/android-sdk');
|
|
155
161
|
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
|
|
156
|
-
'x86_64'
|
|
162
|
+
'x86_64',
|
|
157
163
|
);
|
|
158
164
|
|
|
159
165
|
await createAvd({
|
|
@@ -170,29 +176,128 @@ describe('getStartAppArgs', () => {
|
|
|
170
176
|
'platforms;android-35',
|
|
171
177
|
'system-images;android-35;default;x86_64',
|
|
172
178
|
]);
|
|
173
|
-
expect(spawnSpy).toHaveBeenNthCalledWith(
|
|
179
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(
|
|
180
|
+
1,
|
|
181
|
+
expect.stringMatching(/avdmanager$/),
|
|
182
|
+
['list', 'device'],
|
|
183
|
+
);
|
|
184
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(2, 'bash', [
|
|
174
185
|
'-lc',
|
|
175
186
|
expect.stringContaining(
|
|
176
|
-
'create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8"'
|
|
187
|
+
'create avd --force --name "Pixel_8_API_35" --package "system-images;android-35;default;x86_64" --device "pixel_8" -p ',
|
|
177
188
|
),
|
|
178
189
|
]);
|
|
179
|
-
expect(spawnSpy).toHaveBeenNthCalledWith(
|
|
190
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
|
|
191
|
+
'-lc',
|
|
192
|
+
expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
|
|
193
|
+
]);
|
|
194
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
|
|
180
195
|
'-lc',
|
|
181
196
|
expect.stringContaining(
|
|
182
|
-
`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >>
|
|
197
|
+
`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `,
|
|
183
198
|
),
|
|
184
199
|
]);
|
|
185
200
|
});
|
|
186
201
|
|
|
187
|
-
it('
|
|
188
|
-
vi.spyOn(
|
|
189
|
-
|
|
202
|
+
it('recreates the companion ini file when avdmanager leaves it missing', async () => {
|
|
203
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
204
|
+
const spawnSpy = vi
|
|
205
|
+
.spyOn(tools, 'spawn')
|
|
206
|
+
.mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
|
|
207
|
+
.mockResolvedValueOnce({
|
|
208
|
+
stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
|
|
209
|
+
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
210
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
|
|
211
|
+
'/tmp/android-sdk',
|
|
212
|
+
);
|
|
213
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
|
|
214
|
+
'x86_64',
|
|
190
215
|
);
|
|
216
|
+
|
|
217
|
+
await createAvd({
|
|
218
|
+
name: 'Pixel_6_API_35',
|
|
219
|
+
apiLevel: 35,
|
|
220
|
+
profile: 'pixel_6',
|
|
221
|
+
diskSize: '1G',
|
|
222
|
+
heapSize: '1G',
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(3, 'bash', [
|
|
226
|
+
'-lc',
|
|
227
|
+
expect.stringContaining(`'avd.ini.encoding=UTF-8' 'path=`),
|
|
228
|
+
]);
|
|
229
|
+
expect(spawnSpy).toHaveBeenNthCalledWith(4, 'bash', [
|
|
230
|
+
'-lc',
|
|
231
|
+
expect.stringContaining(
|
|
232
|
+
`'disk.dataPartition.size=1G' 'vm.heapSize=1G' >> `,
|
|
233
|
+
),
|
|
234
|
+
]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('throws an actionable error when the requested AVD profile is unavailable', async () => {
|
|
238
|
+
vi.spyOn(tools, 'spawn').mockResolvedValueOnce({
|
|
239
|
+
stdout:
|
|
240
|
+
'id: 0 or "pixel_8"\n Name: Pixel 8\nid: 1 or "pixel_6"\n Name: Pixel 6\n',
|
|
241
|
+
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
242
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
|
|
243
|
+
'/tmp/android-sdk',
|
|
244
|
+
);
|
|
245
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
|
|
246
|
+
'x86_64',
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
await expect(
|
|
250
|
+
createAvd({
|
|
251
|
+
name: 'Pixel_8_Pro_API_35',
|
|
252
|
+
apiLevel: 35,
|
|
253
|
+
profile: 'pixel_8_pro',
|
|
254
|
+
diskSize: '1G',
|
|
255
|
+
heapSize: '1G',
|
|
256
|
+
}),
|
|
257
|
+
).rejects.toThrow(
|
|
258
|
+
'Android AVD profile "pixel_8_pro" is not available on this machine. Available profiles: pixel_8, pixel_6',
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('throws a clear error when avdmanager does not create config.ini', async () => {
|
|
263
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue(null);
|
|
264
|
+
vi.spyOn(tools, 'spawn')
|
|
265
|
+
.mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
|
|
266
|
+
.mockResolvedValueOnce({
|
|
267
|
+
stdout: 'id: 0 or "pixel_6"\n Name: Pixel 6\n',
|
|
268
|
+
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
269
|
+
vi.spyOn(environment, 'ensureAndroidSdkPackages').mockResolvedValue(
|
|
270
|
+
'/tmp/android-sdk',
|
|
271
|
+
);
|
|
272
|
+
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
|
|
273
|
+
'x86_64',
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
await expect(
|
|
277
|
+
createAvd({
|
|
278
|
+
name: 'Pixel_6_API_35',
|
|
279
|
+
apiLevel: 35,
|
|
280
|
+
profile: 'pixel_6',
|
|
281
|
+
diskSize: '1G',
|
|
282
|
+
heapSize: '1G',
|
|
283
|
+
}),
|
|
284
|
+
).rejects.toThrow(
|
|
285
|
+
'Android AVD "Pixel_6_API_35" was created, but config.ini was not found at ',
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('creates an AVD with arm64 system image packages on arm64 hosts', async () => {
|
|
290
|
+
vi.spyOn(avdConfig, 'readAvdConfig').mockResolvedValue({});
|
|
291
|
+
vi.spyOn(tools, 'spawn')
|
|
292
|
+
.mockResolvedValue({} as Awaited<ReturnType<typeof tools.spawn>>)
|
|
293
|
+
.mockResolvedValueOnce({
|
|
294
|
+
stdout: 'id: 0 or "pixel_8"\n Name: Pixel 8\n',
|
|
295
|
+
} as Awaited<ReturnType<typeof tools.spawn>>);
|
|
191
296
|
const ensureAndroidSdkPackages = vi
|
|
192
297
|
.spyOn(environment, 'ensureAndroidSdkPackages')
|
|
193
298
|
.mockResolvedValue('/tmp/android-sdk');
|
|
194
299
|
vi.spyOn(environment, 'getHostAndroidSystemImageArch').mockReturnValue(
|
|
195
|
-
'arm64-v8a'
|
|
300
|
+
'arm64-v8a',
|
|
196
301
|
);
|
|
197
302
|
|
|
198
303
|
await createAvd({
|
|
@@ -241,7 +346,7 @@ describe('getStartAppArgs', () => {
|
|
|
241
346
|
child.emit('close', 1, null);
|
|
242
347
|
|
|
243
348
|
await expect(startPromise).rejects.toThrow(
|
|
244
|
-
'Unknown AVD name [Pixel_8_API_35]'
|
|
349
|
+
'Unknown AVD name [Pixel_8_API_35]',
|
|
245
350
|
);
|
|
246
351
|
});
|
|
247
352
|
|
|
@@ -271,7 +376,7 @@ describe('getStartAppArgs', () => {
|
|
|
271
376
|
child.emit('close', 1, null);
|
|
272
377
|
|
|
273
378
|
await expect(startPromise).rejects.toThrow(
|
|
274
|
-
'emulator: panic: broken config'
|
|
379
|
+
'emulator: panic: broken config',
|
|
275
380
|
);
|
|
276
381
|
});
|
|
277
382
|
|
|
@@ -291,7 +396,7 @@ describe('getStartAppArgs', () => {
|
|
|
291
396
|
vi.spyOn(emulatorProcess, 'startDetachedProcess').mockReturnValue(
|
|
292
397
|
child as unknown as ReturnType<
|
|
293
398
|
typeof emulatorProcess.startDetachedProcess
|
|
294
|
-
|
|
399
|
+
>,
|
|
295
400
|
);
|
|
296
401
|
|
|
297
402
|
const startPromise = startEmulator('Pixel_8_API_35');
|
|
@@ -317,7 +422,7 @@ describe('getStartAppArgs', () => {
|
|
|
317
422
|
.mockReturnValue(
|
|
318
423
|
child as unknown as ReturnType<
|
|
319
424
|
typeof emulatorProcess.startDetachedProcess
|
|
320
|
-
|
|
425
|
+
>,
|
|
321
426
|
);
|
|
322
427
|
|
|
323
428
|
const startPromise = startEmulator('Pixel_8_API_35');
|
|
@@ -326,7 +431,7 @@ describe('getStartAppArgs', () => {
|
|
|
326
431
|
|
|
327
432
|
expect(startDetachedProcess).toHaveBeenCalledWith(
|
|
328
433
|
expect.stringMatching(/emulator$/),
|
|
329
|
-
expect.arrayContaining(['-no-snapshot-load', '-no-snapshot-save'])
|
|
434
|
+
expect.arrayContaining(['-no-snapshot-load', '-no-snapshot-save']),
|
|
330
435
|
);
|
|
331
436
|
});
|
|
332
437
|
|
|
@@ -345,22 +450,22 @@ describe('getStartAppArgs', () => {
|
|
|
345
450
|
.mockReturnValue(
|
|
346
451
|
child as unknown as ReturnType<
|
|
347
452
|
typeof emulatorProcess.startDetachedProcess
|
|
348
|
-
|
|
453
|
+
>,
|
|
349
454
|
);
|
|
350
455
|
|
|
351
456
|
const startPromise = startEmulator(
|
|
352
457
|
'Pixel_8_API_35',
|
|
353
|
-
'clean-snapshot-generation'
|
|
458
|
+
'clean-snapshot-generation',
|
|
354
459
|
);
|
|
355
460
|
await vi.runAllTimersAsync();
|
|
356
461
|
await startPromise;
|
|
357
462
|
|
|
358
463
|
expect(startDetachedProcess).toHaveBeenCalledWith(
|
|
359
464
|
expect.stringMatching(/emulator$/),
|
|
360
|
-
expect.arrayContaining(['-no-snapshot-load'])
|
|
465
|
+
expect.arrayContaining(['-no-snapshot-load']),
|
|
361
466
|
);
|
|
362
467
|
expect(startDetachedProcess.mock.calls[0]?.[1]).not.toContain(
|
|
363
|
-
'-no-snapshot-save'
|
|
468
|
+
'-no-snapshot-save',
|
|
364
469
|
);
|
|
365
470
|
});
|
|
366
471
|
|
|
@@ -379,7 +484,7 @@ describe('getStartAppArgs', () => {
|
|
|
379
484
|
.mockReturnValue(
|
|
380
485
|
child as unknown as ReturnType<
|
|
381
486
|
typeof emulatorProcess.startDetachedProcess
|
|
382
|
-
|
|
487
|
+
>,
|
|
383
488
|
);
|
|
384
489
|
|
|
385
490
|
const startPromise = startEmulator('Pixel_8_API_35', 'snapshot-reuse');
|
|
@@ -388,10 +493,10 @@ describe('getStartAppArgs', () => {
|
|
|
388
493
|
|
|
389
494
|
expect(startDetachedProcess).toHaveBeenCalledWith(
|
|
390
495
|
expect.stringMatching(/emulator$/),
|
|
391
|
-
expect.arrayContaining(['-no-snapshot-save'])
|
|
496
|
+
expect.arrayContaining(['-no-snapshot-save']),
|
|
392
497
|
);
|
|
393
498
|
expect(startDetachedProcess.mock.calls[0]?.[1]).not.toContain(
|
|
394
|
-
'-no-snapshot-load'
|
|
499
|
+
'-no-snapshot-load',
|
|
395
500
|
);
|
|
396
501
|
});
|
|
397
502
|
|
|
@@ -459,7 +564,7 @@ describe('getStartAppArgs', () => {
|
|
|
459
564
|
|
|
460
565
|
const waitPromise = waitForBoot(
|
|
461
566
|
'Pixel_8_API_35',
|
|
462
|
-
new AbortController().signal
|
|
567
|
+
new AbortController().signal,
|
|
463
568
|
);
|
|
464
569
|
|
|
465
570
|
await vi.advanceTimersByTimeAsync(1000);
|
|
@@ -482,7 +587,7 @@ describe('getStartAppArgs', () => {
|
|
|
482
587
|
|
|
483
588
|
const waitPromise = waitForEmulatorDisconnect(
|
|
484
589
|
'emulator-5554',
|
|
485
|
-
new AbortController().signal
|
|
590
|
+
new AbortController().signal,
|
|
486
591
|
);
|
|
487
592
|
|
|
488
593
|
await vi.advanceTimersByTimeAsync(1000);
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
2
5
|
import {
|
|
6
|
+
ensureAndroidAdbAvailable,
|
|
7
|
+
ensureAndroidEmulatorAvailable,
|
|
3
8
|
getAndroidSdkRoot,
|
|
4
9
|
getAndroidSystemImagePackage,
|
|
5
10
|
getDefaultUnixAndroidSdkRoot,
|
|
6
11
|
getHostAndroidSystemImageArch,
|
|
7
12
|
getRequiredAndroidSdkPackages,
|
|
8
13
|
} from '../environment.js';
|
|
14
|
+
import * as tools from '@react-native-harness/tools';
|
|
9
15
|
|
|
10
16
|
describe('Android environment', () => {
|
|
11
17
|
beforeEach(() => {
|
|
@@ -13,12 +19,131 @@ describe('Android environment', () => {
|
|
|
13
19
|
vi.unstubAllEnvs();
|
|
14
20
|
});
|
|
15
21
|
|
|
22
|
+
it('skips bootstrapping command-line tools when adb is already installed', async () => {
|
|
23
|
+
const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
|
|
24
|
+
const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
|
|
25
|
+
|
|
26
|
+
fs.mkdirSync(path.dirname(adbPath), { recursive: true });
|
|
27
|
+
fs.writeFileSync(adbPath, '');
|
|
28
|
+
|
|
29
|
+
const spawnSpy = vi.spyOn(tools, 'spawn');
|
|
30
|
+
|
|
31
|
+
await expect(
|
|
32
|
+
ensureAndroidAdbAvailable({
|
|
33
|
+
env: { ANDROID_HOME: sdkRoot },
|
|
34
|
+
}),
|
|
35
|
+
).resolves.toBe(sdkRoot);
|
|
36
|
+
|
|
37
|
+
expect(spawnSpy).not.toHaveBeenCalled();
|
|
38
|
+
|
|
39
|
+
fs.rmSync(sdkRoot, { force: true, recursive: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('installs only platform-tools when adb is missing', async () => {
|
|
43
|
+
const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
|
|
44
|
+
const sdkManagerDirectory = path.join(
|
|
45
|
+
sdkRoot,
|
|
46
|
+
'cmdline-tools',
|
|
47
|
+
'latest',
|
|
48
|
+
'bin',
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
fs.mkdirSync(sdkManagerDirectory, { recursive: true });
|
|
52
|
+
fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
|
|
53
|
+
fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
|
|
54
|
+
|
|
55
|
+
const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (
|
|
56
|
+
command: string,
|
|
57
|
+
args?: readonly string[],
|
|
58
|
+
) => {
|
|
59
|
+
if (command === 'bash' && typeof args?.[1] === 'string') {
|
|
60
|
+
const commandString = args[1];
|
|
61
|
+
|
|
62
|
+
if (commandString.includes('platform-tools')) {
|
|
63
|
+
const adbPath = path.join(sdkRoot, 'platform-tools', 'adb');
|
|
64
|
+
fs.mkdirSync(path.dirname(adbPath), { recursive: true });
|
|
65
|
+
fs.writeFileSync(adbPath, '');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {} as Awaited<ReturnType<typeof tools.spawn>>;
|
|
70
|
+
}) as typeof tools.spawn);
|
|
71
|
+
|
|
72
|
+
await expect(
|
|
73
|
+
ensureAndroidAdbAvailable({
|
|
74
|
+
env: { ANDROID_HOME: sdkRoot },
|
|
75
|
+
}),
|
|
76
|
+
).resolves.toBe(sdkRoot);
|
|
77
|
+
|
|
78
|
+
expect(spawnSpy).toHaveBeenCalledWith(
|
|
79
|
+
'bash',
|
|
80
|
+
['-lc', expect.stringContaining('platform-tools')],
|
|
81
|
+
expect.any(Object),
|
|
82
|
+
);
|
|
83
|
+
expect(
|
|
84
|
+
spawnSpy.mock.calls.some(
|
|
85
|
+
([command, args]) =>
|
|
86
|
+
command === 'bash' &&
|
|
87
|
+
typeof args?.[1] === 'string' &&
|
|
88
|
+
args[1].includes('platform-tools') &&
|
|
89
|
+
args[1].includes('emulator'),
|
|
90
|
+
),
|
|
91
|
+
).toBe(false);
|
|
92
|
+
|
|
93
|
+
fs.rmSync(sdkRoot, { force: true, recursive: true });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('installs emulator only when emulator is missing', async () => {
|
|
97
|
+
const sdkRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'android-sdk-'));
|
|
98
|
+
const sdkManagerDirectory = path.join(
|
|
99
|
+
sdkRoot,
|
|
100
|
+
'cmdline-tools',
|
|
101
|
+
'latest',
|
|
102
|
+
'bin',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
fs.mkdirSync(sdkManagerDirectory, { recursive: true });
|
|
106
|
+
fs.writeFileSync(path.join(sdkManagerDirectory, 'sdkmanager'), '');
|
|
107
|
+
fs.writeFileSync(path.join(sdkManagerDirectory, 'avdmanager'), '');
|
|
108
|
+
|
|
109
|
+
const spawnSpy = vi.spyOn(tools, 'spawn').mockImplementation((async (
|
|
110
|
+
command: string,
|
|
111
|
+
args?: readonly string[],
|
|
112
|
+
) => {
|
|
113
|
+
if (command === 'bash' && typeof args?.[1] === 'string') {
|
|
114
|
+
const commandString = args[1];
|
|
115
|
+
|
|
116
|
+
if (commandString.includes('emulator')) {
|
|
117
|
+
const emulatorPath = path.join(sdkRoot, 'emulator', 'emulator');
|
|
118
|
+
fs.mkdirSync(path.dirname(emulatorPath), { recursive: true });
|
|
119
|
+
fs.writeFileSync(emulatorPath, '');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {} as Awaited<ReturnType<typeof tools.spawn>>;
|
|
124
|
+
}) as typeof tools.spawn);
|
|
125
|
+
|
|
126
|
+
await expect(
|
|
127
|
+
ensureAndroidEmulatorAvailable({
|
|
128
|
+
env: { ANDROID_HOME: sdkRoot },
|
|
129
|
+
}),
|
|
130
|
+
).resolves.toBe(sdkRoot);
|
|
131
|
+
|
|
132
|
+
expect(spawnSpy).toHaveBeenCalledWith(
|
|
133
|
+
'bash',
|
|
134
|
+
['-lc', expect.stringContaining('emulator')],
|
|
135
|
+
expect.any(Object),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
fs.rmSync(sdkRoot, { force: true, recursive: true });
|
|
139
|
+
});
|
|
140
|
+
|
|
16
141
|
it('uses the default Unix SDK root when env vars are missing', () => {
|
|
17
142
|
expect(
|
|
18
143
|
getDefaultUnixAndroidSdkRoot({
|
|
19
144
|
platform: 'darwin',
|
|
20
145
|
homeDirectory: '/Users/tester',
|
|
21
|
-
})
|
|
146
|
+
}),
|
|
22
147
|
).toBe('/Users/tester/Library/Android/sdk');
|
|
23
148
|
|
|
24
149
|
expect(
|
|
@@ -27,8 +152,8 @@ describe('Android environment', () => {
|
|
|
27
152
|
{
|
|
28
153
|
platform: 'linux',
|
|
29
154
|
homeDirectory: '/home/tester',
|
|
30
|
-
}
|
|
31
|
-
)
|
|
155
|
+
},
|
|
156
|
+
),
|
|
32
157
|
).toBe('/home/tester/Android/Sdk');
|
|
33
158
|
});
|
|
34
159
|
|
|
@@ -42,8 +167,8 @@ describe('Android environment', () => {
|
|
|
42
167
|
{
|
|
43
168
|
platform: 'darwin',
|
|
44
169
|
homeDirectory: '/Users/tester',
|
|
45
|
-
}
|
|
46
|
-
)
|
|
170
|
+
},
|
|
171
|
+
),
|
|
47
172
|
).toBe('/env/android-home');
|
|
48
173
|
|
|
49
174
|
expect(
|
|
@@ -54,8 +179,8 @@ describe('Android environment', () => {
|
|
|
54
179
|
{
|
|
55
180
|
platform: 'linux',
|
|
56
181
|
homeDirectory: '/home/tester',
|
|
57
|
-
}
|
|
58
|
-
)
|
|
182
|
+
},
|
|
183
|
+
),
|
|
59
184
|
).toBe('/env/android-sdk-root');
|
|
60
185
|
});
|
|
61
186
|
|
|
@@ -63,10 +188,10 @@ describe('Android environment', () => {
|
|
|
63
188
|
expect(getHostAndroidSystemImageArch('x64')).toBe('x86_64');
|
|
64
189
|
expect(getHostAndroidSystemImageArch('arm64')).toBe('arm64-v8a');
|
|
65
190
|
expect(getAndroidSystemImagePackage(35, 'x86_64')).toBe(
|
|
66
|
-
'system-images;android-35;default;x86_64'
|
|
191
|
+
'system-images;android-35;default;x86_64',
|
|
67
192
|
);
|
|
68
193
|
expect(getAndroidSystemImagePackage(35, 'arm64-v8a')).toBe(
|
|
69
|
-
'system-images;android-35;default;arm64-v8a'
|
|
194
|
+
'system-images;android-35;default;arm64-v8a',
|
|
70
195
|
);
|
|
71
196
|
});
|
|
72
197
|
|
|
@@ -76,7 +201,7 @@ describe('Android environment', () => {
|
|
|
76
201
|
apiLevel: 34,
|
|
77
202
|
includeEmulator: true,
|
|
78
203
|
architecture: 'x86_64',
|
|
79
|
-
})
|
|
204
|
+
}),
|
|
80
205
|
).toEqual([
|
|
81
206
|
'platform-tools',
|
|
82
207
|
'emulator',
|