[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/NativeScript/nativescript-cli/experiments/test/debug.ts [Back]  [Original]

import * as stubs from "./stubs";
import * as yok from "../lib/common/yok";
import { DebugAndroidCommand, DebugPlatformCommand } from "../lib/commands/debug";
import { assert } from "chai";
import { Configuration, StaticConfig } from "../lib/config";
import { Options } from "../lib/options";
import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants";
import { FileSystem } from "../lib/common/file-system";
import { AndroidProjectService } from "../lib/services/android-project-service";
import { AndroidDebugBridge } from "../lib/common/mobile/android/android-debug-bridge";
import { AndroidDebugBridgeResultHandler } from "../lib/common/mobile/android/android-debug-bridge-result-handler";
import { DebugCommandErrors } from "../lib/constants";
import { CONNECTED_STATUS, UNREACHABLE_STATUS } from "../lib/common/constants";
import { SettingsService } from "../lib/common/test/unit-tests/stubs";

const helpers = require("../lib/common/helpers");
const originalIsInteracive = helpers.isInteractive;

function createTestInjector(): IInjector {
	const testInjector: IInjector = new yok.Yok();

	testInjector.register("debug|android", DebugAndroidCommand);
	testInjector.register("config", Configuration);
	testInjector.register("staticConfig", StaticConfig);
	testInjector.register("logger", stubs.LoggerStub);
	testInjector.register("options", Options);
	testInjector.register("devicePlatformsConstants", DevicePlatformsConstants);
	testInjector.register('childProcess', stubs.ChildProcessStub);
	testInjector.register('fs', FileSystem);
	testInjector.register('errors', stubs.ErrorsStub);
	testInjector.register('hostInfo', {});
	testInjector.register("analyticsService", {
		trackException: async (): Promise => undefined,
		checkConsent: async (): Promise => undefined,
		trackFeature: async (): Promise => undefined
	});
	testInjector.register('devicesService', {
		initialize: async () => { /* Intentionally left blank */ },
		getDeviceInstances: (): any[] => { return []; },
		execute: async (): Promise => ({})
	});
	testInjector.register("liveSyncService", stubs.LiveSyncServiceStub);
	testInjector.register("androidProjectService", AndroidProjectService);
	testInjector.register("androidToolsInfo", stubs.AndroidToolsInfoStub);
	testInjector.register("hostInfo", {});
	testInjector.register("projectData", { platformsDir: "test", initializeProjectData: () => { /* empty */ } });
	testInjector.register("projectDataService", {});
	testInjector.register("sysInfo", {});
	testInjector.register("mobileHelper", {});
	testInjector.register("pluginVariablesService", {});
	testInjector.register("deviceAppDataFactory", {});
	testInjector.register("projectTemplatesService", {});
	testInjector.register("debugService", {});
	testInjector.register("xmlValidator", {});
	testInjector.register("npm", {});
	testInjector.register("debugDataService", {
		createDebugData: () => ({})
	});
	testInjector.register("androidEmulatorServices", {});
	testInjector.register("adb", AndroidDebugBridge);
	testInjector.register("androidDebugBridgeResultHandler", AndroidDebugBridgeResultHandler);
	testInjector.register("platformService", stubs.PlatformServiceStub);
	testInjector.register("platformsData", {
		availablePlatforms: {
			Android: "Android",
			iOS: "iOS"
		}
	});

	testInjector.register("prompter", {});
	testInjector.registerCommand("debug|android", DebugAndroidCommand);
	testInjector.register("liveSyncCommandHelper", {
		executeLiveSyncOperation: async (): Promise => {
			return null;
		}
	});
	testInjector.register("settingsService", SettingsService);
	testInjector.register("androidPluginBuildService", stubs.AndroidPluginBuildServiceStub);
	testInjector.register("platformEnvironmentRequirements", {});
	testInjector.register("androidResourcesMigrationService", stubs.AndroidResourcesMigrationServiceStub);

	return testInjector;
}

describe("debug command tests", () => {
	describe("getDeviceForDebug", () => {
		it("throws error when both --for-device and --emulator are passed", async () => {
			const testInjector = createTestInjector();
			const options = testInjector.resolve("options");
			options.forDevice = options.emulator = true;
			const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
			await assert.isRejected(debugCommand.getDeviceForDebug(), DebugCommandErrors.UNABLE_TO_USE_FOR_DEVICE_AND_EMULATOR);
		});

		it("returns selected device, when --device is passed", async () => {
			const testInjector = createTestInjector();
			const devicesService = testInjector.resolve("devicesService");
			const deviceInstance = {};
			const specifiedDeviceOption = "device1";
			devicesService.getDevice = async (deviceOption: string): Promise => {
				if (deviceOption === specifiedDeviceOption) {
					return deviceInstance;
				}
			};

			const options = testInjector.resolve("options");
			options.device = specifiedDeviceOption;
			const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
			const selectedDeviceInstance = await debugCommand.getDeviceForDebug();
			assert.deepEqual(selectedDeviceInstance, deviceInstance);
		});

		const assertErrorIsThrown = async (getDeviceInstancesResult: Mobile.IDevice[], passedOptions?: { forDevice: boolean, emulator: boolean }) => {
			const testInjector = createTestInjector();
			if (passedOptions) {
				const options = testInjector.resolve("options");
				options.forDevice = passedOptions.forDevice;
				options.emulator = passedOptions.emulator;
			}

			const devicesService = testInjector.resolve("devicesService");
			devicesService.getDeviceInstances = (): Mobile.IDevice[] => getDeviceInstancesResult;

			const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
			await assert.isRejected(debugCommand.getDeviceForDebug(), DebugCommandErrors.NO_DEVICES_EMULATORS_FOUND_FOR_OPTIONS);
		};

		it("throws error when there are no devices/emulators available", () => {
			return assertErrorIsThrown([]);
		});

		it("throws error when there are no devices/emulators available for selected platform", () => {
			return assertErrorIsThrown([
				{
					deviceInfo: {
						platform: "ios",
						status: CONNECTED_STATUS
					}
				}
			]);
		});

		it("throws error when there are only not-trusted devices/emulators available for selected platform", () => {
			return assertErrorIsThrown([
				{
					deviceInfo: {
						platform: "android",
						status: UNREACHABLE_STATUS
					}
				}
			]);
		});

		it("throws error when there are only devices and --emulator is passed", () => {
			return assertErrorIsThrown([
				{
					deviceInfo: {
						platform: "android",
						status: CONNECTED_STATUS
					},
					isEmulator: false
				}
			], {
					forDevice: false,
					emulator: true
				});
		});

		it("throws error when there are only emulators and --forDevice is passed", () => {
			return assertErrorIsThrown([
				{
					deviceInfo: {
						platform: "android",
						status: CONNECTED_STATUS
					},
					isEmulator: true
				}
			], {
					forDevice: true,
					emulator: false
				});
		});

		it("returns the only available device/emulator when it matches passed -- options", async () => {
			const testInjector = createTestInjector();
			const deviceInstance = {
				deviceInfo: {
					platform: "android",
					status: CONNECTED_STATUS
				},
				isEmulator: true
			};

			const devicesService = testInjector.resolve("devicesService");
			devicesService.getDeviceInstances = (): Mobile.IDevice[] => [deviceInstance];

			const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
			const actualDeviceInstance = await debugCommand.getDeviceForDebug();
			assert.deepEqual(actualDeviceInstance, deviceInstance);
		});

		describe("when multiple devices are detected", () => {
			beforeEach(() => {
				helpers.isInteractive = originalIsInteracive;
			});

			after(() => {
				helpers.isInteractive = originalIsInteracive;
			});

			describe("when terminal is interactive", () => {

				it("prompts the user with information about available devices for specified platform only and returns the selected device instance", async () => {
					helpers.isInteractive = () => true;
					const testInjector = createTestInjector();
					const deviceInstance1 = {
						deviceInfo: {
							platform: "android",
							status: CONNECTED_STATUS,
							identifier: "deviceInstance1",
							displayName: "displayName1"
						},
						isEmulator: true
					};

					const deviceInstance2 = {
						deviceInfo: {
							platform: "android",
							status: CONNECTED_STATUS,
							identifier: "deviceInstance2",
							displayName: "displayName2"
						},
						isEmulator: true
					};

					const iOSDeviceInstance = {
						deviceInfo: {
							platform: "ios",
							status: CONNECTED_STATUS,
							identifier: "iosDevice",
							displayName: "iPhone"
						},
						isEmulator: true
					};

					const devicesService = testInjector.resolve("devicesService");
					devicesService.getDeviceInstances = (): Mobile.IDevice[] => [deviceInstance1, deviceInstance2, iOSDeviceInstance];

					let choicesPassedToPrompter: string[];
					const prompter = testInjector.resolve("prompter");
					prompter.promptForChoice = async (promptMessage: string, choices: any[]): Promise => {
						choicesPassedToPrompter = choices;
						return choices[1];
					};

					const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
					const actualDeviceInstance = await debugCommand.getDeviceForDebug();
					const expectedChoicesPassedToPrompter = [deviceInstance1, deviceInstance2].map(d => `${d.deviceInfo.identifier} - ${d.deviceInfo.displayName}`);
					assert.deepEqual(choicesPassedToPrompter, expectedChoicesPassedToPrompter);

					assert.deepEqual(actualDeviceInstance, deviceInstance2);
				});
			});

			describe("when terminal is not interactive", () => {
				beforeEach(() => {
					helpers.isInteractive = () => false;
				});

				const assertCorrectInstanceIsUsed = async (opts: { forDevice: boolean, emulator: boolean, isEmulatorTest: boolean, excludeLastDevice?: boolean }) => {
					const testInjector = createTestInjector();
					const deviceInstance1 = {
						deviceInfo: {
							platform: "android",
							status: CONNECTED_STATUS,
							identifier: "deviceInstance1",
							displayName: "displayName1",
							version: "5.1"
						},
						isEmulator: opts.isEmulatorTest
					};

					const deviceInstance2 = {
						deviceInfo: {
							platform: "android",
							status: CONNECTED_STATUS,
							identifier: "deviceInstance2",
							displayName: "displayName2",
							version: "6.0"
						},
						isEmulator: opts.isEmulatorTest
					};

					const deviceInstance3 = {
						deviceInfo: {
							platform: "android",
							status: CONNECTED_STATUS,
							identifier: "deviceInstance3",
							displayName: "displayName3",
							version: "7.1"
						},
						isEmulator: !opts.isEmulatorTest
					};

					const options = testInjector.resolve("options");
					options.forDevice = opts.forDevice;
					options.emulator = opts.emulator;

					const devicesService = testInjector.resolve("devicesService");
					const deviceInstances = [deviceInstance1, deviceInstance2];
					if (!opts.excludeLastDevice) {
						deviceInstances.push(deviceInstance3);
					}

					devicesService.getDeviceInstances = (): Mobile.IDevice[] => deviceInstances;

					const debugCommand = testInjector.resolve(DebugPlatformCommand, { debugService: {}, platform: "android" });
					const actualDeviceInstance = await debugCommand.getDeviceForDebug();

					assert.deepEqual(actualDeviceInstance, deviceInstance2);
				};

				it("returns the emulator with highest API level when --emulator is passed", () => {
					return assertCorrectInstanceIsUsed({ forDevice: false, emulator: true, isEmulatorTest: true });
				});

				it("returns the device with highest API level when --forDevice is passed", () => {
					return assertCorrectInstanceIsUsed({ forDevice: true, emulator: false, isEmulatorTest: false });
				});

				it("returns the emulator with highest API level when neither --emulator and --forDevice are passed", () => {
					return assertCorrectInstanceIsUsed({ forDevice: false, emulator: false, isEmulatorTest: true });
				});

				it("returns the device with highest API level when neither --emulator and --forDevice are passed and emulators are not available", async () => {
					return assertCorrectInstanceIsUsed({ forDevice: false, emulator: false, isEmulatorTest: false, excludeLastDevice: true });
				});
			});
		});
	});

	describe("Debugger tests", () => {
		let testInjector: IInjector;

		beforeEach(() => {
			testInjector = createTestInjector();
		});

		it("Ensures that beforePrepareAllPlugins will call gradle with clean option when *NOT* livesyncing", async () => {
			const platformData = testInjector.resolve("platformsData");
			platformData.frameworkPackageName = "tns-android";

			// only test that 'clean' is performed on android  {
				return { version: "3.2.0" };
			};

			const childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess");
			const androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService");
			androidProjectService.getPlatformData = (projectData: IProjectData): IPlatformData => {
				return platformData;
			};
			const projectData: IProjectData = testInjector.resolve("projectData");
			const spawnFromEventCount = childProcess.spawnFromEventCount;
			await androidProjectService.beforePrepareAllPlugins(projectData);
			assert.isTrue(childProcess.lastCommand.indexOf("gradle") !== -1);
			assert.isTrue(childProcess.lastCommandArgs[0] === "clean");
			assert.isTrue(spawnFromEventCount === 0);
			assert.isTrue(spawnFromEventCount + 1 === childProcess.spawnFromEventCount);
		});
	});
});

Web Proxy Viewer  |  New URL  |  Original Page