diff --git a/index.d.ts b/index.d.ts index 3da73d1..87ddd01 100644 --- a/index.d.ts +++ b/index.d.ts @@ -8,7 +8,7 @@ export { Point } from "./lib/point"; export { SearchOptions } from "./lib/search-options"; export { Locator } from "./lib/locators"; export { Direction } from "./lib/direction"; -export { DeviceManger } from "./lib/device-controller"; +export { DeviceManager } from "./lib/device-manager"; export { FrameComparer } from "./lib/frame-comparer"; export { IRectangle } from "./lib/interfaces/rectangle"; export { IDeviceManager } from "./lib/interfaces/device-manager"; diff --git a/index.ts b/index.ts index 9e3af33..9e783a7 100644 --- a/index.ts +++ b/index.ts @@ -6,6 +6,7 @@ import { IDeviceManager } from "./lib/interfaces/device-manager"; import { shutdown, findFreePort } from "./lib/utils"; import * as frameComparerHelper from "./lib/frame-comparer"; import { FrameComparer } from "./lib/frame-comparer"; +import { DeviceManager } from "./lib/device-manager"; export { AppiumDriver } from "./lib/appium-driver"; export { AppiumServer } from "./lib/appium-server"; @@ -15,7 +16,7 @@ export { Point } from "./lib/point"; export { SearchOptions } from "./lib/search-options"; export { Locator } from "./lib/locators"; export { Direction } from "./lib/direction"; -export { DeviceManger } from "./lib/device-controller"; +export { DeviceManager } from "./lib/device-manager"; export { FrameComparer } from "./lib/frame-comparer"; export { IRectangle } from "./lib/interfaces/rectangle"; export { IDeviceManager } from "./lib/interfaces/device-manager"; @@ -25,7 +26,7 @@ const appiumServer = new AppiumServer(nsCapabilities); let frameComparer: FrameComparer; let appiumDriver = null; -const attachToExitProcessHoockup = (processToExitFrom, processName ) => { +const attachToExitProcessHoockup = (processToExitFrom, processName) => { const signals = ['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM']; signals.forEach(function (sig) { @@ -71,7 +72,7 @@ export async function createDriver() { // Make sure to turn off "Don't keep activities" // in case of previous execution failure. - await appiumDriver.setDontKeepActivities(false); + await DeviceManager.setDontKeepActivities(nsCapabilities, appiumDriver, false); return appiumDriver; } @@ -98,4 +99,4 @@ const killProcesses = async (code) => { process.once("exit", async (code) => await killProcesses(code)); -attachToExitProcessHoockup(process,"main process"); \ No newline at end of file +attachToExitProcessHoockup(process, "main process"); \ No newline at end of file diff --git a/lib/appium-driver.d.ts b/lib/appium-driver.d.ts index 4642b90..98bd5f1 100644 --- a/lib/appium-driver.d.ts +++ b/lib/appium-driver.d.ts @@ -55,7 +55,6 @@ export declare class AppiumDriver { */ readonly storageByDeviceName: string; static createAppiumDriver(port: number, args: INsCapabilities): Promise; - private static applyDeviceAdditionsSettings; /** * * @param xPath @@ -204,9 +203,5 @@ export declare class AppiumDriver { * @param waitForElement */ findElementByAccessibilityIdIfExists(id: string, waitForElement?: number): Promise; - static executeShellCommand(driver: any, commandAndargs: { - command: string; - "args": Array; - }): Promise; setDontKeepActivities(value: boolean): Promise; } diff --git a/lib/appium-driver.ts b/lib/appium-driver.ts index d56bcd3..7846a26 100644 --- a/lib/appium-driver.ts +++ b/lib/appium-driver.ts @@ -41,6 +41,7 @@ import { ImageHelper } from "./image-helper"; import { ImageOptions } from "./image-options" import { unlinkSync, writeFileSync } from "fs"; import * as webdriverio from "webdriverio"; +import { DeviceManager } from "../lib/device-manager"; export class AppiumDriver { private static pngFileExt = '.png'; @@ -187,7 +188,7 @@ export class AppiumDriver { try { const sessionIfno = await driver.init(args.appiumCaps); log(sessionIfno, args.verbose); - await AppiumDriver.applyDeviceAdditionsSettings(driver, args, sessionIfno); + await DeviceManager.applyDeviceAdditionsSettings(driver, args, sessionIfno); hasStarted = true; } catch (error) { @@ -209,34 +210,6 @@ export class AppiumDriver { return new AppiumDriver(driver, wd, webio, driverConfig, args); } - private static async applyDeviceAdditionsSettings(driver, args: INsCapabilities, sessionIfno: any) { - if (!args.device.config || !args.device.config.density || !args.device.config.offset) { - args.device.config = {}; - let density: number = sessionIfno[1].deviceScreenDensity / 100; - console.log(`Get density from appium session: ${density}`); - if (!density) { - density = await AppiumDriver.executeShellCommand(driver, { command: "wm", args: ["density"] }); - console.log(`Device density recieved from adb shell command ${density}`); - } - args.device.config['density'] = density; - - if (args.appiumCaps.platformName.toLowerCase() === "android") { - args.device.config['offsetPixels'] = AndroidController.calculateScreenOffset(density); - } else { - IOSController.getDevicesScreenInfo().forEach((v, k, m) => { - if (args.device.name.includes(k)) { - args.device.config = { - density: args.device.config['density'] || v.density, - offsetPixels: v.actionBarHeight - }; - } - }); - } - - console.log(`Device setting:`, args.device.config); - } - } - /** * * @param xPath @@ -738,18 +711,9 @@ export class AppiumDriver { } } - public static async executeShellCommand(driver, commandAndargs: { command: string, "args": Array }) { - const output = await driver.execute("mobile: shell", commandAndargs); - return output; - } - public async setDontKeepActivities(value: boolean) { if (this._args.isAndroid) { - const status = value ? 1 : 0; - const output = await AppiumDriver.executeShellCommand(this._driver, { command: "settings", args: ['put', 'global', 'always_finish_activities', status] }); - //check if set - const check = await AppiumDriver.executeShellCommand(this._driver, { command: "settings", args: ['get', 'global', 'always_finish_activities'] }) - console.info(`always_finish_activities: ${check}`) + const output = await DeviceManager.setDontKeepActivities(this._args, this._driver, value); } else { // Do nothing for iOS ... } diff --git a/lib/appium-server.ts b/lib/appium-server.ts index b522f2e..4016731 100644 --- a/lib/appium-server.ts +++ b/lib/appium-server.ts @@ -12,7 +12,7 @@ import { } from "./utils"; import { INsCapabilities } from "./interfaces/ns-capabilities"; import { IDeviceManager } from "./interfaces/device-manager"; -import { DeviceManger } from "./device-controller"; +import { DeviceManager } from "./device-manager"; import { AndroidController } from "mobile-devices-controller"; export class AppiumServer { @@ -57,7 +57,7 @@ export class AppiumServer { this._hasStarted = hasStarted; } - public async start(port, deviceManager: IDeviceManager = new DeviceManger()) { + public async start(port, deviceManager: IDeviceManager = new DeviceManager()) { await this.prepareDevice(deviceManager); await this.prepareApp(); @@ -85,7 +85,9 @@ export class AppiumServer { private startAppiumServer(logLevel: string, isSauceLab: boolean) { const startingServerArgs: Array = isSauceLab ? ["--log-level", logLevel] : ["-p", this.port.toString(), "--log-level", logLevel]; - startingServerArgs.push("--relaxed-security"); + if(this._args.isAndroid && this._args.ignoreDeviceController && !this._args.isSauceLab){ + this._args.relaxedSecurity ? startingServerArgs.push("--relaxed-security") : console.log("'relaxedSecurity' is not enabled!\nTo enabled it use '--relaxedSecurity'!"); + } this._server = child_process.spawn(this._appium, startingServerArgs, { shell: true, detached: false diff --git a/lib/device-controller.d.ts b/lib/device-controller.d.ts index aeb0205..10cbce5 100644 --- a/lib/device-controller.d.ts +++ b/lib/device-controller.d.ts @@ -11,4 +11,11 @@ export declare class DeviceManger implements IDeviceManager { static kill(device: IDevice): Promise; private static getDefaultDevice; getPackageId(device: IDevice, appPath: string): string; + static setDontKeepActivities(nsArgs: INsCapabilities, driver: any, value: boolean): Promise; + static executeShellCommand(driver: any, commandAndargs: { + command: string; + "args": Array; + includeStderr?: boolean; + }): Promise; + static getDensity(nsArgs: any, driver: any): Promise; } diff --git a/lib/device-manager.d.ts b/lib/device-manager.d.ts new file mode 100644 index 0000000..858ee0f --- /dev/null +++ b/lib/device-manager.d.ts @@ -0,0 +1,21 @@ +import { INsCapabilities } from "./interfaces/ns-capabilities"; +import { IDeviceManager } from "./interfaces/device-manager"; +import { IDevice } from "mobile-devices-controller"; +export declare class DeviceManager implements IDeviceManager { + private static _emulators; + constructor(); + startDevice(args: INsCapabilities): Promise; + stopDevice(args: INsCapabilities): Promise; + installApp(args: INsCapabilities): Promise; + uninstallApp(args: INsCapabilities): Promise; + static kill(device: IDevice): Promise; + private static getDefaultDevice; + static setDontKeepActivities(args: INsCapabilities, driver: any, value: any): Promise; + static executeShellCommand(driver: IDevice, commandAndargs: { + command: string; + "args": Array; + }): Promise; + static getDensity(args: INsCapabilities, driver: any): Promise; + static applyDeviceAdditionsSettings(driver: any, args: INsCapabilities, sessionIfno: any): Promise; + getPackageId(device: IDevice, appPath: string): string; +} diff --git a/lib/device-controller.ts b/lib/device-manager.ts similarity index 58% rename from lib/device-controller.ts rename to lib/device-manager.ts index 143cb46..cf44ef8 100644 --- a/lib/device-controller.ts +++ b/lib/device-manager.ts @@ -21,7 +21,7 @@ import { DeviceType } from "mobile-devices-controller"; -export class DeviceManger implements IDeviceManager { +export class DeviceManager implements IDeviceManager { private static _emulators: Map = new Map(); constructor() { @@ -29,7 +29,7 @@ export class DeviceManger implements IDeviceManager { public async startDevice(args: INsCapabilities): Promise { args.appiumCaps.platformName = args.appiumCaps.platformName.toLowerCase(); - let device: IDevice = DeviceManger.getDefaultDevice(args); + let device: IDevice = DeviceManager.getDefaultDevice(args); if (process.env["DEVICE_TOKEN"]) { device.token = process.env["DEVICE_TOKEN"]; device.name = process.env["DEVICE_NAME"] || device.name; @@ -41,8 +41,8 @@ export class DeviceManger implements IDeviceManager { // When isSauceLab specified we simply do nothing; if (args.isSauceLab || args.ignoreDeviceController) { - args.ignoreDeviceController = true; - DeviceManger._emulators.set(args.runType, device); + args.ignoreDeviceController = true; + DeviceManager._emulators.set(args.runType, device); return device; } @@ -89,18 +89,18 @@ export class DeviceManger implements IDeviceManager { } } - DeviceManger._emulators.set(args.runType, device); + DeviceManager._emulators.set(args.runType, device); return device; } public async stopDevice(args: INsCapabilities): Promise { - if (DeviceManger._emulators.has(args.runType) + if (DeviceManager._emulators.has(args.runType) && !args.reuseDevice && !args.isSauceLab && !args.ignoreDeviceController) { - const device = DeviceManger._emulators.get(args.runType); - await DeviceManger.kill(device); + const device = DeviceManager._emulators.get(args.runType); + await DeviceManager.kill(device); } } @@ -133,6 +133,76 @@ export class DeviceManger implements IDeviceManager { return device; } + public static async setDontKeepActivities(args: INsCapabilities, driver, value) { + if (args.isAndroid) { + if (!args.ignoreDeviceController) { + AndroidController.setDontKeepActivities(value, args.device); + } else if (args.relaxedSecurity) { + const status = value ? 1 : 0; + const output = await DeviceManager.executeShellCommand(driver, { command: "settings", args: ['put', 'global', 'always_finish_activities', status] }); + //check if set + const check = await DeviceManager.executeShellCommand(driver, { command: "settings", args: ['get', 'global', 'always_finish_activities'] }); + console.info(`always_finish_activities: ${check}`); + } + } else { + // Do nothing for iOS ... + } + } + + public static async executeShellCommand(driver: IDevice, commandAndargs: { command: string, "args": Array }) { + if (driver.platform.toLowerCase() === Platform.ANDROID) { + const output = await driver.execute("mobile: shell", commandAndargs); + return output; + } + return undefined; + } + + public static async getDensity(args: INsCapabilities, driver) { + args.device.config = args.device.config || {}; + if (args.appiumCaps.platformName.toLowerCase() === "android") { + if (!args.ignoreDeviceController) { + args.device.config.density = await AndroidController.getPhysicalDensity(args.device); + } + + if (args.relaxedSecurity) { + args.device.config.density = await DeviceManager.executeShellCommand(driver, { command: "wm", args: ["density"] }); + console.log(`Device density recieved from adb shell command ${args.device.config.density}`); + } + + if (args.device.config.density) { + args.device.config['offsetPixels'] = AndroidController.calculateScreenOffset(args.device.config.density); + } + } else { + IOSController.getDevicesScreenInfo().forEach((v, k, m) => { + if (args.device.name.includes(k)) { + args.device.config = { + density: args.device.config['density'] || v.density, + offsetPixels: v.actionBarHeight + }; + } + }); + } + } + + public static async applyDeviceAdditionsSettings(driver, args: INsCapabilities, sessionIfno: any) { + if (!args.device.config || !args.device.config.offsetPixels) { + args.device.config = {}; + let density: number = sessionIfno[1].deviceScreenDensity ? sessionIfno[1].deviceScreenDensity / 100 : undefined; + + if (density) { + console.log(`Get density from appium session: ${density}`); + args.device.config['density'] = density; + args.device.config['offsetPixels'] = AndroidController.calculateScreenOffset(args.device.config.density); + } + + if (!density) { + await DeviceManager.getDensity(args, driver); + } + + density ? console.log(`Device setting:`, args.device.config) : console.log(`Could not resolve device density. Please provide offset in appium config`); + } + } + public getPackageId(device: IDevice, appPath: string): string { const appActivity = (device.type === DeviceType.EMULATOR || device.platform === Platform.ANDROID) ? AndroidController.getPackageId(appPath) : IOSController.getIOSPackageId(device.type, appPath); return appActivity; diff --git a/lib/interfaces/ns-capabilities.d.ts b/lib/interfaces/ns-capabilities.d.ts index 7038fa8..58fd135 100644 --- a/lib/interfaces/ns-capabilities.d.ts +++ b/lib/interfaces/ns-capabilities.d.ts @@ -30,4 +30,5 @@ export interface INsCapabilities { wdaLocalPort: number; path: string; automationName: AutomationName; + relaxedSecurity: boolean; } diff --git a/lib/interfaces/ns-capabilities.ts b/lib/interfaces/ns-capabilities.ts index 0bfa0d5..6b4e446 100644 --- a/lib/interfaces/ns-capabilities.ts +++ b/lib/interfaces/ns-capabilities.ts @@ -32,4 +32,5 @@ export interface INsCapabilities { wdaLocalPort: number; path: string; automationName: AutomationName; + relaxedSecurity: boolean } \ No newline at end of file diff --git a/lib/ns-capabilities.d.ts b/lib/ns-capabilities.d.ts index 0530dac..1ecd64f 100644 --- a/lib/ns-capabilities.d.ts +++ b/lib/ns-capabilities.d.ts @@ -26,6 +26,7 @@ export declare class NsCapabilities implements INsCapabilities { private _device; private _ignoreDeviceController; private _wdaLocalPort; + private _relaxedSecurity; private exceptions; constructor(); readonly path: string; @@ -53,6 +54,7 @@ export declare class NsCapabilities implements INsCapabilities { readonly wdaLocalPort: number; device: IDevice; readonly emulatorOptions: string; + readonly relaxedSecurity: boolean; private isAndroidPlatform; private setAutomationName; tryGetAndroidApiLevel(): number; diff --git a/lib/ns-capabilities.ts b/lib/ns-capabilities.ts index 1505cc7..6c833b5 100644 --- a/lib/ns-capabilities.ts +++ b/lib/ns-capabilities.ts @@ -30,6 +30,7 @@ export class NsCapabilities implements INsCapabilities { private _device: IDevice; private _ignoreDeviceController: boolean; private _wdaLocalPort: number; + private _relaxedSecurity: boolean; private exceptions: Array = new Array(); constructor() { @@ -54,6 +55,7 @@ export class NsCapabilities implements INsCapabilities { this._ignoreDeviceController = parser.ignoreDeviceController; this._wdaLocalPort = parser.wdaLocalPort; this._path = parser.path; + this._relaxedSecurity = parser.relaxedSecurity; this.setAutomationName(); this.resolveApplication(); this.checkMandatoryCapabiliies(); @@ -88,6 +90,7 @@ export class NsCapabilities implements INsCapabilities { get device() { return this._device; } set device(device: IDevice) { this._device = device; } get emulatorOptions() { return (this._emulatorOptions || "-wipe-data -gpu on") } + get relaxedSecurity() { return this._relaxedSecurity } private isAndroidPlatform() { return this._appiumCaps.platformName.toLowerCase().includes("android"); } diff --git a/lib/parser.d.ts b/lib/parser.d.ts index 62ca3bb..4d4efe0 100644 --- a/lib/parser.d.ts +++ b/lib/parser.d.ts @@ -1,2 +1,2 @@ export declare const capabilitiesName = "appium.capabilities.json"; -export declare const projectDir: any, projectBinary: any, pluginRoot: any, pluginBinary: any, port: any, verbose: any, appiumCapsLocation: any, testFolder: any, runType: any, isSauceLab: any, appPath: any, storage: any, testReports: any, reuseDevice: any, devMode: any, ignoreDeviceController: any, wdaLocalPort: any, path: any; +export declare const projectDir: any, projectBinary: any, pluginRoot: any, pluginBinary: any, port: any, verbose: any, appiumCapsLocation: any, testFolder: any, runType: any, isSauceLab: any, appPath: any, storage: any, testReports: any, reuseDevice: any, devMode: any, ignoreDeviceController: any, wdaLocalPort: any, path: any, relaxedSecurity: any; diff --git a/lib/parser.ts b/lib/parser.ts index 1c29f1e..aeeb647 100644 --- a/lib/parser.ts +++ b/lib/parser.ts @@ -14,6 +14,7 @@ const config = (() => { .option("wdaLocalPort", { alias: "wda", describe: "WDA port", default: 8410, type: "number" }) .option("verbose", { alias: "v", describe: "Log actions", type: "boolean" }) .option("path", { describe: "path", default: process.cwd(), type: "string" }) + .option("relaxedSecurity", { describe: "appium relaxedSecurity", default: false, type: "boolean" }) .option("appPath", { describe: "application path", type: "string" }) .option("storage", { describe: "Storage for images folder.", type: "string" }) .option("testReports", { describe: "Test reporting folder", type: "string" }) @@ -56,6 +57,7 @@ const config = (() => { devMode: options.devMode || process.env.npm_config_REUSE_APP, ignoreDeviceController: options.ignoreDeviceController, path: options.path, + relaxedSecurity: options.relaxedSecurity }; return config; @@ -80,4 +82,5 @@ export const { ignoreDeviceController, wdaLocalPort, path, + relaxedSecurity } = config;