microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7b48f26eb8caae7ed9cc1a09249195dfed76fa76

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

test/extension/android/androidPlatform.test.ts

604lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as fs from "fs";
5import * as path from "path";
6import assert = require("assert");
7import { AndroidPlatform } from "../../../src/extension/android/androidPlatform";
8import { IAndroidRunOptions, PlatformType } from "../../../src/extension/launchArgs";
9import { FileSystem } from "../../../src/common/node/fileSystem";
10import { ReactNative065 } from "../../resources/reactNative065";
11import * as adb from "../../../src/extension/android/adb";
12import { RecordingsHelper } from "../../resources/recordingsHelper";
13import { CommandExecutor } from "../../../src/common/commandExecutor";
14import { ProjectVersionHelper } from "../../../src/common/projectVersionHelper";
15import "should";
16import * as sinon from "sinon";
17import { SettingsHelper } from "../../../src/extension/settingsHelper";
18import { rimrafAsync } from "../../common/utils";
19
20suite("androidPlatform", function () {
21 suite("extensionContext", function () {
22 const projectRoot = path.join(
23 __dirname,
24 "..",
25 "..",
26 "resources",
27 "projects",
28 "SampleApplication_21",
29 );
30 const projectsFolder = path.join(projectRoot, "..");
31 const androidProjectPath = path.join(projectRoot, "android");
32 const applicationName = "SampleApplication";
33 const androidPackageName = "com.sampleapplication";
34
35 const nodeModulesRoot: string = projectRoot;
36 const genericRunOptions: IAndroidRunOptions = {
37 platform: PlatformType.Android,
38 workspaceRoot: projectRoot,
39 projectRoot,
40 reactNativeVersions: {
41 reactNativeVersion: "^0.19.0",
42 reactNativeWindowsVersion: "",
43 reactNativeMacOSVersion: "",
44 },
45 nodeModulesRoot,
46 };
47
48 const rnProjectContent = fs.readFileSync(ReactNative065.DEFAULT_PROJECT_FILE, "utf8");
49
50 let fileSystem: FileSystem;
51 let reactNative: ReactNative065;
52 let androidPlatform: AndroidPlatform;
53
54 let launchAppStub: Sinon.SinonStub;
55 let getConnectedTargetsStub: Sinon.SinonStub;
56 let getOnlineTargetsStub: Sinon.SinonStub;
57 let apiVersionStub: Sinon.SinonStub;
58 let reverseAdbStub: Sinon.SinonStub;
59 let getReactNativeProjectRootStub: Sinon.SinonStub;
60 let spawnReactCommandStub: Sinon.SinonStub;
61 let getReactNativeVersionsStub: Sinon.SinonStub;
62 let installAppInDeviceStub: Sinon.SinonStub;
63
64 let devices: any;
65 let adbHelper: adb.AdbHelper;
66
67 function createAndroidPlatform(runOptions: IAndroidRunOptions): AndroidPlatform {
68 return new AndroidPlatform(runOptions);
69 }
70
71 setup(async () => {
72 // Configure all the dependencies we'll use in our tests
73 fileSystem = new FileSystem();
74
75 adbHelper = new adb.AdbHelper(genericRunOptions.projectRoot, nodeModulesRoot);
76 launchAppStub = sinon.stub(
77 adbHelper,
78 "launchApp",
79 async (projectRoot_: string, packageName: string, debugTarget?: string) => {
80 devices = devices.map((device: any) => {
81 if (!debugTarget) {
82 device.installedApplications[androidPackageName] = {
83 isInDebugMode: false,
84 };
85 }
86
87 if (debugTarget && debugTarget === device.id) {
88 device.installedApplications[androidPackageName] = {
89 isInDebugMode: false,
90 };
91 }
92
93 return device;
94 });
95 },
96 );
97 getConnectedTargetsStub = sinon.stub(
98 adbHelper,
99 "getConnectedTargets",
100 async function () {
101 return devices;
102 },
103 );
104 getOnlineTargetsStub = sinon.stub(adbHelper, "getOnlineTargets", async function () {
105 return devices.filter((device: any) => {
106 return device.isOnline;
107 });
108 });
109 apiVersionStub = sinon.stub(adbHelper, "apiVersion", async function () {
110 return adb.AndroidAPILevel.LOLLIPOP;
111 });
112 reverseAdbStub = sinon.stub(adbHelper, "reverseAdb", async function () {
113 return;
114 });
115
116 reactNative = new ReactNative065(fileSystem, adbHelper);
117
118 getReactNativeProjectRootStub = sinon.stub(
119 SettingsHelper,
120 "getReactNativeProjectRoot",
121 () => projectRoot,
122 );
123
124 androidPlatform = createAndroidPlatform(genericRunOptions);
125
126 spawnReactCommandStub = sinon.stub(
127 CommandExecutor.prototype,
128 "spawnReactCommand",
129 function () {
130 return reactNative.runAndroid(genericRunOptions);
131 },
132 );
133
134 getReactNativeVersionsStub = sinon.stub(
135 ProjectVersionHelper,
136 "getReactNativeVersions",
137 async function () {
138 return {
139 reactNativeVersion: "0.0.1",
140 reactNativeWindowsVersion: "",
141 };
142 },
143 );
144
145 (androidPlatform as any).adbHelper = adbHelper;
146
147 installAppInDeviceStub = sinon.stub(
148 reactNative,
149 "installAppInDevice",
150 async function (deviceId: string) {
151 devices = devices.map((device: any) => {
152 if (deviceId && deviceId === device.id) {
153 device.installedApplications[androidPackageName] = {};
154 }
155
156 return device;
157 });
158 },
159 );
160
161 // Delete existing React Native project before creating
162 await rimrafAsync(projectsFolder, {});
163 // Create a React-Native project we'll use in our tests
164 await reactNative
165 .fromProjectFileContent(rnProjectContent)
166 .createProject(projectRoot, applicationName);
167 });
168
169 teardown(async () => {
170 // Delete existing React Native project after each test
171 await rimrafAsync(projectsFolder, {});
172 launchAppStub.restore();
173 getConnectedTargetsStub.restore();
174 getOnlineTargetsStub.restore();
175 apiVersionStub.restore();
176 reverseAdbStub.restore();
177 getReactNativeProjectRootStub.restore();
178 spawnReactCommandStub.restore();
179 getReactNativeVersionsStub.restore();
180 installAppInDeviceStub.restore();
181 devices = [];
182 });
183
184 const testWithRecordings = new RecordingsHelper(() => reactNative).test;
185
186 testWithRecordings(
187 "runApp launches the app when a single emulator is connected",
188 [
189 "react-native/run-android/win10-rn0.21.0/succeedsWithOneVSEmulator",
190 "react-native/run-android/win10-rn0.22.2/succeedsWithOneVSEmulator",
191 "react-native/run-android/osx10.10-rn0.21.0/succeedsWithOneVSEmulator",
192 ],
193 async () => {
194 devices = fillDevices(["Nexus_5"]);
195
196 await androidPlatform.runApp();
197 const isRunning =
198 devices[0].installedApplications[androidPackageName].isInDebugMode === false;
199 isRunning.should.be.true();
200 },
201 );
202
203 testWithRecordings(
204 "runApp launches the app when two emulators are connected",
205 ["react-native/run-android/win10-rn0.21.0/succeedsWithTwoVSEmulators"],
206 async () => {
207 devices = fillDevices(["Nexus_5", "Nexus_6"]);
208
209 await androidPlatform.runApp();
210 const [isRunningOnNexus5, isRunningOnNexus6] = [
211 devices[0].installedApplications[androidPackageName].isInDebugMode === false,
212 devices[1].installedApplications[androidPackageName].isInDebugMode === false,
213 ];
214 // It should be running in exactly one of these two devices
215 isRunningOnNexus5.should.not.eql(isRunningOnNexus6);
216 },
217 );
218
219 testWithRecordings(
220 "runApp launches the app when three emulators are connected",
221 ["react-native/run-android/win10-rn0.21.0/succeedsWithThreeVSEmulators"],
222 async () => {
223 devices = fillDevices(["Nexus_5", "Nexus_6", "Nexus_7"]);
224
225 await androidPlatform.runApp();
226 const isRunningList = [
227 devices[0].installedApplications[androidPackageName].isInDebugMode === false,
228 devices[1].installedApplications[androidPackageName].isInDebugMode === false,
229 devices[2].installedApplications[androidPackageName].isInDebugMode === false,
230 ];
231 // It should be running in exactly one of these three devices
232 isRunningList.filter(v => v).should.eql([true]);
233 },
234 );
235
236 testWithRecordings(
237 "runApp fails if no devices are connected",
238 ["react-native/run-android/win10-rn0.21.0/failsDueToNoDevicesConnected"],
239 async () => {
240 try {
241 await androidPlatform.runApp();
242 should.assert(false, "runApp should've exited with an error");
243 } catch (error) {
244 (error as Error).message
245 .startsWith("There is no any Android debuggable online target")
246 .should.be.true();
247 }
248 },
249 );
250
251 testWithRecordings(
252 "runApp launches the app in an online emulator only",
253 ["react-native/run-android/win10-rn0.21.0/succeedsWithFiveVSEmulators"],
254 async () => {
255 devices = fillDevices(["Nexus_5", "Nexus_6", "Nexus_7", "Nexus_8", "Nexus_9"]);
256 devices[4].isOnline = false;
257
258 await androidPlatform.runApp();
259 const isRunningOnOfflineDevice =
260 devices[4].installedApplications[androidPackageName].isInDebugMode === false;
261 isRunningOnOfflineDevice.should.be.false();
262 },
263 );
264
265 testWithRecordings(
266 "runApp launches the app in the device specified as target",
267 ["react-native/run-android/win10-rn0.21.0/succeedsWithFiveVSEmulators"],
268 async () => {
269 devices = fillDevices(["Nexus_5", "Nexus_6", "Nexus_10", "Nexus_11", "Nexus_12"]);
270
271 const runOptions: any = {
272 platform: PlatformType.Android,
273 workspaceRoot: projectRoot,
274 projectRoot: projectRoot,
275 target: "Nexus_12",
276 reactNativeVersions: {
277 reactNativeVersion: "^0.19.0",
278 reactNativeWindowsVersion: "",
279 },
280 nodeModulesRoot,
281 };
282 const platform = createAndroidPlatform(runOptions);
283 (platform as any).adbHelper = adbHelper;
284 await platform.runApp();
285 const isRunningOnNexus12 =
286 devices[4].installedApplications[androidPackageName].isInDebugMode === false;
287 isRunningOnNexus12.should.be.true();
288 },
289 );
290
291 testWithRecordings(
292 "runApp launches the app in a random online device if the target is offline",
293 ["react-native/run-android/win10-rn0.21.0/succeedsWithTenVSEmulators"],
294 async () => {
295 const onlineDevicesIds = [
296 "Nexus_11",
297 "Nexus_13",
298 "Nexus_14",
299 "Nexus_15",
300 "Nexus_16",
301 "Nexus_17",
302 ];
303 const offineDevicesIds = ["Nexus_5", "Nexus_6", "Nexus_10", "Nexus_12"];
304 devices = fillDevices(offineDevicesIds.concat(onlineDevicesIds));
305 devices[0].isOnline = false;
306 devices[1].isOnline = false;
307 devices[2].isOnline = false;
308 devices[3].isOnline = false;
309
310 const runOptions: any = {
311 platform: PlatformType.Android,
312 workspaceRoot: projectRoot,
313 projectRoot: projectRoot,
314 target: "Nexus_12",
315 reactNativeVersions: {
316 reactNativeVersion: "^0.19.0",
317 reactNativeWindowsVersion: "",
318 },
319 nodeModulesRoot,
320 };
321 const platform = createAndroidPlatform(runOptions);
322 (platform as any).adbHelper = adbHelper;
323 await platform.runApp();
324 const devicesRunningAppId = devices.filter(
325 (device: any) =>
326 device.installedApplications[androidPackageName].isInDebugMode === false,
327 );
328 devicesRunningAppId.length.should.eql(1);
329 onlineDevicesIds.should.containEql(devicesRunningAppId[0].id);
330 },
331 );
332
333 testWithRecordings(
334 "runApp doesn't fail even if the call to start the LogCat does fail",
335 [
336 "react-native/run-android/win10-rn0.21.0/succeedsWithOneVSEmulator",
337 "react-native/run-android/win10-rn0.22.2/succeedsWithOneVSEmulator",
338 "react-native/run-android/osx10.10-rn0.21.0/succeedsWithOneVSEmulator",
339 ],
340 async () => {
341 devices = fillDevices(["Nexus_5"]);
342
343 await androidPlatform.runApp();
344 const isRunning =
345 devices[0].installedApplications[androidPackageName].isInDebugMode === false;
346 isRunning.should.be.true();
347 },
348 );
349
350 testWithRecordings(
351 "runApp fails when the android project doesn't exist, and shows a nice error message",
352 [
353 "react-native/run-android/win10-rn0.21.0/failsDueToAndroidFolderMissing",
354 "react-native/run-android/win10-rn0.22.2/failsDueToAndroidFolderMissing",
355 ],
356 async () => {
357 devices = fillDevices(["Nexus_5"]);
358
359 await fileSystem.rmdir(androidProjectPath);
360 let isRunning: boolean;
361 try {
362 await androidPlatform.runApp();
363 should.assert(false, "Expected runApp to end up with an error");
364 isRunning = false;
365 } catch (error) {
366 (error as Error).message.should.eql(
367 "Android project not found. (error code 1203)",
368 );
369 isRunning = !!devices[0].installedApplications[androidPackageName];
370 }
371 isRunning.should.be.false();
372 },
373 );
374
375 testWithRecordings(
376 "runApp fails when the android emulator shell is unresponsive, and shows a nice error message",
377 ["react-native/run-android/osx10.10-rn0.21.0/failsDueToAdbCommandTimeout"],
378 async () => {
379 devices = fillDevices(["Nexus_5"]);
380
381 let isRunning: boolean;
382 try {
383 await androidPlatform.runApp();
384 should.assert(false, "Expected runApp to end up with an error");
385 isRunning = false;
386 } catch (error) {
387 "An Android shell command timed-out. Please retry the operation. (error code 1202)".should.eql(
388 (error as Error).message,
389 );
390 isRunning = !!devices[0].installedApplications[androidPackageName];
391 }
392 isRunning.should.be.false();
393 },
394 );
395
396 test("getRunArguments should return correct target", function () {
397 const runOptions: any = {
398 platform: PlatformType.Android,
399 workspaceRoot: projectRoot,
400 projectRoot: projectRoot,
401 target: "Nexus_12",
402 nodeModulesRoot,
403 };
404 const platform = createAndroidPlatform(runOptions);
405 const runArgs = platform.getRunArguments();
406
407 runArgs.should.be.an.Array();
408 runArgs.should.containDeepOrdered(["--deviceId", "Nexus_12"]);
409 });
410
411 test("getRunArguments should remove simulator target from args", function () {
412 const runOptions: any = {
413 platform: PlatformType.Android,
414 workspaceRoot: projectRoot,
415 projectRoot: projectRoot,
416 target: "simulator",
417 nodeModulesRoot,
418 };
419 const platform = createAndroidPlatform(runOptions);
420 const runArgs = platform.getRunArguments();
421
422 runArgs.should.be.an.Array();
423 runArgs.should.be.empty();
424 });
425
426 test("getRunArguments should remove device target from args", function () {
427 const runOptions: any = {
428 platform: PlatformType.Android,
429 workspaceRoot: projectRoot,
430 projectRoot: projectRoot,
431 target: "device",
432 nodeModulesRoot,
433 };
434 const platform = createAndroidPlatform(runOptions);
435 const runArgs = platform.getRunArguments();
436
437 runArgs.should.be.an.Array();
438 runArgs.should.be.empty();
439 });
440
441 test("getRunArguments should return correct args", function () {
442 const args = ["--deviceId", "device_id"];
443 const runOptions: any = {
444 platform: PlatformType.Android,
445 workspaceRoot: projectRoot,
446 projectRoot: projectRoot,
447 runArguments: args,
448 target: "Nexus_12",
449 nodeModulesRoot,
450 };
451 const platform = createAndroidPlatform(runOptions);
452 const runArgs = platform.getRunArguments();
453
454 runArgs.should.be.an.Array();
455 runArgs.should.containDeepOrdered(args);
456 });
457
458 test("AdbHelper should correctly parse Android Sdk Location from local.properties file content", () => {
459 const adbHelper = new adb.AdbHelper("", nodeModulesRoot);
460 let getPlatformStub: Sinon.SinonStub;
461 function testPaths(inputPath: string, expectedPath: string) {
462 const resultPath1 = adbHelper.parseSdkLocation(`sdk.dir=${inputPath}`);
463 const resultPath2 = adbHelper.parseSdkLocation(`sdk.dir =${inputPath}`);
464 const resultPath3 = adbHelper.parseSdkLocation(`sdk.dir = ${inputPath}`);
465 assert.strictEqual(resultPath1, expectedPath);
466 assert.strictEqual(resultPath2, expectedPath);
467 assert.strictEqual(resultPath3, expectedPath);
468 }
469
470 const os = require("os");
471 function mockPlatform(platform: NodeJS.Platform) {
472 getPlatformStub?.restore();
473 getPlatformStub = sinon.stub(os, "platform", function () {
474 return platform;
475 });
476 }
477
478 mockPlatform("win32");
479 testPaths(
480 String.raw`C\:\\Users\\User1\\AndroidSdk`,
481 String.raw`C:\Users\User1\AndroidSdk`,
482 );
483 testPaths(String.raw`\\\\Network\\Shared\\Folder`, String.raw`\\Network\Shared\Folder`);
484 testPaths(
485 String.raw`\\\\Network\\Shared\\Folder\\Android SDK`,
486 String.raw`\\Network\Shared\Folder\Android SDK`,
487 );
488 testPaths(
489 String.raw`C\:\\Users\\User1\\Android Sdk`,
490 String.raw`C:\Users\User1\Android Sdk`,
491 );
492
493 mockPlatform("darwin");
494 testPaths(String.raw`/var/lib/some/path`, String.raw`/var/lib/some/path`);
495 testPaths(String.raw`~/Library`, String.raw`~/Library`);
496 testPaths(String.raw`/Users/User1/home/path`, String.raw`/Users/User1/home/path`);
497 testPaths(
498 String.raw`/Users/User1/home/path/Android SDK`,
499 String.raw`/Users/User1/home/path/Android SDK`,
500 );
501 testPaths(
502 String.raw`/Volumes/Macintosh HD/Users/foo/Library/Android/sdk/platform-tools`,
503 String.raw`/Volumes/Macintosh HD/Users/foo/Library/Android/sdk/platform-tools`,
504 );
505
506 teardown(() => {
507 getPlatformStub?.restore();
508 });
509 });
510
511 test("AdbHelper parseSdkLocation function should correctly parse Android Sdk Location from local.properties and wrap with quotes", () => {
512 function testPaths(expectedPath: string, projectRoot: string) {
513 const adbHelper = new adb.AdbHelper(projectRoot, nodeModulesRoot);
514 const localPropertiesFilePath = path.join(
515 projectRoot,
516 "android",
517 "local.properties",
518 );
519 const fileContent = fs.readFileSync(localPropertiesFilePath).toString();
520 const resultPath = adbHelper.parseSdkLocation(fileContent);
521 assert.strictEqual(resultPath, expectedPath);
522 }
523
524 if (process.platform == "win32") {
525 const mockProjectRoot = path.join(
526 __dirname,
527 "..",
528 "..",
529 "..",
530 "test",
531 "resources",
532 "auxiliaryFiles",
533 "templateProject",
534 "win",
535 );
536 testPaths(String.raw`C:\Android\android sdk`, mockProjectRoot);
537 } else {
538 const mockProjectRoot = path.join(
539 __dirname,
540 "..",
541 "..",
542 "..",
543 "test",
544 "resources",
545 "auxiliaryFiles",
546 "templateProject",
547 "others",
548 );
549 testPaths(
550 String.raw`/Volumes/Macintosh HD/Users/foo/Library/Android/sdk/`,
551 mockProjectRoot,
552 );
553 }
554 });
555
556 test("AdbHelper getAdbPath function should get sdk and adb path flexible from local.properties file and environment variable", () => {
557 let mockProjectRoot;
558 if (process.platform == "win32") {
559 mockProjectRoot = path.join(
560 __dirname,
561 "..",
562 "..",
563 "..",
564 "test",
565 "resources",
566 "auxiliaryFiles",
567 "templateProject",
568 "win",
569 );
570 } else {
571 mockProjectRoot = path.join(
572 __dirname,
573 "..",
574 "..",
575 "..",
576 "test",
577 "resources",
578 "auxiliaryFiles",
579 "templateProject",
580 "others",
581 );
582 }
583
584 const adbHelper = new adb.AdbHelper(mockProjectRoot, nodeModulesRoot);
585 const adbValue = adbHelper.getAdbPath(mockProjectRoot);
586 assert.strictEqual(adbValue, "adb");
587 });
588 });
589});
590
591function fillDevices(ids: string[]): any[] {
592 let devices: any[] = [];
593 ids.forEach(id => {
594 devices.push({
595 isOnline: true,
596 installedApplications: {},
597 runningApplications: {},
598 isVirtualTarget: true,
599 id: id,
600 });
601 });
602
603 return devices;
604}
605