Skip to content

Commit

Permalink
Merge pull request #261 from NativeScript/svetoslavtsenov/gestures
Browse files Browse the repository at this point in the history
feat: implement pinch, pan, rotate and scroll
  • Loading branch information
Zdravko authored Nov 19, 2019
2 parents 0bbb353 + 6f6edce commit 8744490
Show file tree
Hide file tree
Showing 16 changed files with 366 additions and 162 deletions.
37 changes: 26 additions & 11 deletions lib/appium-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {
encodeImageToBase64,
ensureReportsDirExists,
checkImageLogType,
adbShellCommand
adbShellCommand,
logWarn
} from "./utils";

import { INsCapabilities } from "./interfaces/ns-capabilities";
Expand Down Expand Up @@ -167,6 +168,11 @@ export class AppiumDriver {
}

public async navBack() {
if (this.isAndroid) {
logInfo("=== Navigate back with hardware button!");
} else {
logInfo("=== Navigate back.");
}
return await this._driver.back();
}

Expand Down Expand Up @@ -244,7 +250,7 @@ export class AppiumDriver {
prepareApp(args);
if (!args.device) {
if (args.isAndroid) {
args.device = DeviceManager.getDefaultDevice(args, sessionInfo.capabilities.desired.deviceName, sessionInfo.capabilities.deviceUDID.replace("emulator-", ""), sessionInfo.capabilities.deviceUDID.includes("emulator") ? DeviceType.EMULATOR : DeviceType.SIMULATOR, sessionInfo.capabilities.desired.platformVersion || sessionInfo.capabilities.platformVersion);
args.device = DeviceManager.getDefaultDevice(args, sessionInfo.capabilities.desired.deviceName, sessionInfo.capabilities.deviceUDID.replace("emulator-", ""), sessionInfo.capabilities.deviceUDID.includes("emulator") ? DeviceType.EMULATOR : DeviceType.SIMULATOR, sessionInfo.capabilities.deviceApiLevel || sessionInfo.capabilities.platformVersion);
} else {
args.device = DeviceManager.getDefaultDevice(args);
}
Expand All @@ -257,6 +263,8 @@ export class AppiumDriver {
}
} catch (error) {
args.verbose = true;
console.log("===============================");
console.log("", error)
if (!args.ignoreDeviceController && error && error.message && error.message.includes("Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]")) {
await DeviceManager.kill(args.device);
await DeviceController.startDevice(args.device);
Expand All @@ -280,11 +288,11 @@ export class AppiumDriver {
console.log("Retry launching appium driver!");
hasStarted = false;

if (error && error.message && error.message.includes("WebDriverAgent")) {
const freePort = await findFreePort(100, args.wdaLocalPort);
console.log("args.appiumCaps['wdaLocalPort']", freePort);
args.appiumCaps["wdaLocalPort"] = freePort;
}
// if (error && error.message && error.message.includes("WebDriverAgent")) {
// const freePort = await findFreePort(100, args.wdaLocalPort);
// console.log("args.appiumCaps['wdaLocalPort']", freePort);
// args.appiumCaps["wdaLocalPort"] = freePort;
// }
}

if (hasStarted) {
Expand Down Expand Up @@ -491,7 +499,7 @@ export class AppiumDriver {
* @param xOffset
*/
public async scroll(direction: Direction, y: number, x: number, yOffset: number, xOffset: number = 0) {
await scroll(this._wd, this._driver, direction, this._webio.isIOS, y, x, yOffset, xOffset, this._args.verbose);
await scroll(this._wd, this._driver, direction, this._webio.isIOS, y, x, yOffset, xOffset);
}

/**
Expand All @@ -509,13 +517,15 @@ export class AppiumDriver {
while ((el === null || !isDisplayed) && retryCount > 0) {
try {
el = await element();
isDisplayed = await el.isDisplayed();
isDisplayed = el && await el.isDisplayed();
if (!isDisplayed) {
await scroll(this._wd, this._driver, direction, this._webio.isIOS, startPoint.y, startPoint.x, offsetPoint.x, offsetPoint.y, this._args.verbose);
await scroll(this._wd, this._driver, direction, this._webio.isIOS, startPoint.y, startPoint.x, offsetPoint.y, offsetPoint.x);
el = null;
}
} catch (error) {
console.log("scrollTo Error: " + error);
await scroll(this._wd, this._driver, direction, this._webio.isIOS, startPoint.y, startPoint.x, offsetPoint.y, offsetPoint.x);
el = null;
}

retryCount--;
Expand Down Expand Up @@ -864,7 +874,7 @@ export class AppiumDriver {
}
}

private static async applyAdditionalSettings(args) {
private static async applyAdditionalSettings(args: INsCapabilities) {
if (args.isSauceLab) return;

args.appiumCaps['udid'] = args.appiumCaps['udid'] || args.device.token;
Expand All @@ -881,6 +891,11 @@ export class AppiumDriver {
args.appiumCaps["wdaStartupRetries"] = 5;
args.appiumCaps["shouldUseSingletonTestManager"] = args.appiumCaps.shouldUseSingletonTestManager;

if (args.derivedDataPath) {
args.appiumCaps["derivedDataPath"] = `${args.derivedDataPath}/${args.device.token}`;
logWarn('Changed derivedDataPath to: ', args.appiumCaps["derivedDataPath"]);
}

// It looks we need it for XCTest (iOS 10+ automation)
if (args.appiumCaps.platformVersion >= 10 && args.wdaLocalPort) {
console.log(`args.appiumCaps['wdaLocalPort']: ${args.wdaLocalPort}`);
Expand Down
2 changes: 1 addition & 1 deletion lib/appium-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class AppiumServer {

private startAppiumServer(logLevel: string, isSauceLab: boolean) {
const startingServerArgs: Array<string> = isSauceLab ? ["--log-level", logLevel] : ["-p", this.port.toString(), "--log-level", logLevel];
if (this._args.isAndroid && this._args.ignoreDeviceController && !this._args.isSauceLab) {
if (this._args.isAndroid) {
this._args.relaxedSecurity ? startingServerArgs.push("--relaxed-security") : console.log("'relaxedSecurity' is not enabled!\nTo enabled it use '--relaxedSecurity'!");
}

Expand Down
11 changes: 6 additions & 5 deletions lib/device-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export class DeviceManager implements IDeviceManager {
type: type,
platform: args.appiumCaps.platformName.toLowerCase(),
token: token,
apiLevel: platformVersion || args.appiumCaps.platformVersion,
apiLevel: platformVersion || args.appiumCaps.deviceApiLevel || args.appiumCaps.platformVersion,
config: { "density": args.appiumCaps.density, "offsetPixels": args.appiumCaps.offsetPixels }
}

Expand All @@ -197,7 +197,7 @@ export class DeviceManager implements IDeviceManager {
const sizeArr = sessionInfoDetails.deviceScreenSize.split("x");
args.device.deviceScreenSize = { width: sizeArr[0], height: sizeArr[1] };

args.device.apiLevel = sessionInfoDetails.deviceApiLevel;
args.device.apiLevel = sessionInfoDetails.deviceApiLevel || args.device.apiLevel;
args.device.deviceScreenDensity = sessionInfoDetails.deviceScreenDensity / 100;
args.device.config = { "density": args.device.deviceScreenDensity || args.device.config.density, "offsetPixels": +sessionInfoDetails.statBarHeight || args.device.config.offsetPixels };
} else {
Expand All @@ -209,6 +209,7 @@ export class DeviceManager implements IDeviceManager {

args.device.statBarHeight = sessionInfoDetails.statBarHeight;
args.device.viewportRect = DeviceManager.convertViewportRectToIRectangle(sessionInfoDetails.viewportRect);
args.device.token = args.device.token || sessionInfoDetails.udid;

return args.device;
}
Expand All @@ -217,14 +218,14 @@ export class DeviceManager implements IDeviceManager {
const status = value ? 1 : 0;
try {
if (args.isAndroid) {
if (!args.ignoreDeviceController) {
AndroidController.setDontKeepActivities(value, args.device);
} else if (args.relaxedSecurity) {
if (args.relaxedSecurity) {
const output = await DeviceManager.executeShellCommand(driver, { command: "settings", args: ['put', 'global', 'always_finish_activities', status] });
console.log(`Output from setting always_finish_activities to ${status}: ${output}`);
//check if set
const check = await DeviceManager.executeShellCommand(driver, { command: "settings", args: ['get', 'global', 'always_finish_activities'] });
console.info(`Check if always_finish_activities is set correctly: ${check}`);
} else if (!args.ignoreDeviceController) {
AndroidController.setDontKeepActivities(value, args.device);
}
} else {
// Do nothing for iOS ...
Expand Down
2 changes: 1 addition & 1 deletion lib/direction.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export declare const enum Direction {
export declare enum Direction {
down = 0,
up = 1,
left = 2,
Expand Down
2 changes: 1 addition & 1 deletion lib/direction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const enum Direction {
export enum Direction {
down,
up,
left,
Expand Down
1 change: 1 addition & 0 deletions lib/interfaces/ns-capabilities-args.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AutomationName } from "../automation-name";
import { ITestReporter } from "./test-reporter";
import { LogImageType } from "../enums/log-image-type";
export interface INsCapabilitiesArgs {
derivedDataPath?: string;
port?: number;
wdaLocalPort?: number;
projectDir?: string;
Expand Down
1 change: 1 addition & 0 deletions lib/interfaces/ns-capabilities-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ITestReporter } from "./test-reporter";
import { LogImageType } from "../enums/log-image-type";

export interface INsCapabilitiesArgs {
derivedDataPath?: string;
port?: number;
wdaLocalPort?: number;
projectDir?: string;
Expand Down
1 change: 1 addition & 0 deletions lib/ns-capabilities.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export declare class NsCapabilities implements INsCapabilities {
deviceTypeOrPlatform: string;
driverConfig: any;
logImageTypes: Array<LogImageType>;
derivedDataPath: string;
constructor(_parser: INsCapabilitiesArgs);
readonly isAndroid: any;
readonly isIOS: boolean;
Expand Down
2 changes: 2 additions & 0 deletions lib/ns-capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class NsCapabilities implements INsCapabilities {
public deviceTypeOrPlatform: string;
public driverConfig: any;
public logImageTypes: Array<LogImageType>;
public derivedDataPath: string;

constructor(private _parser: INsCapabilitiesArgs) {
this.projectDir = this._parser.projectDir;
Expand All @@ -76,6 +77,7 @@ export class NsCapabilities implements INsCapabilities {
this.isSauceLab = this._parser.isSauceLab;
this.ignoreDeviceController = this._parser.ignoreDeviceController;
this.wdaLocalPort = this._parser.wdaLocalPort;
this.derivedDataPath = this._parser.derivedDataPath;
this.path = this._parser.path;
this.capabilitiesName = this._parser.capabilitiesName;
this.imagesPath = this._parser.imagesPath;
Expand Down
2 changes: 1 addition & 1 deletion lib/parser.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import { LogImageType } from "./enums/log-image-type";
export declare const projectDir: string, projectBinary: string, pluginRoot: string, pluginBinary: string, port: number, verbose: boolean, appiumCapsLocation: string, testFolder: string, runType: string, isSauceLab: boolean, appPath: string, storage: string, testReports: string, devMode: boolean, ignoreDeviceController: boolean, wdaLocalPort: number, path: string, relaxedSecurity: boolean, cleanApp: boolean, attachToDebug: boolean, sessionId: string, startSession: boolean, capabilitiesName: string, imagesPath: string, startDeviceOptions: string, deviceTypeOrPlatform: string, device: any, driverConfig: any, logImageTypes: LogImageType[], appiumCaps: any;
export declare const projectDir: string, projectBinary: string, pluginRoot: string, pluginBinary: string, port: number, verbose: boolean, appiumCapsLocation: string, testFolder: string, runType: string, isSauceLab: boolean, appPath: string, storage: string, testReports: string, devMode: boolean, ignoreDeviceController: boolean, wdaLocalPort: number, derivedDataPath: string, path: string, relaxedSecurity: boolean, cleanApp: boolean, attachToDebug: boolean, sessionId: string, startSession: boolean, capabilitiesName: string, imagesPath: string, startDeviceOptions: string, deviceTypeOrPlatform: string, device: any, driverConfig: any, logImageTypes: LogImageType[], appiumCaps: any;
5 changes: 5 additions & 0 deletions lib/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const config = (() => {
type: "string"
})
.option("wdaLocalPort", { alias: "wda", describe: "WDA port", type: "number" })
.options("derivedDataPath", {
describe: "set the unique derived data path root for each driver instance. This will help to avoid possible conflicts and to speed up the parallel execution",
type: "string" })
.option("verbose", { alias: "v", describe: "Log actions", type: "boolean" })
.option("path", { describe: "Execution path", default: process.cwd(), type: "string" })
.option("relaxedSecurity", { describe: "appium relaxedSecurity", default: false, type: "boolean" })
Expand Down Expand Up @@ -160,6 +163,7 @@ const config = (() => {
pluginRoot: pluginRoot,
pluginBinary: pluginBinary,
wdaLocalPort: options.wdaLocalPort || process.env.npm_config_wdaLocalPort || process.env["WDA_LOCAL_PORT"] || 8410,
derivedDataPath: options.derivedDataPath || process.env.npm_config_derivedDataPath || process.env["DERIVED_DATA_PATH"],
testFolder: options.testFolder || process.env.npm_config_testFolder || "e2e",
runType: options.runType || process.env.npm_config_runType,
appiumCapsLocation: options.appiumCapsLocation || process.env.npm_config_appiumCapsLocation || join(projectDir, options.testFolder, "config", options.capabilitiesName),
Expand Down Expand Up @@ -208,6 +212,7 @@ export const {
devMode,
ignoreDeviceController,
wdaLocalPort,
derivedDataPath,
path,
relaxedSecurity,
cleanApp,
Expand Down
69 changes: 60 additions & 9 deletions lib/ui-element.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export declare class UIElement {
* Click on element
*/
click(): Promise<any>;
getCenter(): Promise<{
x: number;
y: number;
}>;
tapCenter(): Promise<void>;
tapAtTheEnd(): Promise<void>;
/**
Expand All @@ -26,17 +30,25 @@ export declare class UIElement {
*/
tap(): Promise<any>;
/**
* @experimental
* Double tap on element
*/
doubleTap(): Promise<any>;
doubleTap(offset?: {
x: number;
y: number;
}): Promise<any>;
longPress(duration: number): Promise<void>;
/**
* Get location of element
*/
location(): Promise<Point>;
/**
* Get size of element
*/
size(): Promise<Point>;
size(): Promise<{
width: number;
height: number;
}>;
/**
* Get text of element
*/
Expand Down Expand Up @@ -113,13 +125,6 @@ export declare class UIElement {
*/
scrollTo(direction: Direction, elementToSearch: () => Promise<UIElement>, yOffset?: number, xOffset?: number, retries?: number): Promise<UIElement>;
/**
* Drag element with specific offset
* @param direction
* @param yOffset
* @param xOffset - default value 0
*/
drag(direction: Direction, yOffset: number, xOffset?: number): Promise<void>;
/**
* Click and hold over an element
* @param time in milliseconds to increase the default press period.
*/
Expand Down Expand Up @@ -165,4 +170,50 @@ export declare class UIElement {
* @param direction
*/
swipe(direction: Direction): Promise<void>;
/**
* Drag element with specific offset
* @experimental
* @param direction
* @param yOffset
* @param xOffset - default value 0
*/
drag(direction: Direction, yOffset: number, xOffset?: number, duration?: number): Promise<void>;
/**
*@experimental
* Pan element with specific offset
* @param offsets where the finger should move to.
* @param initPointOffset element.getRectangle() is used as start point. In case some additional offset should be provided use this param.
*/
pan(offsets: {
x: number;
y: number;
}[], initPointOffset?: {
x: number;
y: number;
}): Promise<void>;
/**
* @experimental
* This method will try to move two fingers from opposite corners.
* One finger starts from
* { x: elementRect.x + offset.x, y: elementRect.y + offset.y }
* and ends to
* { x: elementRect.x + elementRect.width - offset.x, y: elementRect.height + elementRect.y - offset.y }
* and the other finger starts from
* { x: elementRect.width + elementRect.x - offset.x, y: elementRect.height + elementRect.y - offset.y }
* and ends to
* { x: elementRect.x + offset.x, y: elementRect.y + offset.y }
*/
rotate(offset?: {
x: number;
y: number;
}): Promise<void>;
/**
* @experimental
* @param zoomFactory - zoomIn or zoomOut. Only zoomIn action is implemented
* @param offset
*/
pinch(zoomType: "in" | "out", offset?: {
x: number;
y: number;
}): Promise<void>;
}
Loading

0 comments on commit 8744490

Please sign in to comment.