com.zebrunner.carina.utils.mobile.IMobileUtils Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.zebrunner.carina.utils.mobile;
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.time.Duration;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.DeviceRotation;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.Point;
import org.openqa.selenium.ScreenOrientation;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.html5.Location;
import org.openqa.selenium.interactions.Interactive;
import org.openqa.selenium.interactions.Pause;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import com.zebrunner.carina.utils.android.AndroidService;
import com.zebrunner.carina.utils.android.DeviceTimeZone;
import com.zebrunner.carina.utils.android.IAndroidUtils;
import com.zebrunner.carina.utils.commons.SpecialKeywords;
import com.zebrunner.carina.utils.config.Configuration;
import com.zebrunner.carina.utils.messager.Messager;
import com.zebrunner.carina.webdriver.DriverHelper;
import com.zebrunner.carina.webdriver.IDriverPool;
import com.zebrunner.carina.webdriver.config.WebDriverConfiguration;
import com.zebrunner.carina.webdriver.decorator.ExtendedWebElement;
import io.appium.java_client.HasAppStrings;
import io.appium.java_client.HasDeviceTime;
import io.appium.java_client.HasOnScreenKeyboard;
import io.appium.java_client.HasSettings;
import io.appium.java_client.HidesKeyboard;
import io.appium.java_client.InteractsWithApps;
import io.appium.java_client.LocksDevice;
import io.appium.java_client.PullsFiles;
import io.appium.java_client.PushesFiles;
import io.appium.java_client.Setting;
import io.appium.java_client.SupportsLegacyAppManagement;
import io.appium.java_client.appmanagement.ApplicationState;
import io.appium.java_client.appmanagement.BaseActivateApplicationOptions;
import io.appium.java_client.appmanagement.BaseInstallApplicationOptions;
import io.appium.java_client.appmanagement.BaseRemoveApplicationOptions;
import io.appium.java_client.appmanagement.BaseTerminateApplicationOptions;
import io.appium.java_client.clipboard.ClipboardContentType;
import io.appium.java_client.clipboard.HasClipboard;
import io.appium.java_client.remote.SupportsContextSwitching;
import io.appium.java_client.remote.SupportsLocation;
import io.appium.java_client.remote.SupportsRotation;
import io.appium.java_client.screenrecording.BaseStartScreenRecordingOptions;
import io.appium.java_client.screenrecording.BaseStopScreenRecordingOptions;
import io.appium.java_client.screenrecording.CanRecordScreen;
/**
* Contains utility methods for working with android and ios
*/
public interface IMobileUtils extends IDriverPool {
Logger UTILS_LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
// TODO: [VD] make private after migration to java 9+
/**
* @deprecated this constant is not used in IMobileUtils
*/
@Deprecated(forRemoval = true, since = "8.x")
long EXPLICIT_TIMEOUT = Configuration.getRequired(WebDriverConfiguration.Parameter.EXPLICIT_TIMEOUT, Long.class);
/**
* @deprecated this constant is not used in IMobileUtils
*/
@Deprecated(forRemoval = true, since = "8.x")
int MINIMUM_TIMEOUT = 2;
int DEFAULT_TOUCH_ACTION_DURATION = 1000;
int DEFAULT_MAX_SWIPE_COUNT = 50;
int DEFAULT_MIN_SWIPE_COUNT = 1;
enum Direction {
LEFT,
RIGHT,
UP,
DOWN,
VERTICAL,
HORIZONTAL,
VERTICAL_DOWN_FIRST,
HORIZONTAL_RIGHT_FIRST
}
enum Zoom {
IN,
OUT
}
/**
* Tap the center of element
*
* @param element Element to touch
*/
default void tap(ExtendedWebElement element) {
UTILS_LOGGER.info("tap on {}", element.getName());
Point point = element.getLocation();
Dimension size = element.getSize();
tap(point.getX() + size.getWidth() / 2, point.getY() + size.getHeight() / 2);
}
/**
* Tap by coordinates with default 1000ms duration
*
* @param startx x coordinate
* @param starty y coordinate
*/
default void tap(int startx, int starty) {
tap(startx, starty, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Tap slowly to imitate log tap on element
*
* @param elem Element to long tap
*/
default void longTap(ExtendedWebElement elem) {
UTILS_LOGGER.info("Long tap on {} element", elem.getName());
Dimension size = elem.getSize();
int width = size.getWidth();
int height = size.getHeight();
Point point = elem.getLocation();
int x = point.getX() + width / 2;
int y = point.getY() + height / 2;
try {
swipe(x, y, x, y, 2500);
} catch (WebDriverException e) {
UTILS_LOGGER.error("Exception when call longTap method: ", e);
}
}
/**
* Tap and Hold (LongPress) on element
*
* @param element Element to long press
* @return is long press successful
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean longPress(ExtendedWebElement element) {
boolean isActionSuccessful = false;
// todo change this value to optimal or try to found constant
final Duration longPressDuration = Duration.ofMillis(1000);
UTILS_LOGGER.info("longPress on {}", element.getName());
// TODO: SZ migrate to FluentWaits
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Point elementLocation = element.getLocation();
Sequence longPressSequence = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), elementLocation.getX(),
elementLocation.getY()))
.addAction(finger.createPointerDown(LEFT.asArg()))
.addAction(new Pause(finger, longPressDuration))
.addAction(finger.createPointerUp(LEFT.asArg()));
Interactive driver = null;
try {
driver = (Interactive) getDriver();
driver.perform(List.of(longPressSequence));
isActionSuccessful = true;
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support longPress method", e);
} catch (WebDriverException e) {
UTILS_LOGGER.info("Error occurs during longPress: " + e, e);
}
return isActionSuccessful;
}
/**
* Tap by coordinates with custom duration
*
* @param startx x coordinate
* @param starty y coordinate
* @param duration touch hold time
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void tap(int startx, int starty, int duration) {
// TODO: add Screenshot.capture()
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence tapSequence = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), startx, starty))
.addAction(finger.createPointerDown(LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(duration)))
.addAction(finger.createPointerUp(LEFT.asArg()));
Interactive driver = null;
try {
driver = (Interactive) getDriver();
driver.perform(List.of(tapSequence));
Messager.TAP_EXECUTED.info(String.valueOf(startx), String.valueOf(starty));
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support tap method", e);
} catch (WebDriverException e) {
Messager.TAP_NOT_EXECUTED.error(String.valueOf(startx), String.valueOf(starty));
throw e;
}
}
/**
* swipe till element
*
* @param element ExtendedWebElement
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(final ExtendedWebElement element) {
return swipe(element, null, Direction.UP, DEFAULT_MAX_SWIPE_COUNT, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Swipe till element
*
* @param element ExtendedWebElement
* @param count int
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(final ExtendedWebElement element, int count) {
return swipe(element, null, Direction.UP, count, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* swipe till element
*
* @param element ExtendedWebElement
* @param direction Direction
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(final ExtendedWebElement element, Direction direction) {
return swipe(element, null, direction, DEFAULT_MAX_SWIPE_COUNT, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* swipe till element
*
* @param element ExtendedWebElement
* @param count int
* @param duration int
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(final ExtendedWebElement element, int count, int duration) {
return swipe(element, null, Direction.UP, count, duration);
}
/**
* swipe till element
*
* @param element ExtendedWebElement
* @param direction Direction
* @param count int
* @param duration int
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(final ExtendedWebElement element, Direction direction, int count, int duration) {
return swipe(element, null, direction, count, duration);
}
/**
* Swipe inside container in default direction - Direction.UP
* Number of attempts is limited by count argument
*
*
* @param element ExtendedWebElement
* @param container ExtendedWebElement
* @param count int
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(ExtendedWebElement element, ExtendedWebElement container, int count) {
return swipe(element, container, Direction.UP, count, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Swipe inside container in default direction - Direction.UP
* Number of attempts is limited by 5
*
*
* @param element ExtendedWebElement
* @param container ExtendedWebElement
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(ExtendedWebElement element, ExtendedWebElement container) {
return swipe(element, container, Direction.UP, DEFAULT_MAX_SWIPE_COUNT, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Swipe inside container in specified direction
* Number of attempts is limited by 5
*
*
* @param element ExtendedWebElement
* @param container ExtendedWebElement
* @param direction Direction
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(ExtendedWebElement element, ExtendedWebElement container, Direction direction) {
return swipe(element, container, direction, DEFAULT_MAX_SWIPE_COUNT, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Swipe inside container in specified direction with default pulling timeout in 1000ms
* Number of attempts is limited by count argument
*
*
* @param element ExtendedWebElement
* @param container ExtendedWebElement
* @param direction Direction
* @param count int
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(ExtendedWebElement element, ExtendedWebElement container, Direction direction,
int count) {
return swipe(element, container, direction, count, DEFAULT_TOUCH_ACTION_DURATION);
}
/**
* Swipe to element inside container in specified direction while element
* will not be present on the screen. If element is on the screen already,
* scrolling will not be performed.
*
*
* @param element
* element to which it will be scrolled
* @param container
* element, inside which scrolling is expected. null to scroll
* @param direction
* direction of scrolling. HORIZONTAL and VERTICAL support swiping in both directions automatically
* @param count
* for how long to scroll, ms
* @param duration
* pulling timeout, ms
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean swipe(ExtendedWebElement element, ExtendedWebElement container, Direction direction,
int count, int duration) {
boolean isVisible = element.isVisible(1);
if (isVisible) {
// no sense to continue;
UTILS_LOGGER.info("element already present before swipe: {}", element.getNameWithLocator());
return true;
} else {
UTILS_LOGGER.info("swiping to element: {}", element.getNameWithLocator());
}
Direction oppositeDirection = null;
boolean bothDirections = false;
switch (direction) {
case UP:
oppositeDirection = Direction.DOWN;
break;
case DOWN:
oppositeDirection = Direction.UP;
break;
case LEFT:
oppositeDirection = Direction.RIGHT;
break;
case RIGHT:
oppositeDirection = Direction.LEFT;
break;
case HORIZONTAL:
direction = Direction.LEFT;
oppositeDirection = Direction.RIGHT;
bothDirections = true;
break;
case HORIZONTAL_RIGHT_FIRST:
direction = Direction.RIGHT;
oppositeDirection = Direction.LEFT;
bothDirections = true;
break;
case VERTICAL:
direction = Direction.UP;
oppositeDirection = Direction.DOWN;
bothDirections = true;
break;
case VERTICAL_DOWN_FIRST:
direction = Direction.DOWN;
oppositeDirection = Direction.UP;
bothDirections = true;
break;
default:
throw new RuntimeException("Unsupported direction for swipeInContainerTillElement: " + direction);
}
int currentCount = count;
while (!isVisible && currentCount-- > 0) {
UTILS_LOGGER.debug("Element not present! Swipe {} will be executed to element: {}", direction, element.getNameWithLocator());
swipeInContainer(container, direction, duration);
UTILS_LOGGER.info("Swipe was executed. Attempts remain: {}", currentCount);
isVisible = element.isVisible(1);
}
currentCount = count;
while (bothDirections && !isVisible && currentCount-- > 0) {
UTILS_LOGGER.debug(
"Element not present! Swipe {} will be executed to element: {}", oppositeDirection, element.getNameWithLocator());
swipeInContainer(container, oppositeDirection, duration);
UTILS_LOGGER.info("Swipe was executed. Attempts remain: {}", currentCount);
isVisible = element.isVisible(1);
}
UTILS_LOGGER.info("Result: {}", isVisible);
return isVisible;
}
/**
* Swipe by coordinates
*
* @param startx int
* @param starty int
* @param endx int
* @param endy int
* @param duration int Millis
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void swipe(int startx, int starty, int endx, int endy, int duration) {
UTILS_LOGGER.debug("Starting swipe...");
WebDriver drv = getDriver();
UTILS_LOGGER.debug("Getting driver dimension size...");
Dimension scrSize = drv.manage().window().getSize();
UTILS_LOGGER.debug("Finished driver dimension size...");
// explicitly limit range of coordinates
if (endx >= scrSize.width) {
UTILS_LOGGER.warn("endx coordinate is bigger then device width! It will be limited!");
endx = scrSize.width - 1;
} else {
endx = Math.max(1, endx);
}
if (endy >= scrSize.height) {
UTILS_LOGGER.warn("endy coordinate is bigger then device height! It will be limited!");
endy = scrSize.height - 1;
} else {
endy = Math.max(1, endy);
}
UTILS_LOGGER.debug("startx: {}; starty: {}; endx: {}; endy: {}; duration: {}", startx, starty, endx, endy, duration);
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence swipe = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), startx, starty))
.addAction(finger.createPointerDown(LEFT.asArg()))
.addAction(finger.createPointerMove(Duration.ofMillis(duration), PointerInput.Origin.viewport(), endx, endy))
.addAction(finger.createPointerUp(LEFT.asArg()));
Interactive driver = null;
try {
driver = (Interactive) drv;
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support swipe method", e);
}
driver.perform(List.of(swipe));
UTILS_LOGGER.debug("Finished swipe...");
}
/**
* swipeInContainer
*
* @param container ExtendedWebElement
* @param direction Direction
* @param duration int
* @return boolean
*/
default boolean swipeInContainer(ExtendedWebElement container, Direction direction, int duration) {
return swipeInContainer(container, direction, DEFAULT_MIN_SWIPE_COUNT, duration);
}
/**
* swipeInContainer
*
* @param container ExtendedWebElement
* @param direction Direction
* @param count int
* @param duration int
* @return boolean
*/
default boolean swipeInContainer(ExtendedWebElement container, Direction direction, int count, int duration) {
int startx = 0;
int starty = 0;
int endx = 0;
int endy = 0;
Point elementLocation = null;
Dimension elementDimensions = null;
if (container == null) {
// whole screen/driver is a container!
WebDriver driver = getDriver();
elementLocation = new Point(0, 0); // initial left corner for that case
elementDimensions = driver.manage().window().getSize();
} else {
if (container.isElementNotPresent(5)) {
Assert.fail("Cannot swipe! Impossible to find element " + container.getName());
}
elementLocation = container.getLocation();
elementDimensions = container.getSize();
}
double minCoefficient = 0.3;
double maxCoefficient = 0.6;
// calculate default coefficient based on OS type
String os = IDriverPool.getDefaultDevice().getOs();
if (os.equalsIgnoreCase(SpecialKeywords.ANDROID)) {
minCoefficient = 0.25;
maxCoefficient = 0.5;
} else if (os.equalsIgnoreCase(SpecialKeywords.IOS) || os.equalsIgnoreCase(SpecialKeywords.MAC) || os.equalsIgnoreCase(SpecialKeywords.TVOS)) {
minCoefficient = 0.25;
maxCoefficient = 0.8;
}
switch (direction) {
case LEFT:
starty = endy = elementLocation.getY() + Math.round(elementDimensions.getHeight() / 2f);
startx = (int) (elementLocation.getX() + Math.round(maxCoefficient * elementDimensions.getWidth()));
endx = (int) (elementLocation.getX() + Math.round(minCoefficient * elementDimensions.getWidth()));
break;
case RIGHT:
starty = endy = elementLocation.getY() + Math.round(elementDimensions.getHeight() / 2f);
startx = (int) (elementLocation.getX() + Math.round(minCoefficient * elementDimensions.getWidth()));
endx = (int) (elementLocation.getX() + Math.round(maxCoefficient * elementDimensions.getWidth()));
break;
case UP:
startx = endx = elementLocation.getX() + Math.round(elementDimensions.getWidth() / 2f);
starty = (int) (elementLocation.getY() + Math.round(maxCoefficient * elementDimensions.getHeight()));
endy = (int) (elementLocation.getY() + Math.round(minCoefficient * elementDimensions.getHeight()));
break;
case DOWN:
startx = endx = elementLocation.getX() + Math.round(elementDimensions.getWidth() / 2f);
starty = (int) (elementLocation.getY() + Math.round(minCoefficient * elementDimensions.getHeight()));
endy = (int) (elementLocation.getY() + Math.round(maxCoefficient * elementDimensions.getHeight()));
break;
default:
throw new RuntimeException("Unsupported direction: " + direction);
}
UTILS_LOGGER.debug("Swipe from (X = {}; Y = {}) to (X = {}; Y = {})", startx, starty, endx, endy);
try {
for (int i = 0; i < count; ++i) {
swipe(startx, starty, endx, endy, duration);
}
return true;
} catch (Exception e) {
UTILS_LOGGER.error(String.format("Error during Swipe from (X = %d; Y = %d) to (X = %d; Y = %d): %s", startx, starty, endx, endy, e));
}
return false;
}
/**
* Swipe up several times
*
* @param times int
* @param duration int
*/
default void swipeUp(final int times, final int duration) {
for (int i = 0; i < times; i++) {
swipeUp(duration);
}
}
/**
* Swipe up
*
* @param duration int
*/
default void swipeUp(final int duration) {
UTILS_LOGGER.info("Swipe up will be executed.");
swipeInContainer(null, Direction.UP, duration);
}
/**
* Swipe down several times
*
* @param times int
* @param duration int
*/
default void swipeDown(final int times, final int duration) {
for (int i = 0; i < times; i++) {
swipeDown(duration);
}
}
/**
* Swipe down
*
* @param duration int
*/
default void swipeDown(final int duration) {
UTILS_LOGGER.info("Swipe down will be executed.");
swipeInContainer(null, Direction.DOWN, duration);
}
/**
* Swipe left several times
*
* @param times int
* @param duration int
*/
default void swipeLeft(final int times, final int duration) {
for (int i = 0; i < times; i++) {
swipeLeft(duration);
}
}
/**
* Swipe left
*
* @param duration int
*/
default void swipeLeft(final int duration) {
UTILS_LOGGER.info("Swipe left will be executed.");
swipeLeft(null, duration);
}
/**
* Swipe left in container
*
* @param container ExtendedWebElement
* @param duration int
*/
default void swipeLeft(ExtendedWebElement container, final int duration) {
UTILS_LOGGER.info("Swipe left will be executed.");
swipeInContainer(container, Direction.LEFT, duration);
}
/**
* Swipe right several times
*
* @param times int
* @param duration int
*/
default void swipeRight(final int times, final int duration) {
for (int i = 0; i < times; i++) {
swipeRight(duration);
}
}
/**
* Swipe right
*
* @param duration int
*/
default void swipeRight(final int duration) {
UTILS_LOGGER.info("Swipe right will be executed.");
swipeRight(null, duration);
}
/**
* Swipe right in container
*
* @param container ExtendedWebElement
* @param duration int
*/
default void swipeRight(ExtendedWebElement container, final int duration) {
UTILS_LOGGER.info("Swipe right will be executed.");
swipeInContainer(container, Direction.RIGHT, duration);
}
/**
* Set Android Device Default TimeZone And Language based on config or to GMT and En
* Without restoring actual focused apk.
*
* @deprecated IMobileUtils should contains only methods for Android and IOS, so use
* {@link IAndroidUtils#setDeviceDefaultTimeZoneLanguage()}
*/
@Deprecated(forRemoval = true, since = "8.x")
default void setDeviceDefaultTimeZoneAndLanguage() {
setDeviceDefaultTimeZoneAndLanguage(false);
}
/**
* Set default TimeZone And Language based on config or to GMT and En
*
* @param returnAppFocus - if true store actual Focused apk and activity, than restore after setting Timezone and Language.
* @deprecated IMobileUtils should contains only methods for Android and IOS, so use
* {@link IAndroidUtils#setDeviceDefaultTimeZoneLanguage(boolean)}
*/
@Deprecated(forRemoval = true, since = "8.x")
default void setDeviceDefaultTimeZoneAndLanguage(boolean returnAppFocus) {
try {
String baseApp = "";
String os = IDriverPool.getDefaultDevice().getOs();
if (os.equalsIgnoreCase(SpecialKeywords.ANDROID)) {
AndroidService androidService = AndroidService.getInstance();
if (returnAppFocus) {
baseApp = androidService.getCurrentFocusedApkDetails();
}
DeviceTimeZone.TimeZoneFormat timeZone = Configuration.get(WebDriverConfiguration.Parameter.DEFAULT_DEVICE_TIMEZONE)
.map(DeviceTimeZone.TimeZoneFormat::parse).orElse(DeviceTimeZone.TimeZoneFormat.GMT);
DeviceTimeZone.TimeFormat timeFormat = Configuration.get(WebDriverConfiguration.Parameter.DEFAULT_DEVICE_TIME_FORMAT)
.map(DeviceTimeZone.TimeFormat::parse).orElse(DeviceTimeZone.TimeFormat.FORMAT_12);
String deviceLanguage = Configuration.get(WebDriverConfiguration.Parameter.DEFAULT_DEVICE_LANGUAGE).orElse("en_US");
UTILS_LOGGER.info("Set device timezone to {}", timeZone);
UTILS_LOGGER.info("Set device time format to {}", timeFormat);
UTILS_LOGGER.info("Set device language to {}", deviceLanguage);
boolean timeZoneChanged = androidService.setDeviceTimeZone(timeZone.getTimeZone(), timeZone.getSettingsTZ(), timeFormat);
boolean languageChanged = androidService.setDeviceLanguage(deviceLanguage);
UTILS_LOGGER.info("Device TimeZone was changed to timeZone '{}' : {}. Device Language was changed to language '{}': {}",
timeZone, timeZoneChanged, deviceLanguage, languageChanged);
if (returnAppFocus) {
androidService.openApp(baseApp);
}
} else {
UTILS_LOGGER.info("Current OS is {}. But we can set default TimeZone and Language only for Android.", os);
}
} catch (Exception e) {
UTILS_LOGGER.error("Error while setting to device default timezone and language!", e);
}
}
/**
* Hides the keyboard if it is showing
*
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void hideKeyboard() {
HidesKeyboard driver = null;
try {
driver = (HidesKeyboard) getDriver();
driver.hideKeyboard();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support hideKeyboard method", e);
} catch (Exception e) {
if (!e.getMessage().contains("Soft keyboard not present, cannot hide keyboard")) {
UTILS_LOGGER.error("Exception appears during hideKeyboard: ", e);
}
}
}
/**
* Check if keyboard is showing
*
* @return true if keyboard is displayed. False otherwise
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean isKeyboardShown() {
HasOnScreenKeyboard driver = null;
try {
driver = (HasOnScreenKeyboard) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support isKeyboardShown method", e);
}
return driver.isKeyboardShown();
}
default void zoom(Zoom type) {
UTILS_LOGGER.info("Zoom will be performed :{}", type);
WebDriver driver = getDriver();
Dimension scrSize = driver.manage().window().getSize();
int height = scrSize.getHeight();
int width = scrSize.getWidth();
UTILS_LOGGER.debug("Screen height : {}", height);
UTILS_LOGGER.debug("Screen width : {}", width);
Point point1 = new Point(width / 2, height / 2 - 30);
Point point2 = new Point(width / 2, height / 10 * 3);
Point point3 = new Point(width / 2, height / 2 + 30);
Point point4 = new Point(width / 2, (7 * height) / 10);
if (type == Zoom.OUT) {
zoom(point1.getX(), point1.getY(), point2.getX(), point2.getY(), point3.getX(), point3.getY(), point4.getX(), point4.getY(),
DEFAULT_TOUCH_ACTION_DURATION);
} else if (type == Zoom.IN) {
zoom(point2.getX(), point2.getY(), point1.getX(), point1.getY(), point4.getX(), point4.getY(), point3.getX(), point3.getY(),
DEFAULT_TOUCH_ACTION_DURATION);
}
}
/**
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void zoom(int startx1, int starty1, int endx1, int endy1, int startx2, int starty2, int endx2, int endy2, int duration) {
UTILS_LOGGER.debug(
"Zoom action will be performed with parameters : startX1 : {} ; startY1: {} ; endX1: {} ; endY1: {}; startX2 : {} ; startY2: {} ; endX2: {} ; endY2: {}",
startx1, starty1, endx1, endy1, startx2, starty2, endx2, endy2);
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence zoomPartOne = new Sequence(finger, 0);
zoomPartOne.addAction(finger.createPointerMove(Duration.ofMillis(0),
PointerInput.Origin.viewport(), startx1, starty1));
zoomPartOne.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
zoomPartOne.addAction(new Pause(finger, Duration.ofMillis(duration)));
zoomPartOne.addAction(finger.createPointerMove(Duration.ofMillis(600), // todo investigate the best duration
PointerInput.Origin.viewport(), endx1, endy1));
zoomPartOne.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
PointerInput anotherFinger = new PointerInput(PointerInput.Kind.TOUCH, "another finger");
Sequence zoomPartTwo = new Sequence(anotherFinger, 0);
zoomPartTwo.addAction(anotherFinger.createPointerMove(Duration.ofMillis(0),
PointerInput.Origin.viewport(), startx2, starty2));
zoomPartTwo.addAction(anotherFinger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
zoomPartTwo.addAction(new Pause(anotherFinger, Duration.ofMillis(duration)));
zoomPartTwo.addAction(anotherFinger.createPointerMove(Duration.ofMillis(600), // todo investigate the best duration
PointerInput.Origin.viewport(), endx2, endy2));
zoomPartTwo.addAction(anotherFinger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
Interactive driver = null;
try {
driver = (Interactive) getDriver();
driver.perform(List.of(zoomPartOne, zoomPartTwo));
UTILS_LOGGER.info("Zoom has been performed");
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support zoom method", e);
} catch (WebDriverException e) {
UTILS_LOGGER.error("Error during zooming", e);
}
}
/**
* Check if started driver/application is running in foreground
*
* @return boolean
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean isAppRunning() {
String bundleId = "";
String os = getDevice().getOs();
HasCapabilities driver = null;
try {
driver = (HasCapabilities) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support isAppRunning method", e);
}
Capabilities capabilities = driver.getCapabilities();
// get bundleId or appId of the application started by driver
if (os.equalsIgnoreCase(SpecialKeywords.ANDROID)) {
bundleId = capabilities.getCapability(SpecialKeywords.APP_PACKAGE).toString();
} else if (os.equalsIgnoreCase(SpecialKeywords.IOS) || os.equalsIgnoreCase(SpecialKeywords.MAC)
|| os.equalsIgnoreCase(SpecialKeywords.TVOS)) {
bundleId = capabilities.getCapability(SpecialKeywords.BUNDLE_ID).toString();
}
return isAppRunning(bundleId);
}
/**
* Check running in foreground application by bundleId or appId
*
* @param bundleId the bundle identifier for iOS (or appPackage for Android) of the app to query the state of.
* @return true, if app's status equals {@link ApplicationState#RUNNING_IN_FOREGROUND}, false otherwise
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean isAppRunning(String bundleId) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support isAppRunning method", e);
}
ApplicationState actualApplicationState = driver.queryAppState(bundleId);
return ApplicationState.RUNNING_IN_FOREGROUND.equals(actualApplicationState);
}
/**
* Terminate running driver/application
*
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void terminateApp() {
String bundleId = "";
String os = getDevice().getOs();
HasCapabilities driver = null;
try {
driver = (HasCapabilities) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support terminateApp method", e);
}
Capabilities capabilities = driver.getCapabilities();
// get bundleId or appId of the application started by driver
if (os.equalsIgnoreCase(SpecialKeywords.ANDROID)) {
bundleId = capabilities.getCapability(SpecialKeywords.APP_PACKAGE).toString();
} else if (os.equalsIgnoreCase(SpecialKeywords.IOS) ||
os.equalsIgnoreCase(SpecialKeywords.MAC) ||
os.equalsIgnoreCase(SpecialKeywords.TVOS)) {
bundleId = capabilities.getCapability(SpecialKeywords.BUNDLE_ID).toString();
}
terminateApp(bundleId);
}
/**
* Terminate the particular application if it is running
*
* @param bundleId the bundle identifier (or app id) of the app to be terminated.
* @return true if the app was running before and has been successfully stopped
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean terminateApp(String bundleId) {
return terminateApp(bundleId, null);
}
/**
* Terminate the particular application if it is running
*
* @param bundleId the bundle identifier (or app id) of the app to be terminated.
* @param options the set of termination options supported by the particular platform.
* @return true if the app was running before and has been successfully stopped
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean terminateApp(String bundleId, @Nullable BaseTerminateApplicationOptions> options) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support terminateApp method", e);
}
return driver.terminateApp(bundleId, options);
}
/**
* The application that has its package name set to current driver's
* capabilities will be closed to background IN CASE IT IS CURRENTLY IN
* FOREGROUND. Will be in recent app's list;
*
* @throws UnsupportedOperationException if driver does not support this feature
* @deprecated https://github.com/appium/appium/issues/1580
*/
@Deprecated(since = "8.x")
default void closeApp() {
UTILS_LOGGER.info("Application will be closed to background");
SupportsLegacyAppManagement driver = null;
try {
driver = (SupportsLegacyAppManagement) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support closeApp method", e);
}
driver.closeApp();
}
// TODO Update this method using findByImage strategy
/**
* Pressing bottom right button on the keyboard by coordinates: "search", "ok",
* "next", etc. - various keys appear at this position. Tested at Nexus 6P
* Android 8.0.0 standard keyboard. Coefficients of coordinates for other
* devices and custom keyboards could be different.
*
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void pressBottomRightKey() {
WebDriver driver = getDriver();
Dimension size = mobilePerformIgnoreException(() -> driver.manage().window().getSize());
int height = size.getHeight();
int width = size.getWidth();
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence pressBottomRightKeySequence = new Sequence(finger, 1)
.addAction(
finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), (int) (width * 0.915), (int) (height * 0.945)))
.addAction(finger.createPointerDown(LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(DEFAULT_TOUCH_ACTION_DURATION)))
.addAction(finger.createPointerUp(LEFT.asArg()));
Interactive drv = null;
try {
drv = (Interactive) driver;
drv.perform(List.of(pressBottomRightKeySequence));
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support pressBottomRightKey method", e);
} catch (WebDriverException e) {
UTILS_LOGGER.error("Error when try to press bottom right key", e);
}
}
private T mobilePerformIgnoreException(Supplier supplier) {
try {
UTILS_LOGGER.debug("Command will be performed with the exception ignoring");
return supplier.get();
} catch (WebDriverException e) {
UTILS_LOGGER.info("Webdriver exception has been fired. One more attempt to execute action.", e);
UTILS_LOGGER.info(supplier.toString());
return supplier.get();
}
}
default boolean isChecked(final ExtendedWebElement element) {
// TODO: SZ migrate to FluentWaits
return element.isElementPresent(5)
&& (element.getElement().isSelected() || element.getAttribute("checked").equals("true"));
}
/**
* Checks if an app is installed on the device
*
* @param packageName bundleId – bundleId of the app
* @return true if app is installed
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean isApplicationInstalled(String packageName) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support isApplicationInstalled method", e);
}
boolean installed = driver.isAppInstalled(packageName);
UTILS_LOGGER.info("Application by package name ({}) installed: {}", packageName, installed);
return installed;
}
/**
* Activates the given app if it installed, but not running or if it is running in the background
*
* @param packageName bundleId – the bundle identifier (or app id) of the app to activate
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void startApp(String packageName) {
startApp(packageName, null);
}
/**
* Activates the given app if it installed, but not running or if it is running in the background
*
* @param packageName bundleId – the bundle identifier (or app id) of the app to activate
* @param options the set of activation options supported by the particular platform
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void startApp(String packageName, @Nullable BaseActivateApplicationOptions> options) {
UTILS_LOGGER.info("Starting {}", packageName);
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support startApp method", e);
}
driver.activateApp(packageName, options);
}
/**
* Install an app on the mobile device
*
* @param apkPath path to app to install or a remote URL
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void installApp(String apkPath) {
installApp(apkPath, null);
}
/**
* Install an app on the mobile device
*
* @param appPath path to app to install or a remote URL
* @param options Set of the corresponding installation options for the particular platform
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void installApp(String appPath, @Nullable BaseInstallApplicationOptions> options) {
UTILS_LOGGER.info("Will install application with apk-file from {}", appPath);
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support installApp method", e);
}
driver.installApp(appPath, options);
}
/**
* Remove the specified app from the device (uninstall)
*
* @param packageName bundleId – the bundle identifier (or app id) of the app to remove
* @return true if the uninstall was successful
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean removeApp(String packageName) {
return removeApp(packageName, null);
}
/**
* Remove the specified app from the device (uninstall)
*
* @param packageName bundleId – the bundle identifier (or app id) of the app to remove
* @param options the set of uninstall options supported by the particular platform
* @return true if the uninstall was successful
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean removeApp(String packageName, @Nullable BaseRemoveApplicationOptions> options) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support removeApp method", e);
}
boolean isRemoved = driver.removeApp(packageName, options);
UTILS_LOGGER.info("Application ({}) was successfully removed: {}", packageName, isRemoved);
return isRemoved;
}
/**
* Runs the current app as a background app for the time
* requested
* This is a synchronous method, it blocks while the
* application is in background.
*
* @param duration The time to run App in background. Minimum time resolution is one millisecond.
* Passing zero or a negative value will switch to Home screen and return immediately.
*/
default void runAppInBackground(Duration duration) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support runAppInBackground method", e);
}
driver.runAppInBackground(duration);
}
/**
* Method to reset test application.
* App's settings will be reset. User will be logged out. Application will be closed to background.
*
* @deprecated https://github.com/appium/appium/issues/15807
*/
@Deprecated(since = "8.x")
default void clearAppCache() {
UTILS_LOGGER.info("Initiation application reset...");
SupportsLegacyAppManagement driver = null;
try {
driver = (SupportsLegacyAppManagement) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support deprecated clearAppCache method", e);
}
driver.resetApp();
}
/**
* Switches to the given context
*
* @param name the name of the context to switch to
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void switchContext(String name) {
SupportsContextSwitching driver = null;
try {
driver = (SupportsContextSwitching) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support switchContext method", e);
}
driver.context(name);
}
/**
* Get the names of available contexts
*
* @return list of available context names
* @throws UnsupportedOperationException if driver does not support this feature
*/
default Set getAvailableContexts() {
SupportsContextSwitching driver = null;
try {
driver = (SupportsContextSwitching) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getAvailableContexts method", e);
}
return driver.getContextHandles();
}
/**
* Get the name of the current context
*
* @return context name or null if it cannot be determined
* @throws UnsupportedOperationException if driver does not support this feature
*/
default String getContext() {
SupportsContextSwitching driver = null;
try {
driver = (SupportsContextSwitching) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getContext method", e);
}
return driver.getContext();
}
/**
* Get device rotation
*
* @return rotation, see {@link DeviceRotation}
* @throws UnsupportedOperationException if driver does not support this feature
*/
default DeviceRotation getRotation() {
SupportsRotation driver = null;
try {
driver = (SupportsRotation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getContext method", e);
}
return driver.rotation();
}
/**
* Change the rotation of the device
*
* @param rotation rotation, see {@link DeviceRotation}
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void rotate(DeviceRotation rotation) {
SupportsRotation driver = null;
try {
driver = (SupportsRotation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support rotate method", e);
}
driver.rotate(rotation);
}
/**
* Change the orientation of the device
*
* @param rotation – rotation, see {@link ScreenOrientation}
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void rotate(ScreenOrientation rotation) {
SupportsRotation driver = null;
try {
driver = (SupportsRotation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support rotate method", e);
}
driver.rotate(rotation);
}
/**
* Get device orientation
*
* @return orientation, see {@link ScreenOrientation}
* @throws UnsupportedOperationException if driver does not support this feature
*/
default ScreenOrientation getOrientation() {
SupportsRotation driver = null;
try {
driver = (SupportsRotation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getOrientation method", e);
}
return driver.getOrientation();
}
/**
* Gets the physical location
*
* @return a {@link Location} containing the location information. Returns null if the location is
* not available
* @throws UnsupportedOperationException if driver does not support this feature
*/
default Location getDeviceLocation() {
SupportsLocation driver = null;
try {
driver = (SupportsLocation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getLocation method", e);
}
return driver.location();
}
/**
* Set the physical location
*
* @param location a {@link Location} containing the location information
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void setDeviceLocation(Location location) {
SupportsLocation driver = null;
try {
driver = (SupportsLocation) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setLocation method", e);
}
driver.setLocation(location);
}
/**
* Gets device date and time for both iOS(host time is returned for simulators) and Android devices
* The default format is `YYYY-MM-DDTHH:mm:ssZ`, which complies to ISO-8601.
*
* @return device time as string
* @throws UnsupportedOperationException if driver does not support this feature
*/
default String getDeviceTime() {
HasDeviceTime driver = null;
try {
driver = (HasDeviceTime) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getDeviceTime method", e);
}
return driver.getDeviceTime();
}
/**
* Gets device date and time for both iOS(host time is returned for simulators) and Android devices
*
* @param format The set of format specifiers. Read
* https://momentjs.com/docs/ to get the full list of supported
* datetime format specifiers. The default format is
* `YYYY-MM-DDTHH:mm:ssZ`, which complies to ISO-8601
* @return device time as string
* @throws UnsupportedOperationException if driver does not support this feature
*/
default String getDeviceTime(String format) {
HasDeviceTime driver = null;
try {
driver = (HasDeviceTime) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getDeviceTime method", e);
}
return driver.getDeviceTime();
}
/**
* Pull a file from the remote system
* On Android the application under test should be built with debuggable flag enabled in order to get access to its container
* on the internal file system
*
* @param remotePath If the path starts with @applicationId// prefix, then the file
* will be pulled from the root of the corresponding application container.
* Otherwise, the root folder is considered as / on Android and
* on iOS it is a media folder root (real devices only).
* @return A byte array of Base64 encoded data
* @throws UnsupportedOperationException if driver does not support this feature
*/
default byte[] pullFile(String remotePath) {
PullsFiles driver = null;
try {
driver = (PullsFiles) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support pullFile method", e);
}
return driver.pullFile(remotePath);
}
/**
* Pull a folder content from the remote system
* On Android the application under test should be built with debuggable flag enabled in order to get access to its container
* on the internal file system.
*
* @param remotePath If the path starts with @applicationId/ prefix, then the folder
* will be pulled from the root of the corresponding application container.
* Otherwise, the root folder is considered as / on Android and
* on iOS it is a media folder root (real devices only).
* @return A byte array of Base64 encoded zip archive data.
* @throws UnsupportedOperationException if driver does not support this feature
*/
default byte[] pullFolder(String remotePath) {
PullsFiles driver = null;
try {
driver = (PullsFiles) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support pullFolder method", e);
}
return driver.pullFile(remotePath);
}
/**
* Get the state of an application
*
* @param bundleId the bundle identifier (or app id) of the app to get the state of
* @return state of app, one of {@link ApplicationState} value
* @throws UnsupportedOperationException if driver does not support this feature
*/
default ApplicationState getAppState(String bundleId) {
InteractsWithApps driver = null;
try {
driver = (InteractsWithApps) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getAppState method", e);
}
return driver.queryAppState(bundleId);
}
/**
* Get all defined Strings from an app for the default language
*
* @return a map with localized strings defined in the app
* @throws UnsupportedOperationException if driver does not support this feature
*/
default Map getAppStringMap() {
HasAppStrings driver = null;
try {
driver = (HasAppStrings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getAppStringMap method", e);
}
return driver.getAppStringMap();
}
/**
* Get all defined Strings from an app for the specified language
*
* @param language strings language code
* @return a map with localized strings defined in the app
* @throws UnsupportedOperationException if driver does not support this feature
*/
default Map getAppStringMap(String language) {
HasAppStrings driver = null;
try {
driver = (HasAppStrings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getAppStringMap method", e);
}
return driver.getAppStringMap(language);
}
/**
* Get all defined Strings from an app for the specified language and strings filename
*
* @param language strings language code
* @param stringFile strings filename
* @return a map with localized strings defined in the app
* @throws UnsupportedOperationException if driver does not support this feature
*/
default Map getAppStringMap(String language, String stringFile) {
HasAppStrings driver = null;
try {
driver = (HasAppStrings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getAppStringMap method", e);
}
return driver.getAppStringMap(language, stringFile);
}
/**
* This method locks a device
* It will return silently if the device is already locked
*
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void lockDevice() {
LocksDevice driver = null;
try {
driver = (LocksDevice) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support lockDevice method", e);
}
driver.lockDevice();
}
/**
* Lock the device (bring it to the lock screen) for a given number of
* seconds or forever (until the command for unlocking is called)
* The call is ignored if the device has been already locked.
*
* @param duration for how long to lock the screen. Minimum time resolution is one second.
* A negative/zero value will lock the device and return immediately.
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void lockDevice(Duration duration) {
LocksDevice driver = null;
try {
driver = (LocksDevice) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support lockDevice method", e);
}
driver.lockDevice(duration);
}
/**
* Unlock the device if it is locked
* This method will return silently if the device is not locked
*
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void unlockDevice() {
LocksDevice driver = null;
try {
driver = (LocksDevice) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support unlockDevice method", e);
}
driver.unlockDevice();
}
/**
* Check if the device is locked
*
* @return true if the device is locked or false otherwise.
* @throws UnsupportedOperationException if driver does not support this feature
*/
default boolean isDeviceLocked() {
LocksDevice driver = null;
try {
driver = (LocksDevice) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support isDeviceLocked method", e);
}
return driver.isDeviceLocked();
}
/**
* Saves base64 encoded data as a media file on the remote system.
*
* @param remotePath Path to file to write data to on remote device
* Only the filename part matters there on Simulator, so the remote end
* can figure out which type of media data it is and save
* it into a proper folder on the target device. Check
* 'xcrun simctl addmedia' output to get more details on
* supported media types.
* If the path starts with @applicationId/ prefix, then the file
* will be pushed to the root of the corresponding application container.
* @param base64Data Base64 encoded byte array of media file data to write to remote device
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void pushFile(String remotePath, byte[] base64Data) {
PushesFiles driver = null;
try {
driver = (PushesFiles) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support pushFile method", e);
}
driver.pushFile(remotePath, base64Data);
}
/**
* Saves base64 encoded data as a media file on the remote system
*
* @param remotePath See the documentation on {@link #pushFile(String, byte[])}
* @param file Is an existing local file to be written to the remote device
* @throws IOException when there are problems with a file or current file system
* @throws UnsupportedOperationException if driver does not support this feature
*/
default void pushFile(String remotePath, File file) throws IOException {
PushesFiles driver = null;
try {
driver = (PushesFiles) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support pushFile method", e);
}
driver.pushFile(remotePath, file);
}
/**
* Start asynchronous screen recording process
*
* @param The platform-specific {@link BaseStartScreenRecordingOptions}
* @param options see the documentation on the {@link BaseStartScreenRecordingOptions}
* descendant for the particular platform
* @return `not used`
* @throws UnsupportedOperationException if driver does not support this feature
*/
@SuppressWarnings("rawtypes")
default String startRecordingScreen(T options) {
CanRecordScreen driver = null;
try {
driver = (CanRecordScreen) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support startRecordingScreen method", e);
}
return driver.startRecordingScreen(options);
}
/**
* Start asynchronous screen recording process with default options
*
* @return `not used`
* @throws UnsupportedOperationException if driver does not support this feature
*/
default String startRecordingScreen() {
CanRecordScreen driver = null;
try {
driver = (CanRecordScreen) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support startRecordingScreen method", e);
}
return driver.startRecordingScreen();
}
/**
* Gather the output from the previously started screen recording to a media file
*
* @param The platform-specific {@link BaseStopScreenRecordingOptions}
* @param options see the documentation on the {@link BaseStopScreenRecordingOptions}
* descendant for the particular platform
* @return Base-64 encoded content of the recorded media file or an empty string
* if the file has been successfully uploaded to a remote location (depends on the actual options)
* @throws UnsupportedOperationException if driver does not support this feature
*/
@SuppressWarnings("rawtypes")
default String stopRecordingScreen(T options) {
CanRecordScreen driver = null;
try {
driver = (CanRecordScreen) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support stopRecordingScreen method", e);
}
return driver.stopRecordingScreen(options);
}
/**
* Gather the output from the previously started screen recording to a media file
* with default options
*
* @return Base-64 encoded content of the recorded media file
* @throws UnsupportedOperationException if driver does not support this feature
*/
default String stopRecordingScreen() {
CanRecordScreen driver = null;
try {
driver = (CanRecordScreen) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support stopRecordingScreen method", e);
}
return driver.stopRecordingScreen();
}
/**
* Set the content of device's clipboard.
*
* @param contentType one of supported content types.
* @param base64Content base64-encoded content to be set.
*/
default void setClipboard(ClipboardContentType contentType, byte[] base64Content) {
HasClipboard driver = null;
try {
driver = (HasClipboard) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setClipboard method", e);
}
driver.setClipboard(contentType, base64Content);
}
/**
* Get the content of the clipboard.
*
* @param contentType one of supported content types.
* @return the actual content of the clipboard as base64-encoded string or an empty string if the clipboard is empty.
*/
default String getClipboard(ClipboardContentType contentType) {
HasClipboard driver = null;
try {
driver = (HasClipboard) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getClipboard method", e);
}
return driver.getClipboard(contentType);
}
/**
* Set the clipboard text.
*
* @param text the actual text to be set.
*/
default void setTextToClipboard(String text) {
HasClipboard driver = null;
try {
driver = (HasClipboard) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setTextToClipboard method", e);
}
driver.setClipboardText(text);
}
/**
* Get the clipboard text.
*
* @return either the text, which is stored in the clipboard or an empty string if the clipboard is empty.
*/
default String getTextFromClipboard() {
HasClipboard driver = null;
try {
driver = (HasClipboard) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getTextFromClipboard method", e);
}
return driver.getClipboardText();
}
/**
* Set a setting for this test session.
*
* @param setting setting you wish to set.
* @param value value of the setting.
* @return {@link HasSettings} instance for chaining.
*/
default HasSettings setSetting(Setting setting, Object value) {
HasSettings driver = null;
try {
driver = (HasSettings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setSetting method", e);
}
return driver.setSetting(setting, value);
}
/**
* Set a setting for this test session.
*
* @param settingName setting name you wish to set.
* @param value value of the setting.
* @return {@link HasSettings} instance for chaining.
*/
default HasSettings setSetting(String settingName, Object value) {
HasSettings driver = null;
try {
driver = (HasSettings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setSetting method", e);
}
return driver.setSetting(settingName, value);
}
/**
* Sets settings for this test session.
*
* @param settings a map with settings, where key is the setting name you wish to set and value is the value of
* the setting.
* @return {@link HasSettings} {@link HasSettings} instance for chaining.
*/
default HasSettings setSettings(EnumMap settings) {
HasSettings driver = null;
try {
driver = (HasSettings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setSettings method", e);
}
return driver.setSettings(settings);
}
/**
* Sets settings for this test session.
*
* @param settings a map with settings, where key is the setting name you wish to set and value is the value of
* the setting.
* @return {@link HasSettings} instance for chaining.
*/
default HasSettings setSettings(Map settings) {
HasSettings driver = null;
try {
driver = (HasSettings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support setSettings method", e);
}
return driver.setSettings(settings);
}
/**
* Get settings stored for this test session.
*
* @return JsonObject, a straight-up hash of settings.
*/
default Map getSettings() {
HasSettings driver = null;
try {
driver = (HasSettings) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getSettings method", e);
}
return driver.getSettings();
}
/**
* Get capabilities of the current driver
*
* @return see {@link Capabilities}
*/
default Capabilities getCapabilities() {
HasCapabilities driver = null;
try {
driver = (HasCapabilities) getDriver();
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support getCapabilities method", e);
}
return driver.getCapabilities();
}
/**
* Drag and drop
*
* @param dragMeElement element to drag
* @param dropZoneElement drop zone element
*/
default void dragAndDrop(ExtendedWebElement dragMeElement, ExtendedWebElement dropZoneElement) {
dragAndDrop(dragMeElement, dropZoneElement, Duration.ofMillis(1000), Duration.ofMillis(1000));
}
/**
* Drag and drop
*
* @param dragMeElement element to drag
* @param dropZoneElement drop zone element
* @param pressingTime time of clicking on the element to be dragged
* @param dragTime time to drag an element to the drop zone
*/
default void dragAndDrop(ExtendedWebElement dragMeElement, ExtendedWebElement dropZoneElement, Duration pressingTime, Duration dragTime) {
Dimension dragMeElementSize = dragMeElement.getSize();
Point dragMeElementLocation = dragMeElement.getLocation();
Point dragMeElementCenter = new Point(dragMeElementLocation.getX() + dragMeElementSize.getWidth() / 2,
dragMeElementLocation.getY() + dragMeElementSize.getHeight() / 2);
Dimension dropZoneElementSize = dropZoneElement.getSize();
Point dropZoneElementLocation = dropZoneElement.getLocation();
Point dropZoneElementCenter = new Point(dropZoneElementLocation.getX() + dropZoneElementSize.getWidth() / 2,
dropZoneElementLocation.getY() + dropZoneElementSize.getHeight() / 2);
dragAndDrop(dragMeElementCenter.x, dragMeElementCenter.y, dropZoneElementCenter.x, dropZoneElementCenter.y,
pressingTime, dragTime);
}
/**
* Drag and drop
*
* @param fromX x point of the drag and drop element
* @param fromY y point of the drag and drop element
* @param toX x point of the drop zone
* @param toY y point of the drop zone
* @param pressingTime time of clicking on the element to be dragged
* @param dragTime time to drag an element to the drop zone
*/
default void dragAndDrop(int fromX, int fromY, int toX, int toY, Duration pressingTime, Duration dragTime) {
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence sequence = new Sequence(finger, 1);
sequence.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), fromX, fromY));
sequence.addAction(finger.createPointerDown(PointerInput.MouseButton.MIDDLE.asArg()));
sequence.addAction(new Pause(finger, pressingTime));
sequence.addAction(finger.createPointerMove(dragTime, PointerInput.Origin.viewport(), toX, toY));
sequence.addAction(finger.createPointerUp(PointerInput.MouseButton.MIDDLE.asArg()));
Interactive drv = null;
try {
drv = (Interactive) getDriver();
drv.perform(List.of(sequence));
} catch (ClassCastException e) {
throw new UnsupportedOperationException("Driver is not support dragAndDrop method", e);
}
}
}