diff --git a/lib/appium-driver.ts b/lib/appium-driver.ts index f4615cc..ba52eec 100644 --- a/lib/appium-driver.ts +++ b/lib/appium-driver.ts @@ -499,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); + await scroll(this._wd, this._driver, direction, this._args, y, x, yOffset, xOffset); } /** @@ -519,12 +519,12 @@ export class AppiumDriver { el = await element(); isDisplayed = el && await el.isDisplayed(); if (!isDisplayed) { - await scroll(this._wd, this._driver, direction, this._webio.isIOS, startPoint.y, startPoint.x, offsetPoint.y, offsetPoint.x); + await scroll(this._wd, this._driver, direction, this._args, 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); + await scroll(this._wd, this._driver, direction, this._args, startPoint.y, startPoint.x, offsetPoint.y, offsetPoint.x); el = null; } diff --git a/lib/ui-element.ts b/lib/ui-element.ts index 62a37fa..a2b19ee 100644 --- a/lib/ui-element.ts +++ b/lib/ui-element.ts @@ -2,8 +2,9 @@ import { Point } from "./point"; import { Direction } from "./direction"; import { INsCapabilities } from "./interfaces/ns-capabilities"; import { AutomationName } from "./automation-name"; -import { calculateOffset, adbShellCommand, logError, wait, logInfo } from "./utils"; +import { calculateOffset, adbShellCommand, logError, wait, logInfo, hasNotch } from "./utils"; import { AndroidKeyEvent } from "mobile-devices-controller"; +import { isNumber } from "util"; export class UIElement { private static readonly DEFAULT_REFETCH_TIME = 1000; @@ -305,7 +306,18 @@ export class UIElement { location.x += xOffset; } if (yOffset === 0) { - yOffset = location.y + size.height - 5; + const verticalEnd = location.y + size.height; + yOffset = verticalEnd - 5; + if (hasNotch(this._args.device.name) && isNumber(this._args.device.viewportRect.height) && (verticalEnd * this._args.device.deviceScreenDensity >= this._args.device.viewportRect.height)) { + const notchHeight = 35 + if (size.height > notchHeight) { + yOffset = yOffset - 30; // 30 is enough to skip the bottom notch + } + + if (direction === Direction.down && xOffset < 50 && (location.x + size.width) > 50) { // If on the bottom on device with notch 'x' should be >=50. Otherwise the app is minimized + location.x += 50 - xOffset; + } + } } } @@ -318,7 +330,7 @@ export class UIElement { } } - const endPoint = calculateOffset(direction, location.y, yOffset, location.x, xOffset, this._args.isIOS); + const endPoint = calculateOffset(direction, location.y, yOffset, location.x, xOffset, this._args); const action = new this._wd.TouchAction(this._driver); action @@ -499,7 +511,7 @@ export class UIElement { const x = location.x === 0 ? 10 : location.x; const y = location.y === 0 ? 10 : location.y; - const endPoint = calculateOffset(direction, y, yOffset, x, xOffset, this._args.isIOS); + const endPoint = calculateOffset(direction, y, yOffset, x, xOffset, this._args); duration = duration || endPoint.duration; if (this._args.isAndroid) { diff --git a/lib/utils.d.ts b/lib/utils.d.ts index 2cd64f7..0d5f4a5 100644 --- a/lib/utils.d.ts +++ b/lib/utils.d.ts @@ -21,7 +21,7 @@ export declare const getStorage: (args: INsCapabilities) => string; export declare function getReportPath(args: INsCapabilities): string; export declare const getRegexResultsAsArray: (regex: any, str: any) => any[]; export declare function getAppPath(caps: INsCapabilities): string; -export declare function calculateOffset(direction: any, y: number, yOffset: number, x: number, xOffset: number, isIOS: boolean): { +export declare function calculateOffset(direction: any, y: number, yOffset: number, x: number, xOffset: number, args: INsCapabilities): { startPoint: Point; endPoint: Point; duration: number; @@ -33,7 +33,7 @@ export declare function calculateOffset(direction: any, y: number, yOffset: numb * @param yOffset * @param xOffset */ -export declare function scroll(wd: any, driver: any, direction: Direction, isIOS: boolean, y: number, x: number, yOffset: number, xOffset: number): Promise; +export declare function scroll(wd: any, driver: any, direction: Direction, args: INsCapabilities, y: number, x: number, yOffset: number, xOffset: number): Promise; export declare const addExt: (fileName: string, ext: string) => string; export declare const isPortAvailable: (port: any) => Promise<{}>; export declare const findFreePort: (retries?: number, port?: number) => Promise; @@ -53,6 +53,7 @@ export declare function logError(info: any, obj?: any): void; export declare function log(message: any, verbose: any): void; export declare const logColorized: (bgColor: ConsoleColor, frontColor: ConsoleColor, info: any) => void; export declare function adbShellCommand(wd: any, command: string, args: Array): Promise; +export declare function hasNotch(device: string): boolean; declare enum ConsoleColor { Reset = "\u001B[0m", Bright = "\u001B[1m", diff --git a/lib/utils.ts b/lib/utils.ts index 75985e3..166b70f 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -381,19 +381,26 @@ export function getAppPath(caps: INsCapabilities) { return appFullPath; } -export function calculateOffset(direction, y: number, yOffset: number, x: number, xOffset: number, isIOS: boolean) { +export function calculateOffset(direction, y: number, yOffset: number, x: number, xOffset: number, args: INsCapabilities) { let speed = 10; let yEnd = y; let xEnd = x; let duration = Math.abs(yEnd) * speed; if (direction === Direction.down) { - yEnd = Math.abs(y); - y = Math.abs(yOffset - y); + yEnd = y == 0 ? Math.abs(y + args.device.statBarHeight) : Math.abs(y); + y = Math.abs(yOffset); duration = Math.abs(yOffset) * speed; } if (direction === Direction.up) { - yEnd = Math.abs((Math.abs(y - yOffset))); + const actionbarHeight = 50; + if (y == 0) { + y = args.device.statBarHeight + y; + if (y + actionbarHeight < yOffset) { + y = y + actionbarHeight; + } + } + yEnd = Math.abs((Math.abs(yOffset))); duration = Math.abs(yOffset) * speed; } @@ -406,8 +413,8 @@ export function calculateOffset(direction, y: number, yOffset: number, x: number if (direction === Direction.left) { xEnd = Math.abs(xOffset + x); duration = Math.abs(xOffset) * speed; - const addToX = isIOS ? 50 : 5; - if (isIOS) { + const addToX = args.isIOS ? 50 : 5; + if (args.isIOS) { x = x === 0 ? 50 : x; } else { x = x === 0 ? 5 : x; @@ -439,14 +446,14 @@ export function calculateOffset(direction, y: number, yOffset: number, x: number * @param yOffset * @param xOffset */ -export async function scroll(wd, driver, direction: Direction, isIOS: boolean, y: number, x: number, yOffset: number, xOffset: number) { +export async function scroll(wd, driver, direction: Direction, args: INsCapabilities, y: number, x: number, yOffset: number, xOffset: number) { if (x === 0) { x = 20; } if (y === 0) { y = 20; } - const endPoint = calculateOffset(direction, y, yOffset, x, xOffset, isIOS); + const endPoint = calculateOffset(direction, y, yOffset, x, xOffset, args); const action = new wd.TouchAction(driver); action .press({ x: x, y: y }) @@ -699,6 +706,11 @@ export async function adbShellCommand(wd: any, command: string, args: Array await wd.execute('mobile: shell', { "command": command, "args": args }); } +export function hasNotch(device: string): boolean { + const notchDevices = ["iPhone X", "iPhone 11"]; + return notchDevices.some(notchDevice => device.includes(notchDevice)); +} + enum ConsoleColor { Reset = "\x1b[0m", Bright = "\x1b[1m",