From 91e13f1720c519188cc5c3520dbad28b15833eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gro=C3=9Fmann?= Date: Mon, 27 Jun 2022 12:06:09 +0200 Subject: [PATCH] Merge remote-tracking branch 'origin/master' # Conflicts: # README.md # appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverFactory.java --- README.md | 206 +++++++- appium/build.gradle | 1 + .../tic/testframework/appium/Browsers.java | 31 ++ .../appium/WinAppDriverFactory.java | 149 ++++++ .../core/WinAppDriverCoreAdapter.java | 66 +++ .../DriverUi_Appium.java} | 26 +- .../mobile/driver/AppiumDriverFactory.java | 155 +++--- .../mobile/driver/AppiumDriverManager.java | 2 + .../mobile/driver/AppiumDriverRequest.java | 56 --- .../mobile/driver/MobileBrowsers.java | 8 +- .../AppiumGuiElementCoreAdapter.java | 472 ++---------------- .../AppiumGuiElementCoreFactory.java | 45 -- .../webdrivermanager/AppiumDriverRequest.java | 63 +++ .../webdrivermanager/WinAppDriverRequest.java | 167 +++++++ .../appium/windows/CalculatorApp.java | 53 ++ .../test/driver/TesterraAppiumDriverTest.java | 54 +- .../mobile/test/driver/WinAppDriverTest.java | 83 +++ .../test/guielement/MobileGuiElementTest.java | 4 +- .../test/guielement/MobilePageTest.java | 4 +- appium/src/test/resources/test.properties | 9 +- build.gradle | 8 +- 21 files changed, 957 insertions(+), 705 deletions(-) create mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/Browsers.java create mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/WinAppDriverFactory.java create mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/core/WinAppDriverCoreAdapter.java rename appium/src/main/java/eu/tsystems/mms/tic/testframework/{mobile/hook/MobileAppiumHook.java => ioc/DriverUi_Appium.java} (55%) delete mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverRequest.java delete mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreFactory.java create mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/AppiumDriverRequest.java create mode 100644 appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/WinAppDriverRequest.java create mode 100644 appium/src/test/java/eu/tsystems/mms/tic/testframework/appium/windows/CalculatorApp.java create mode 100644 appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/WinAppDriverTest.java diff --git a/README.md b/README.md index e5c28b9..f7faea3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Testerra Appium Connector

- + @@ -30,19 +30,21 @@ It will register with Testerra Hooking system and uses the event bus to react on | Appium connector | Testerra | |------------------|---------------| -| `>=1.1` | `>=1.3` | +| `>=1.1` | `>=1.3` | +| `>=2.0-RC-5` | `>=2.0-RC-18` | +| `>=2.0` | `>=2.0` | ### Usage -Include the following dependency in your project. +Include the following dependency in your project. Please replace `2-SNAPSHOT` with the latest version. Gradle: ````groovy // Version from this module -implementation 'io.testerra:appium:1.2' -// Replace with your Testerra version -implementation 'io.testerra:driver-ui:1.3' +implementation 'io.testerra:appium:2.0' +// Used Testerra version +implementation 'io.testerra:driver-ui:2.0' implementation 'io.appium:java-client:7.3.0' ```` @@ -53,18 +55,25 @@ Maven: io.testerra appium - 1.2 + 2.0 - + io.testerra driver-ui - 1.3 + 2.0 io.appium java-client 7.3.0 + + + + org.slf4j + slf4j-api + + ``` @@ -84,13 +93,10 @@ public class ExampleTest extends TesterraTest { @Test public void testT01_My_first_test() { - final AppiumDriverManager appiumDriverManager = new AppiumDriverManager(); - - final WebDriver driver = WebDriverManager.getWebDriver(); - final AppiumDriver appiumDriver = appiumDriverManager.fromWebDriver(driver); - - appiumDriver.rotate(ScreenOrientation.LANDSCAPE); - driver.get(PropertyManager.get("tt.baseurl")); + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(); + WEB_DRIVER_MANAGER.unwrapWebDriver(webDriver, AppiumDriver.class).ifPresent(appiumDriver -> { + appiumDriver.rotate(ScreenOrientation.LANDSCAPE); + }); } } ``` @@ -116,8 +122,7 @@ default values will provide you a device with given operating system and of `@ca #### Screenshots -Screenshots on test case failure works out of the box, because Appium is implementing the necessary interfaces of Selenium to -achieve this. +Screenshots on test case failure works out of the box, because Appium is implementing the necessary interfaces of Selenium to achieve this. #### Videos @@ -125,14 +130,169 @@ Because videos are a platform dependent feature, Appium connector does not provi ### Properties +| Property | default | Description | +|--------------------------------|---------------------------------------|-------------------------------------------------------| +| tt.mobile.grid.url | NONE | Grid URL of Appium / Selenium Grid ending on "wd/hub" | +| tt.mobile.grid.access.key | NONE | Access key of your user and project | +| tt.mobile.device.query.ios | "@os='ios' and @category='PHONE'" | Access key of your user and project | +| tt.mobile.device.query.android | "@os='android' and @category='PHONE'" | Access key of your user and project | + +--- + +### AppiumDriverRequest + +You can also create new sessions by using the `WebDriverRequest` interface. + +```java +AppiumDriverRequest appiumRequest = new AppiumDriverRequest(); +appiumRequest.setAccessKey(String); +appiumRequest.setDeviceQuery(String); +appiumRequest.setServerUrl(URL); +appiumRequest.setStartupTimeoutSeconds(int); +appiumRequest.setReuseTimeoutSeconds(int); + +WebDriver appiumDriver = WEB_DRIVER_MANAGER.getWebDriver(appiumRequest); +``` + +## WinAppDriver support + +The Appium connector also supports automation of Windows application using the `WindowsDriver` in two setup scenarios. + +* Using an Appium-Server +* Using a WinAppServer + +### Using Appium-Server (*tbd*) +*(Documentation missing)* + +### Using WinAppDriver + +The [WinAppDriver](https://github.com/microsoft/WinAppDriver) is like a Selenium server for Windows applications. You can download it from the official project website or just install it via. [chocolatey](https://chocolatey.org/) + +```shell +choco install winappdriver +``` + +Before you are able to use it, you should make sure: + +* That the Windows Operating System runs in Development Mode. +* The WinAppDriver able to run. + +```text +"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe" + +Windows Application Driver listening for requests at: http://127.0.0.1:4723/ +Press ENTER to exit. +``` +You can customize the connection using [Properties](#Properties) + +## Starting an application + +You can start applications in several ways: + +* Start applications by the executable path. +* Start drivers for the Windows Desktop. +* Start drivers from known application window title. +* Start application from internal application id (*Documentation unknown*) + +### Start applications from path + +The application path gets translated to the application id and also sets the working directory based on its parent. + +```java +WinAppDriverRequest appRequest = new WinAppDriverRequest(); +appRequest.setApplicationPath("C:\\Program Files (x86)\\Application\\Application.exe"); +``` + +**NOTE**: Some applications require their working directory set to the parent of the application binary to run properly. + +For example: `C:\\Program Files (x86)\\Application\\Application.exe` needs to run in `C:\\Program Files (x86)\\Application`. When setting the application path, the `WinAppDriverRequests` tries to detect the parent directory by using Java `Path.getParent()`, but this seems to be buggy when using Windows paths in Posix environments. Therefore, we've implemented a workaround to detect the base directory by using basic regular expressions with `/` and `\\` delimiters. + +### Start a Desktop driver +```java +WinAppDriverRequest appRequest = new WinAppDriverRequest(); +appRequest.setDesktopApplication(); +``` + +### Start driver from known window title + +This will try to initialize the driver by an already opened application identified by its window title. Otherwise, it will try to start by given application id. + +```java +WinAppDriverRequest appRequest = new WinAppDriverRequest(); +appRequest.reuseApplicationByWindowTitle("My App"); +appRequest.setApplicationPath("C:\\Program Files (x86)\\Application\\Application.exe"); +``` + +### Start applications from application id + +Starting the application from application id is currently unknown. However, this application id starts the default calculator app. + +```java +WinAppDriverRequest appRequest = new WinAppDriverRequest(); +appRequest.setApplication("Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"); +``` + +### Retrieving element selectors + +The WinAppDriver project provides a binary release of tool named [UIRecorder](https://github.com/microsoft/WinAppDriver/releases/tag/UIR-v1.1) for retrieving element selectors xPath by hovering and focusing elements. + +### Find UiElements + +Most of the applications support finding elements using `AutomationId` attribute selector. + +```java +PreparedLocator automationLocator = LOCATE.prepare("//*[@AutomationId=\"%s\"]"); +UiElement num1Btn = find(automationLocator.with("num1Button")); +``` + +### Accessing the native WindowsDriver API + +All features of the `WindowsDriver` implementation are hidden by the `WebDriver` by default. +To retrieve the raw WindowsDriver, you can unwrap it via. `WebDriverManager`. + +```java +Optional optionalWindowsDriver = WEB_DRIVER_MANAGER.unwrapWebDriver(appDriver, WindowsDriver.class); + +optionalWindowsDriver.ifPresent(windowsDriver -> { + // Native API access here +}); +``` + +### Closing applications + +The application will automatically be closed, when `WebDriver.quit()` gets called managed by Testerra on session end. But that closes the application's window which doesn't mean that the application is forced to quit. It could still be opened as a system service available by System tray icons. + +There is an experimental feature to force quit an application: https://github.com/Microsoft/WinAppDriver/issues/159 + +Anyway, if you want to prevent Testerra from closing your `WinAppDriver`, just configure it on the `WinAppDriverRequest`. + +```java +appRequest.setShutdownAfterTest(false); +appRequest.setShutdownAfterExecution(false); +``` + +### Properties + +The WinAppDriver implementation provides the following properties. + |Property|default|Description| |---|---|---| -|tt.mobile.grid.url|NONE|Grid URL of Appium / Selenium Grid ending on "wd/hub"| -|tt.mobile.grid.access.key|NONE|Access key of your user and project| -|tt.mobile.device.query.ios|"@os='ios' and @category='PHONE'"|Access key of your user and project| -|tt.mobile.device.query.android|"@os='android' and @category='PHONE'"|Access key of your user and project| +|`tt.winapp.server.url`|`http://localhost:4723/`|URL of the WinAppDriver or Appium / Selenium Grid ending on "wd/hub"| +|`tt.winapp.reuse.timeout.seconds`|`2`|Timeout for finding reusable applications. | +|`tt.winapp.startup.timeout.seconds`|`8`|Timeout for general driver startup. | ---- +### Troubleshooting + +**Symptom: Application forget settings after restart** +- Solution: Try to set the working directory manually. + +**Symptom: Elements are not interactable on remote WinAppDriver** +- Reason: When closing RDP connections, the Desktop gets non-interactable as default behaviour. +- Solution: You can use VNC instead or configure your RDP to keep sessions active. More information can be found here: https://github.com/microsoft/WinAppDriver/issues/1510 + +**Symptom: Non-interactable elements on non-focused windows** +- Reason: Some applications get minimized when they loose focus. +- Solution: Try to bring the window to the front by calling `webDriver.manage().window().setPosition(new Point(0,0));` ## Publication diff --git a/appium/build.gradle b/appium/build.gradle index 4b7d63a..473f544 100644 --- a/appium/build.gradle +++ b/appium/build.gradle @@ -6,6 +6,7 @@ dependencies { implementation 'org.apache.commons:commons-lang3:3.9' testImplementation 'io.testerra:driver-ui:' + testerraTestVersion + testImplementation 'io.testerra:report-ng:' + testerraTestVersion testImplementation 'io.appium:java-client:7.3.0' } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/Browsers.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/Browsers.java new file mode 100644 index 0000000..026c1f1 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/Browsers.java @@ -0,0 +1,31 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.appium; + +/** + * A list ob supported browsers + */ +public class Browsers { + public static final String windows ="windows"; + public static final String mobile_chrome = "mobile_chrome"; + public static final String mobile_safari = "mobile_safari"; +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/WinAppDriverFactory.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/WinAppDriverFactory.java new file mode 100644 index 0000000..24e4442 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/WinAppDriverFactory.java @@ -0,0 +1,149 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.appium; + +import eu.tsystems.mms.tic.testframework.core.WinAppDriverCoreAdapter; +import eu.tsystems.mms.tic.testframework.common.IProperties; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementCore; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData; +import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext; +import eu.tsystems.mms.tic.testframework.testing.TestControllerProvider; +import eu.tsystems.mms.tic.testframework.utils.Sleepy; +import eu.tsystems.mms.tic.testframework.webdriver.WebDriverFactory; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverRequest; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WinAppDriverRequest; +import io.appium.java_client.windows.WindowsDriver; +import io.appium.java_client.windows.WindowsElement; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; + +public class WinAppDriverFactory implements WebDriverFactory, Loggable, TestControllerProvider, Sleepy { + + public enum Properties implements IProperties { + WINAPP_SERVER_URL("tt.winapp.server.url", "http://localhost:4723/"), + REUSE_TIMEOUT_SECONDS("tt.winapp.reuse.timeout.seconds", 2), + STARTUP_TIMEOUT_SECONDS("tt.winapp.startup.timeout.seconds", 8), + //WINAPP_DRIVER_PATH("tt.winapp.driver","C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe") + ; + private final String property; + private final Object defaultValue; + + Properties(String property, Object defaultValue) { + this.property = property; + this.defaultValue = defaultValue; + } + + public String toString() { + return this.property; + } + + public Object getDefault() { + return this.defaultValue; + } + } + + private WindowsDriver startNewWindowsDriver(WinAppDriverRequest appDriverRequest, SessionContext sessionContext) { + final URL finalWinAppServerUrl = appDriverRequest.getServerUrl().get(); + sessionContext.setNodeUrl(finalWinAppServerUrl); + + DesiredCapabilities desiredCapabilities = appDriverRequest.getDesiredCapabilities(); + desiredCapabilities.setCapability(WinAppDriverRequest.DEVICE_NAME, "WindowsPC"); +// desiredCapabilities.setCapability("ms:experimental-webdriver", true); + sessionContext.setActualBrowserName("WindowsPC"); + appDriverRequest.getApplicationId().ifPresent(appId -> { + desiredCapabilities.setCapability(WinAppDriverRequest.APP_ID, appId); + }); + + WindowsDriver windowsDriver = new WindowsDriver<>(finalWinAppServerUrl, desiredCapabilities); + //CONTROL.retryFor(appDriverRequest.getStartupTimeoutSeconds(), windowsDriver::getTitle, this::sleep); + return windowsDriver; + } + + @Override + public WebDriverRequest prepareWebDriverRequest(WebDriverRequest webDriverRequest) { + if (!(webDriverRequest instanceof WinAppDriverRequest)) { + throw new RuntimeException("Unsupported " + webDriverRequest.getClass().getSimpleName()); + } + return webDriverRequest; + } + + @Override + public WebDriver createWebDriver(WebDriverRequest webDriverRequest, SessionContext sessionContext) { + WinAppDriverRequest appDriverRequest = (WinAppDriverRequest)webDriverRequest; + + /** + * Try to reuse an already opened application + * @see https://github.com/Microsoft/WinAppDriver/blob/v1.0-RC2/README.md#attaching-to-an-existing-app-window + * @see https://github.com/microsoft/WinAppDriver/issues/1092 + */ + appDriverRequest.getReusableApplicationWindowTitle().ifPresent(reuseableWindowTitle -> { + DesiredCapabilities desiredCapabilities = appDriverRequest.getDesiredCapabilities(); + WinAppDriverRequest desktopDriverRequest; + if (WinAppDriverRequest.APP_ID_DESKTOP.equals(desiredCapabilities.getCapability(WinAppDriverRequest.APP_ID))) { + desktopDriverRequest = appDriverRequest; + } else { + desktopDriverRequest = new WinAppDriverRequest(); + desktopDriverRequest.setDesktopApplication(); + appDriverRequest.getServerUrl().ifPresent(desktopDriverRequest::setServerUrl); + } + + WindowsDriver desktopDriver = startNewWindowsDriver(desktopDriverRequest, sessionContext); + + log().info(String.format("Try to create driver on running application by window title \"%s\"", reuseableWindowTitle)); + CONTROL.waitFor(appDriverRequest.getReuseTimeoutSeconds(), () -> { + WebElement elementByName = desktopDriver.findElementByName(reuseableWindowTitle); + String nativeWindowHandle = elementByName.getAttribute("NativeWindowHandle"); + int nativeWindowHandleId = Integer.parseInt(nativeWindowHandle); + if (nativeWindowHandleId > 0) { + log().info("Found running application window handle: " + nativeWindowHandle); + desiredCapabilities.setCapability(WinAppDriverRequest.TOP_LEVEL_WINDOW, Integer.toHexString(nativeWindowHandleId)); + appDriverRequest.unsetApplication(); + } else { + log().warn("Ignore invalid application window handle: " + nativeWindowHandle); + } + }); + + if (desktopDriverRequest != appDriverRequest) { + desktopDriver.quit(); + } + }); + + return startNewWindowsDriver(appDriverRequest, sessionContext); + } + + @Override + public List getSupportedBrowsers() { + return Arrays.asList(Browsers.windows); + } + + @Override + public GuiElementCore createCore(GuiElementData guiElementData) { + return new WinAppDriverCoreAdapter(guiElementData); + } +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/core/WinAppDriverCoreAdapter.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/core/WinAppDriverCoreAdapter.java new file mode 100644 index 0000000..3deb021 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/core/WinAppDriverCoreAdapter.java @@ -0,0 +1,66 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.core; + +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.pageobjects.GuiElement; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.AbstractWebDriverCore; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementCore; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData; +import java.awt.Color; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +/** + * Implements {@link GuiElementCore} to fulfill Testerra {@link GuiElement} functionality. + */ +public class WinAppDriverCoreAdapter extends AbstractWebDriverCore implements Loggable { + + public WinAppDriverCoreAdapter(GuiElementData guiElementData) { + super(guiElementData); + } + + @Override + protected void switchToDefaultContent(WebDriver webDriver) { + // not supported + } + + @Override + protected void switchToFrame(WebDriver webDriver, WebElement webElement) { + // not supported + } + + @Override + public void highlight(Color color) { + // Not supported + } + + @Override + protected void highlightWebElement(WebElement webElement, Color color) { + // Not supported + } + + @Override + public void swipe(int offsetX, int offSetY) { + // Not supported + } +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/hook/MobileAppiumHook.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java similarity index 55% rename from appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/hook/MobileAppiumHook.java rename to appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java index 81c95ff..b2a2bee 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/hook/MobileAppiumHook.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java @@ -19,15 +19,15 @@ * under the License. * */ -package eu.tsystems.mms.tic.testframework.mobile.hook; +package eu.tsystems.mms.tic.testframework.ioc; -import eu.tsystems.mms.tic.testframework.common.PropertyManager; +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; +import com.google.inject.multibindings.Multibinder; +import eu.tsystems.mms.tic.testframework.appium.WinAppDriverFactory; import eu.tsystems.mms.tic.testframework.hooks.ModuleHook; import eu.tsystems.mms.tic.testframework.mobile.driver.AppiumDriverFactory; -import eu.tsystems.mms.tic.testframework.mobile.driver.MobileBrowsers; -import eu.tsystems.mms.tic.testframework.mobile.guielement.AppiumGuiElementCoreFactory; -import eu.tsystems.mms.tic.testframework.pageobjects.GuiElement; -import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverManager; +import eu.tsystems.mms.tic.testframework.webdriver.WebDriverFactory; /** * Implementes Testerra {@link ModuleHook} @@ -37,16 +37,12 @@ * * @author Eric Kubenka */ -public class MobileAppiumHook implements ModuleHook { +public class DriverUi_Appium extends AbstractModule { @Override - public void init() { - WebDriverManager.registerWebDriverFactory(new AppiumDriverFactory(), MobileBrowsers.mobile_chrome, MobileBrowsers.mobile_safari); - GuiElement.registerGuiElementCoreFactory(new AppiumGuiElementCoreFactory(), MobileBrowsers.mobile_chrome, MobileBrowsers.mobile_safari); - } - - @Override - public void terminate() { - + protected void configure() { + Multibinder webDriverFactoryBinder = Multibinder.newSetBinder(binder(), WebDriverFactory.class); + webDriverFactoryBinder.addBinding().to(AppiumDriverFactory.class).in(Scopes.SINGLETON); + webDriverFactoryBinder.addBinding().to(WinAppDriverFactory.class).in(Scopes.SINGLETON); } } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverFactory.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverFactory.java index 9d1edb8..683b9d4 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverFactory.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverFactory.java @@ -22,26 +22,30 @@ package eu.tsystems.mms.tic.testframework.mobile.driver; +import eu.tsystems.mms.tic.testframework.appium.Browsers; import eu.tsystems.mms.tic.testframework.common.PropertyManager; +import eu.tsystems.mms.tic.testframework.common.Testerra; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.mobile.guielement.AppiumGuiElementCoreAdapter; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementCore; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData; import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext; import eu.tsystems.mms.tic.testframework.report.utils.ExecutionContextController; -import eu.tsystems.mms.tic.testframework.webdrivermanager.AbstractWebDriverRequest; -import eu.tsystems.mms.tic.testframework.webdrivermanager.UnspecificWebDriverRequest; -import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverFactory; +import eu.tsystems.mms.tic.testframework.report.utils.IExecutionContextController; +import eu.tsystems.mms.tic.testframework.utils.DefaultCapabilityUtils; +import eu.tsystems.mms.tic.testframework.webdriver.WebDriverFactory; +import eu.tsystems.mms.tic.testframework.webdrivermanager.AppiumDriverRequest; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverRequest; +import io.appium.java_client.AppiumDriver; import io.appium.java_client.android.AndroidDriver; -import io.appium.java_client.android.AndroidElement; import io.appium.java_client.ios.IOSDriver; -import io.appium.java_client.ios.IOSElement; import io.appium.java_client.remote.MobileBrowserType; -import org.apache.commons.lang3.StringUtils; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.SessionNotCreatedException; +import java.util.Arrays; +import java.util.List; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.DesiredCapabilities; -import org.openqa.selenium.support.events.EventFiringWebDriver; - -import java.net.MalformedURLException; import java.net.URL; +import org.openqa.selenium.support.events.EventFiringWebDriver; /** * Creates {@link WebDriver} sessions for {@link io.appium.java_client.AppiumDriver} based on {@link AppiumDriverRequest} @@ -50,105 +54,86 @@ * * @author Eric Kubenka */ -public class AppiumDriverFactory extends WebDriverFactory { - +public class AppiumDriverFactory implements WebDriverFactory, Loggable { @Override - protected AppiumDriverRequest buildRequest(AbstractWebDriverRequest webDriverRequest) { - + public WebDriverRequest prepareWebDriverRequest(WebDriverRequest webDriverRequest) { AppiumDriverRequest finalRequest; if (webDriverRequest instanceof AppiumDriverRequest) { finalRequest = (AppiumDriverRequest) webDriverRequest; - } else if (webDriverRequest instanceof UnspecificWebDriverRequest) { + } else { finalRequest = new AppiumDriverRequest(); finalRequest.setSessionKey(webDriverRequest.getSessionKey()); finalRequest.setBrowser(webDriverRequest.getBrowser()); finalRequest.setBrowserVersion(webDriverRequest.getBrowserVersion()); - webDriverRequest.getBaseUrl().ifPresent(finalRequest::setBaseUrl); - } else { - throw new RuntimeException(webDriverRequest.getClass().getSimpleName() + " is not allowed here"); } - return finalRequest; - } + DesiredCapabilities requestCapabilities = finalRequest.getDesiredCapabilities(); - @Override - protected DesiredCapabilities buildCapabilities(DesiredCapabilities preSetCaps, AppiumDriverRequest request) { - return request.getDesiredCapabilities().merge(preSetCaps); - } + // general caps + requestCapabilities.setCapability("testName", ExecutionContextController.getCurrentExecutionContext().runConfig.getReportName()); - /** - * Sets an capability value if the existing value doesn't match the same type, - * is an empty string or doesn't exists. - * @todo Move this to Testerra core - * @param capabilities - * @param capabilityName - * @param capability - * @param - */ - private void setCapabilityIfAbsent(DesiredCapabilities capabilities, String capabilityName, T capability) { - Object existingCapability = capabilities.getCapability(capabilityName); - if (!capability.getClass().isInstance(existingCapability) || (existingCapability instanceof String && StringUtils.isBlank((String)existingCapability))) { - capabilities.setCapability(capabilityName, capability); + switch (webDriverRequest.getBrowser()) { + case Browsers.mobile_safari: { + finalRequest.setDeviceQuery(PropertyManager.getProperty("tt.mobile.device.query.ios", "@os='ios' and @category='PHONE'")); + break; + } + case Browsers.mobile_chrome: { + finalRequest.setDeviceQuery(PropertyManager.getProperty("tt.mobile.device.query.android", "@os='android' and @category='PHONE'")); + break; + } } + + return finalRequest; } @Override - protected WebDriver getRawWebDriver(AppiumDriverRequest webDriverRequest, DesiredCapabilities desiredCapabilities, SessionContext sessionContext) { - - final String GRID_ACCESS_KEY = PropertyManager.getProperty("tt.mobile.grid.access.key"); - final String GRID_URL = PropertyManager.getProperty("tt.mobile.grid.url"); - final String APPIUM_DEVICE_QUERY_IOS = PropertyManager.getProperty("tt.mobile.device.query.ios", "@os='ios' and @category='PHONE'"); - final String APPIUM_DEVICE_QUERY_ANDROID = PropertyManager.getProperty("tt.mobile.device.query.android", "@os='android' and @category='PHONE'"); + public WebDriver createWebDriver(WebDriverRequest webDriverRequest, SessionContext sessionContext) { + AppiumDriverRequest appiumDriverRequest = (AppiumDriverRequest)webDriverRequest; + DesiredCapabilities requestCapabilities = appiumDriverRequest.getDesiredCapabilities(); + URL appiumUrl = appiumDriverRequest.getServerUrl().get(); + DesiredCapabilities finalCapabilities = new DesiredCapabilities(requestCapabilities); - // early exit. - if (webDriverRequest.getBrowser() == null) { - throw new RuntimeException("DriverRequest was null."); - } - - // general caps - setCapabilityIfAbsent(desiredCapabilities, AppiumDriverRequest.CAPABILITY_NAME_TEST_NAME, ExecutionContextController.getCurrentExecutionContext().runConfig.getReportName()); - desiredCapabilities.setCapability("accessKey", GRID_ACCESS_KEY); + IExecutionContextController executionContextController = Testerra.getInjector().getInstance(IExecutionContextController.class); + DefaultCapabilityUtils utils = new DefaultCapabilityUtils(); + utils.putIfAbsent(finalCapabilities, AppiumDriverRequest.CAPABILITY_NAME_TEST_NAME, executionContextController.getExecutionContext().runConfig.getReportName()); + AppiumDriver appiumDriver = null; switch (webDriverRequest.getBrowser()) { - case MobileBrowsers.mobile_safari: - - desiredCapabilities.setCapability("deviceQuery", APPIUM_DEVICE_QUERY_IOS); - desiredCapabilities.setBrowserName(MobileBrowserType.SAFARI); - - try { - final IOSDriver driver = new IOSDriver<>(new URL(GRID_URL), desiredCapabilities); - final AppiumDeviceQuery appiumDeviceQuery = new AppiumDeviceQuery(driver.getCapabilities()); - log().info("iOS Session created for: " + appiumDeviceQuery.toString()); - webDriverRequest.getBaseUrl().ifPresent(url -> driver.get(url.toString())); - return driver; - } catch (MalformedURLException e) { - throw new SessionNotCreatedException("Could not create session, because URL invalid"); - } - - case MobileBrowsers.mobile_chrome: - - desiredCapabilities.setCapability("deviceQuery", APPIUM_DEVICE_QUERY_ANDROID); - desiredCapabilities.setBrowserName(MobileBrowserType.CHROME); - - try { - final AndroidDriver driver = new AndroidDriver<>(new URL(GRID_URL), desiredCapabilities); - final AppiumDeviceQuery appiumDeviceQuery = new AppiumDeviceQuery(driver.getCapabilities()); - log().info("Android Session created for: " + appiumDeviceQuery.toString()); - webDriverRequest.getBaseUrl().ifPresent(url -> driver.get(url.toString())); - return driver; - } catch (MalformedURLException e) { - throw new SessionNotCreatedException("Could not create session, because URL invalid"); - } - - default: - throw new RuntimeException("Mobile Browser not supported."); + case Browsers.mobile_safari: { + finalCapabilities.setBrowserName(MobileBrowserType.SAFARI); + appiumDriver = new IOSDriver<>(appiumUrl, finalCapabilities); + break; + } + case Browsers.mobile_chrome: { + finalCapabilities.setBrowserName(MobileBrowserType.CHROME); + appiumDriver = new AndroidDriver<>(appiumUrl, finalCapabilities); + break; + } + } + if (appiumDriver != null) { + AppiumDeviceQuery appiumDeviceQuery = new AppiumDeviceQuery(appiumDriver.getCapabilities()); + sessionContext.setActualBrowserName(appiumDeviceQuery.toString()); + } else { + throw new RuntimeException("Mobile Browser not supported: " + webDriverRequest.getBrowser()); } + return appiumDriver; + } + + @Override + public void setupNewWebDriverSession(EventFiringWebDriver webDriver, SessionContext sessionContext) { + AppiumDriverRequest appiumDriverRequest = (AppiumDriverRequest)sessionContext.getWebDriverRequest(); + appiumDriverRequest.getBaseUrl().ifPresent(url -> webDriver.get(url.toString())); } @Override - protected void setupSession(EventFiringWebDriver eventFiringWebDriver, AppiumDriverRequest request) { + public List getSupportedBrowsers() { + return Arrays.asList(Browsers.mobile_chrome, Browsers.mobile_safari); + } + @Override + public GuiElementCore createCore(GuiElementData guiElementData) { + return new AppiumGuiElementCoreAdapter(guiElementData); } } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverManager.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverManager.java index 51ccf03..5b93227 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverManager.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverManager.java @@ -22,6 +22,7 @@ package eu.tsystems.mms.tic.testframework.mobile.driver; +import eu.tsystems.mms.tic.testframework.webdrivermanager.IWebDriverManager; import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverProxy; import io.appium.java_client.AppiumDriver; import io.appium.java_client.MobileElement; @@ -36,6 +37,7 @@ * Time: 11:43 * * @author Eric Kubenka + * @deprecated Use {@link IWebDriverManager#unwrapWebDriver(WebDriver, Class)} instead */ public class AppiumDriverManager { diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverRequest.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverRequest.java deleted file mode 100644 index f1248c1..0000000 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Testerra - * - * (C) 2020, Eric Kubenka, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG - * - * Deutsche Telekom AG and all other contributors / - * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.mobile.driver; - -import eu.tsystems.mms.tic.testframework.webdrivermanager.AbstractWebDriverRequest; -import java.net.URL; -import java.util.Optional; - -/** - * Extends {@link AbstractWebDriverRequest} - * Date: 24.06.2020 - * Time: 13:29 - * - * @author Eric Kubenka - */ -public class AppiumDriverRequest extends AbstractWebDriverRequest { - - public static final String CAPABILITY_NAME_TEST_NAME = "testName"; - - private AppiumDeviceQuery appiumDeviceQuery; - - public AppiumDeviceQuery getAppiumDeviceQuery() { - - return appiumDeviceQuery; - } - - public void setAppiumDeviceQuery(AppiumDeviceQuery appiumDeviceQuery) { - - this.appiumDeviceQuery = appiumDeviceQuery; - } - - @Override - public Optional getServerUrl() { - return Optional.empty(); - } -} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileBrowsers.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileBrowsers.java index c233421..84079fa 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileBrowsers.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileBrowsers.java @@ -22,6 +22,8 @@ package eu.tsystems.mms.tic.testframework.mobile.driver; +import eu.tsystems.mms.tic.testframework.appium.Browsers; + /** * Extends Testerra {@link eu.tsystems.mms.tic.testframework.constants.Browsers} *

@@ -29,9 +31,7 @@ * Time: 09:19 * * @author Eric Kubenka + * @deprecated Use {@link Browsers} instead */ -public class MobileBrowsers { - - public static final String mobile_chrome = "mobile_chrome"; - public static final String mobile_safari = "mobile_safari"; +public class MobileBrowsers extends Browsers { } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreAdapter.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreAdapter.java index 6e40cb9..5516330 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreAdapter.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreAdapter.java @@ -22,18 +22,12 @@ package eu.tsystems.mms.tic.testframework.mobile.guielement; -import eu.tsystems.mms.tic.testframework.common.PropertyManager; -import eu.tsystems.mms.tic.testframework.constants.TesterraProperties; -import eu.tsystems.mms.tic.testframework.exceptions.ElementNotFoundException; import eu.tsystems.mms.tic.testframework.logging.Loggable; import eu.tsystems.mms.tic.testframework.mobile.driver.AppiumDriverManager; import eu.tsystems.mms.tic.testframework.pageobjects.GuiElement; -import eu.tsystems.mms.tic.testframework.pageobjects.Locate; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.AbstractWebDriverCore; import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementCore; import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData; -import eu.tsystems.mms.tic.testframework.pageobjects.internal.frames.FrameLogic; -import eu.tsystems.mms.tic.testframework.utils.JSUtils; -import eu.tsystems.mms.tic.testframework.utils.TimerUtils; import io.appium.java_client.AppiumDriver; import io.appium.java_client.TouchAction; import io.appium.java_client.touch.LongPressOptions; @@ -41,22 +35,11 @@ import io.appium.java_client.touch.WaitOptions; import io.appium.java_client.touch.offset.ElementOption; import io.appium.java_client.touch.offset.PointOption; - -import org.apache.commons.lang3.StringUtils; -import org.openqa.selenium.By; -import org.openqa.selenium.Dimension; -import org.openqa.selenium.Point; -import org.openqa.selenium.StaleElementReferenceException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.time.Duration; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.ui.Select; - -import java.awt.Color; -import java.io.File; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; +import org.openqa.selenium.support.events.EventFiringWebDriver; /** * Implements {@link GuiElementCore} to fullfill Testerra {@link GuiElement} functionality. @@ -65,210 +48,25 @@ * * @author Eric Kubenka */ -public class AppiumGuiElementCoreAdapter implements GuiElementCore, Loggable { +public class AppiumGuiElementCoreAdapter extends AbstractWebDriverCore implements Loggable { - private static final int DELAY_AFTER_GUIELEMENT_FIND_MILLIS = PropertyManager.getIntProperty(TesterraProperties.DELAY_AFTER_GUIELEMENT_FIND_MILLIS); - - private final WebDriver driver; private final AppiumDriver appiumDriver; - private final By by; - private final GuiElementData guiElementData; private static final AppiumDriverManager appiumDriverManager = new AppiumDriverManager(); - public AppiumGuiElementCoreAdapter(By by, WebDriver webDriver, GuiElementData guiElementData) { - - this.by = by; - this.driver = webDriver; - this.guiElementData = guiElementData; - this.appiumDriver = appiumDriverManager.fromWebDriver(this.driver); - } - - @Override - public WebElement getWebElement() { - - find(); - return this.guiElementData.webElement; - } - - @Override - public By getBy() { - - return this.by; - } - - @Override - @Deprecated - public void scrollToElement() { - - this.scrollToElement(0); - } - - @Override - @Deprecated - public void scrollToElement(int yOffset) { - - final Point location = getWebElement().getLocation(); - final int x = location.getX(); - final int y = location.getY() - yOffset; - log().trace("Scrolling into view: " + x + ", " + y); - - JSUtils.executeScript(appiumDriver, "scroll(" + x + ", " + y + ");"); - } - - @Override - public void scrollIntoView() { - - this.scrollIntoView(new Point(0, 0)); - } - - @Override - public void scrollIntoView(Point offset) { - - JSUtils utils = new JSUtils(); - utils.scrollToCenter(appiumDriver, getWebElement(), offset); - } - - @Override - public void select() { - - if (!isSelected()) { - click(); - } - } - - @Override - public void deselect() { - - if (isSelected()) { - click(); - } - } - - @Override - public void type(String text) { - - clear(); - getWebElement().sendKeys(text); - } - - @Override - public void click() { - - getWebElement().click(); - } - - @Override - @Deprecated - public void clickJS() { - - click(); - } - - @Override - @Deprecated - public void clickAbsolute() { - - click(); - } - - @Override - @Deprecated - public void mouseOverAbsolute2Axis() { - - throw new MobileActionNotSupportedException("mouseOverAbsolute2Axis() not supported on mobile GuiElement"); - } - - @Override - public void submit() { - - getWebElement().submit(); - } - - @Override - public void sendKeys(CharSequence... charSequences) { - - getWebElement().sendKeys(charSequences); - } - - @Override - public void clear() { - - getWebElement().clear(); - } - - @Override - public String getTagName() { - - return getWebElement().getTagName(); - } - - @Override - public GuiElement getSubElement(By byLocator, String description) { - - return getSubElement(byLocator).setName(description); - } - - @Override - public GuiElement getSubElement(By byLocator) { - - return getSubElement(Locate.by(byLocator)); - } - - @Override - public GuiElement getSubElement(Locate locator) { - - final FrameLogic frameLogic = guiElementData.frameLogic; - GuiElement[] frames = null; - if (frameLogic != null) { - frames = frameLogic.getFrames(); - } - - String abstractLocatorString = locator.getBy().toString(); - if (abstractLocatorString.toLowerCase().contains("xpath")) { - int i = abstractLocatorString.indexOf(":") + 1; - String xpath = abstractLocatorString.substring(i).trim(); - String prevXPath = xpath; - // Check if locator does not start with dot, ignoring a leading parenthesis for choosing the n-th element - if (xpath.startsWith("/")) { - xpath = xpath.replaceFirst("/", "./"); - log().warn(String.format("Replaced absolute xpath locator \"%s\" to relative: \"%s\"", prevXPath, xpath)); - locator = Locate.by(By.xpath(xpath)); - } else if (!xpath.startsWith(".")) { - xpath = "./" + xpath; - log().warn(String.format("Added relative xpath locator for children to \"%s\": \"%s\"", prevXPath, xpath)); - locator = Locate.by(By.xpath(xpath)); - } - } - - GuiElement subElement = new GuiElement(this.driver, locator, frames); - subElement.setParent(this); - return subElement; - } - - @Override - public Point getLocation() { - - return this.getWebElement().getLocation(); + public AppiumGuiElementCoreAdapter(GuiElementData guiElementData) { + super(guiElementData); + this.appiumDriver = appiumDriverManager.fromWebDriver(this.guiElementData.getWebDriver()); } @Override - public Dimension getSize() { + protected void switchToDefaultContent(WebDriver webDriver) { - return this.getWebElement().getSize(); } @Override - public String getCssValue(String cssIdentifier) { + protected void switchToFrame(WebDriver webDriver, WebElement webElement) { - return this.getWebElement().getCssValue(cssIdentifier); - } - - @Override - @Deprecated - public void mouseOver() { - - this.hover(); } @Override @@ -280,149 +78,39 @@ public void hover() { @Override public void contextClick() { + this.findWebElement(webElement -> { + final ElementOption elementOption = new ElementOption().withElement(webElement); + final TouchAction action = new TouchAction<>(appiumDriver); - final ElementOption elementOption = new ElementOption().withElement(appiumDriver.findElement(this.by)); - final TouchAction action = new TouchAction<>(appiumDriver); - - action.longPress(new LongPressOptions().withElement(elementOption)); - action.perform(); - } - - @Override - @Deprecated - public void mouseOverJS() { - - this.mouseOver(); - } - - @Override - public Select getSelectElement() { - - return new Select(this.getWebElement()); - } - - @Override - public List getTextsFromChildren() { - - final List childElements = this.getWebElement().findElements(By.xpath(".//*")); - final ArrayList childTexts = new ArrayList<>(); - - for (final WebElement childElement : childElements) { - final String text = childElement.getText(); - if (StringUtils.isNotBlank(text)) { - childTexts.add(text); - } - } - - return childTexts; + action.longPress(new LongPressOptions().withElement(elementOption)); + action.perform(); + }); } @Override public void doubleClick() { + this.findWebElement(webElement -> { + final ElementOption elementOption = new ElementOption().withElement(webElement); + final TouchAction action = new TouchAction<>(appiumDriver); - final ElementOption elementOption = new ElementOption().withElement(getWebElement()); - final TouchAction action = new TouchAction<>(appiumDriver); - - final TapOptions tapOptions = new TapOptions().withTapsCount(2).withElement(elementOption); - action.tap(tapOptions).perform(); - } - - @Override - public void highlight(Color color) { - - JSUtils.highlightWebElement(appiumDriver, this.getWebElement(), color); + final TapOptions tapOptions = new TapOptions().withTapsCount(2).withElement(elementOption); + action.tap(tapOptions).perform(); + }); } @Override public void swipe(int offsetX, int offsetY) { + this.findWebElement(webElement -> { + TouchAction touchAction = new TouchAction(appiumDriver); - TouchAction touchAction = new TouchAction(appiumDriver); - - final TapOptions tapOption = new TapOptions().withElement(new ElementOption().withElement(getWebElement())); - touchAction.tap(tapOption); - touchAction.waitAction(new WaitOptions().withDuration(Duration.ofMillis(1500))); - touchAction.moveTo(new PointOption().withCoordinates(offsetX, offsetY)); - touchAction.waitAction(new WaitOptions().withDuration(Duration.ofMillis(1500))); - touchAction.release(); - touchAction.perform(); - } - - @Override - public int getLengthOfValueAfterSendKeys(String textToInput) { - - this.sendKeys(textToInput); - return this.getWebElement().getAttribute("value").length(); - } - - @Override - public int getNumberOfFoundElements() { - - // isPresent() is the save way of getWebElement() - if (isPresent()) { - return this.find(); - } - - return 0; - } - - @Override - @Deprecated - public void rightClick() { - - this.contextClick(); - } - - @Override - @Deprecated - public void rightClickJS() { - - this.contextClick(); - } - - @Override - @Deprecated - public void doubleClickJS() { - - this.doubleClick(); - } - - @Override - public File takeScreenshot() { - - // TODO takeScreenshot - return null; - } - - @Override - public boolean isPresent() { - - try { - log().debug("isPresent(): trying to find WebElement"); - find(); - } catch (Exception e) { - log().debug("isPresent(): Element not found: " + by, e); - return false; - } - return true; - } - - @Override - public boolean isEnabled() { - - return getWebElement().isEnabled(); - } - - @Override - public boolean anyFollowingTextNodeContains(String contains) { - - final GuiElement textNode = this.getSubElement(Locate.by(By.xpath(String.format(".//*[contains(text(),'%s')]", contains)))); - return textNode.isPresent(); - } - - @Override - public boolean isDisplayed() { - - return getWebElement().isDisplayed(); + final TapOptions tapOption = new TapOptions().withElement(new ElementOption().withElement(webElement)); + touchAction.tap(tapOption); + touchAction.waitAction(new WaitOptions().withDuration(Duration.ofMillis(1500))); + touchAction.moveTo(new PointOption().withCoordinates(offsetX, offsetY)); + touchAction.waitAction(new WaitOptions().withDuration(Duration.ofMillis(1500))); + touchAction.release(); + touchAction.perform(); + }); } @Override @@ -459,22 +147,13 @@ public boolean isVisible(boolean complete) { @Override public boolean isSelected() { - - final String checked = this.getWebElement().getAttribute("checked"); - final String selected = this.getWebElement().getAttribute("selected"); - return checked.equalsIgnoreCase("true") || selected.equalsIgnoreCase("true"); - } - - @Override - public String getText() { - - return this.getWebElement().getText(); - } - - @Override - public String getAttribute(String attributeName) { - - return this.getWebElement().getAttribute(attributeName); + AtomicBoolean atomicBoolean = new AtomicBoolean(false); + this.findWebElement(webElement -> { + final String checked = webElement.getAttribute("checked"); + final String selected = webElement.getAttribute("selected"); + atomicBoolean.set(checked.equalsIgnoreCase("true") || selected.equalsIgnoreCase("true")); + }); + return atomicBoolean.get(); } @Override @@ -483,77 +162,4 @@ public boolean isSelectable() { throw new MobileActionNotSupportedException("isSelectable() is not supported on mobile elements"); } - - private int find() { - - int numberOfFoundElements = 0; - guiElementData.webElement = null; - GuiElementCore parent = guiElementData.parent; - Exception notFoundCause = null; - try { - List elements; - if (parent != null) { - elements = parent.getWebElement().findElements(by); - } else { - elements = this.appiumDriver.findElements(by); - } - if (elements != null) { - final Locate selector = guiElementData.guiElement.getLocator(); - Predicate filter = selector.getFilter(); - if (filter != null) { - elements.removeIf(webElement -> !filter.test(webElement)); - } - if (selector.isUnique() && elements.size() > 1) { - throw new Exception("To many WebElements found (" + elements.size() + ")"); - } - numberOfFoundElements = elements.size(); - - setWebElement(elements); - } - } catch (Exception e) { - notFoundCause = e; - } - - throwExceptionIfWebElementIsNull(notFoundCause); - - if (DELAY_AFTER_GUIELEMENT_FIND_MILLIS > 0) { - TimerUtils.sleep(DELAY_AFTER_GUIELEMENT_FIND_MILLIS); - } - - return numberOfFoundElements; - } - - private void setWebElement(List elements) { - - int numberOfFoundElements = elements.size(); - if (numberOfFoundElements < guiElementData.index + 1) { - throw new StaleElementReferenceException("Request index of GuiElement was " + guiElementData.index + ", but only " + numberOfFoundElements + " were found. There you go, GuiElementList-Fanatics!"); - } - - if (numberOfFoundElements > 0) { - - WebElement webElement = elements.get(Math.max(0, guiElementData.index)); - - // check for shadowRoot - if (guiElementData.shadowRoot) { - Object o = JSUtils.executeScript(appiumDriver, "return arguments[0].shadowRoot", webElement); - if (o instanceof WebElement) { - webElement = (WebElement) o; - } - } - - // set webelement - guiElementData.webElement = webElement; - GuiElementData.WEBELEMENT_MAP.put(webElement, guiElementData.guiElement); - } else { - log().debug("find(): GuiElement " + toString() + " was NOT found. Element list has 0 entries."); - } - } - - private void throwExceptionIfWebElementIsNull(Exception cause) { - if (guiElementData.webElement == null) { - throw new ElementNotFoundException(this.guiElementData.guiElement, cause); - } - } - } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreFactory.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreFactory.java deleted file mode 100644 index 757eb59..0000000 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/AppiumGuiElementCoreFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Testerra - * - * (C) 2020, Eric Kubenka, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG - * - * Deutsche Telekom AG and all other contributors / - * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.mobile.guielement; - -import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementCore; -import eu.tsystems.mms.tic.testframework.pageobjects.internal.core.GuiElementData; -import eu.tsystems.mms.tic.testframework.pageobjects.internal.creation.GuiElementCoreFactory; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; - -/** - * Implements {@link GuiElementCoreFactory} to create Appium base {@link GuiElementCore} implementations - * Date: 24.06.2020 - * Time: 13:18 - * - * @author Eric Kubenka - */ -public class AppiumGuiElementCoreFactory implements GuiElementCoreFactory { - - @Override - public GuiElementCore create(By by, WebDriver webDriver, GuiElementData guiElementData) { - - return new AppiumGuiElementCoreAdapter(by, webDriver, guiElementData); - } -} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/AppiumDriverRequest.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/AppiumDriverRequest.java new file mode 100644 index 0000000..4daf103 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/AppiumDriverRequest.java @@ -0,0 +1,63 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.webdrivermanager; + +import eu.tsystems.mms.tic.testframework.common.PropertyManager; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; + +public class AppiumDriverRequest extends SeleniumWebDriverRequest { + + private final String DEVICE_QUERY = "deviceQuery"; + private final String ACCESS_KEY = "accessKey"; + public static final String CAPABILITY_NAME_TEST_NAME = "testName"; + + public AppiumDriverRequest() { + super(); + setAccessKey(PropertyManager.getProperty("tt.mobile.grid.access.key")); + } + + @Override + public Optional getServerUrl() { + if (!super.getServerUrl().isPresent()) { + try { + this.setServerUrl(PropertyManager.getProperty("tt.mobile.grid.url")); + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to retrieve default Appium URL from properties", e); + } + } + return super.getServerUrl(); + } + + public void setDeviceQuery(String deviceQuery) { + this.getDesiredCapabilities().setCapability(DEVICE_QUERY, deviceQuery); + } + +// public Optional getDeviceQuery() { +// return Optional.ofNullable(this.getDesiredCapabilities().getCapability(DEVICE_QUERY).toString()); +// } + + public void setAccessKey(String accessKey) { + this.getDesiredCapabilities().setCapability(ACCESS_KEY, accessKey); + } +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/WinAppDriverRequest.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/WinAppDriverRequest.java new file mode 100644 index 0000000..9273aee --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/webdrivermanager/WinAppDriverRequest.java @@ -0,0 +1,167 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.webdrivermanager; + +import eu.tsystems.mms.tic.testframework.appium.Browsers; +import eu.tsystems.mms.tic.testframework.appium.WinAppDriverFactory; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class WinAppDriverRequest extends AbstractWebDriverRequest implements Loggable { + public static final String TOP_LEVEL_WINDOW="appTopLevelWindow"; + public static final String DEVICE_NAME="deviceName"; + public static final String APP_ID="app"; + public static final String WORKING_DIR="appWorkingDir"; + public static final String APP_ARGUMENTS ="appArguments"; + public static final String APP_ID_DESKTOP="Root"; + public static final String WAIT_FOR_APP_LAUNCH="ms:waitForAppLaunch"; + + private String reuseApplicationByWindowTitle = null; + private String appId = null; + private int startupTimeoutSeconds = 0; + private int reuseTimeoutSeconds = 0; + + public WinAppDriverRequest() { + super(); + setBrowser(Browsers.windows); + setStartupTimeoutSeconds(WinAppDriverFactory.Properties.STARTUP_TIMEOUT_SECONDS.asLong().intValue()); + setReuseTimeoutSeconds(WinAppDriverFactory.Properties.REUSE_TIMEOUT_SECONDS.asLong().intValue()); + } + + public void setDesktopApplication() { + this.setApplication(APP_ID_DESKTOP); + } + + /** + * Tries to reuse a driver by an given application window title. + */ + public void reuseApplicationByWindowTitle(String applicationTitle) { + this.reuseApplicationByWindowTitle = applicationTitle; + if (DEFAULT_SESSION_KEY.equals(this.getSessionKey())) { + this.setSessionKey(applicationTitle); + } + } + + public Optional getReusableApplicationWindowTitle() { + return Optional.ofNullable(this.reuseApplicationByWindowTitle); + } + + public void setApplicationPath(String applicationPath) { + this.setApplicationPath(Paths.get(applicationPath)); + } + + public void setApplicationPath(Path applicationPath) { +// if (!Files.exists(applicationPath)) { +// throw new RuntimeException("Application not found: " + applicationPath); +// } + + String applicationPathString = applicationPath.toString(); + this.setApplication(applicationPathString); + + Path parent = applicationPath.getParent(); + if (parent != null) { + this.setWorkingDir(parent); + } else { + /** + * When there is no parent, try to find the base path, + * by regular expression. + * This is a workaround for a Java bug (maybe Java 8) + */ + Pattern basePathRegex = Pattern.compile("(.*)[\\\\\\/][^\\\\\\/]+$"); + Matcher matcher = basePathRegex.matcher(applicationPathString); + if (matcher.matches()) { + String group = matcher.group(1); + setWorkingDir(group); + } + } + } + + /** + * Sets the application id and the session key. + */ + public void setApplication(String applicationId) { + this.appId = applicationId; + if (applicationId != null) { + this.setSessionKey(applicationId); + } + } + + /** + * Removes the application id + */ + public void unsetApplication() { + this.appId = null; + } + + public Optional getApplicationId() { + return Optional.ofNullable(this.appId); + } + + public void setWorkingDir(Path workingDir) { + this.setWorkingDir(workingDir.toString()); + } + + public void setWorkingDir(String workingDir) { + this.getDesiredCapabilities().setCapability(WORKING_DIR, workingDir); + } + + public void setApplicationArguments(String ... argv) { + this.getDesiredCapabilities().setCapability(APP_ARGUMENTS, String.join(" ", argv)); + } + + @Override + public Optional getServerUrl() { + if (!super.getServerUrl().isPresent()) { + try { + setServerUrl(WinAppDriverFactory.Properties.WINAPP_SERVER_URL.asString()); + } catch (MalformedURLException e) { + log().error("Invalid value for property: " + WinAppDriverFactory.Properties.WINAPP_SERVER_URL); + } + } + return super.getServerUrl(); + } + + public int getStartupTimeoutSeconds() { + return this.startupTimeoutSeconds; + } + + public void setStartupTimeoutSeconds(int startupTimeoutSeconds) { + this.startupTimeoutSeconds = startupTimeoutSeconds; + this.getDesiredCapabilities().setCapability(WAIT_FOR_APP_LAUNCH, startupTimeoutSeconds); + } + + public int getReuseTimeoutSeconds() { + return reuseTimeoutSeconds; + } + + public void setReuseTimeoutSeconds(int reuseTimeoutSeconds) { + this.reuseTimeoutSeconds = reuseTimeoutSeconds; + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/appium/windows/CalculatorApp.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/appium/windows/CalculatorApp.java new file mode 100644 index 0000000..e46d2fd --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/appium/windows/CalculatorApp.java @@ -0,0 +1,53 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.appium.windows; + +import eu.tsystems.mms.tic.testframework.pageobjects.Page; +import eu.tsystems.mms.tic.testframework.pageobjects.PreparedLocator; +import eu.tsystems.mms.tic.testframework.pageobjects.TestableUiElement; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; +import org.openqa.selenium.WebDriver; + +public class CalculatorApp extends Page { + + private final PreparedLocator automationLocator = LOCATE.prepare("//*[@AutomationId=\"%s\"]"); + + private final UiElement num1Btn = find(automationLocator.with("num1Button")); + private final UiElement num3Btn = find(automationLocator.with("num3Button")); + private final UiElement num7Btn = find(automationLocator.with("num7Button")); + private final UiElement results = find(automationLocator.with("CalculatorResults")); + + public CalculatorApp(WebDriver webDriver) { + super(webDriver); + } + + public void typeSomething() { + num1Btn.click(); + num3Btn.click(); + num3Btn.click(); + num7Btn.click(); + } + + public TestableUiElement getResults() { + return results; + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/TesterraAppiumDriverTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/TesterraAppiumDriverTest.java index 74c12ca..efcaa0a 100644 --- a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/TesterraAppiumDriverTest.java +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/TesterraAppiumDriverTest.java @@ -22,18 +22,15 @@ package eu.tsystems.mms.tic.testframework.mobile.test.driver; -import eu.tsystems.mms.tic.testframework.internal.Viewport; -import eu.tsystems.mms.tic.testframework.mobile.driver.AppiumDriverRequest; import eu.tsystems.mms.tic.testframework.mobile.test.AbstractAppiumTest; import eu.tsystems.mms.tic.testframework.report.model.context.Screenshot; -import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext; +import eu.tsystems.mms.tic.testframework.testing.WebDriverManagerProvider; import eu.tsystems.mms.tic.testframework.utils.JSUtils; import eu.tsystems.mms.tic.testframework.utils.UITestUtils; -import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverManager; -import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverSessionsManager; +import eu.tsystems.mms.tic.testframework.utils.WebDriverUtils; import io.appium.java_client.AppiumDriver; import io.appium.java_client.MobileElement; -import java.util.Map; +import org.openqa.selenium.Rectangle; import org.openqa.selenium.ScreenOrientation; import org.openqa.selenium.WebDriver; import org.testng.Assert; @@ -45,37 +42,29 @@ * * @author Eric Kubenka */ -public class TesterraAppiumDriverTest extends AbstractAppiumTest { +public class TesterraAppiumDriverTest extends AbstractAppiumTest implements WebDriverManagerProvider { @Test public void testT01_startDefaultSession() { - String expectedTestName = "testT01_startDefaultSession"; - - AppiumDriverRequest appiumDriverRequest = new AppiumDriverRequest(); - appiumDriverRequest.getDesiredCapabilities().setCapability(AppiumDriverRequest.CAPABILITY_NAME_TEST_NAME, expectedTestName); - - WebDriver webDriver = WebDriverManager.getWebDriver(appiumDriverRequest); - AppiumDriver appiumDriver = appiumDriverManager.fromWebDriver(webDriver); - - appiumDriver.rotate(ScreenOrientation.LANDSCAPE); - webDriver.get("https://the-internet.herokuapp.com/dropdown"); - - Assert.assertEquals(appiumDriver.getCapabilities().getCapability(AppiumDriverRequest.CAPABILITY_NAME_TEST_NAME), expectedTestName); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); + WEB_DRIVER_MANAGER.unwrapWebDriver(driver, AppiumDriver.class).ifPresent(appiumDriver -> { + appiumDriver.rotate(ScreenOrientation.LANDSCAPE); + }); + driver.get("https://the-internet.herokuapp.com/dropdown"); } @Test public void testT02_startMultipleSessions() { - final WebDriver driver = WebDriverManager.getWebDriver(); - final AppiumDriver appiumDriver = appiumDriverManager.fromWebDriver(driver); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); + AppiumDriver appiumDriver = WEB_DRIVER_MANAGER.unwrapWebDriver(driver, AppiumDriver.class).get(); appiumDriver.rotate(ScreenOrientation.LANDSCAPE); driver.get("https://the-internet.herokuapp.com/dropdown"); - - final WebDriver driver2 = WebDriverManager.getWebDriver("second"); - final AppiumDriver appiumDriver2 = appiumDriverManager.fromWebDriver(driver2); + final WebDriver driver2 = WEB_DRIVER_MANAGER.getWebDriver("second"); + AppiumDriver appiumDriver2 = WEB_DRIVER_MANAGER.unwrapWebDriver(driver2, AppiumDriver.class).get(); appiumDriver2.rotate(ScreenOrientation.PORTRAIT); driver2.get("https://the-internet.herokuapp.com/checkboxes"); @@ -85,19 +74,18 @@ public void testT02_startMultipleSessions() { @Test public void testT03_startSessionTwice() { - - final WebDriver driver = WebDriverManager.getWebDriver(); - final AppiumDriver appiumDriver = appiumDriverManager.fromWebDriver(driver); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); driver.get("https://the-internet.herokuapp.com/dropdown"); - Assert.assertEquals(WebDriverManager.getWebDriver(), driver, "Driver equals"); + Assert.assertEquals(WEB_DRIVER_MANAGER.getWebDriver(), driver, "Driver equals"); } @Test public void testT04_takeScreenshot() { - final WebDriver driver = WebDriverManager.getWebDriver(); - final AppiumDriver appiumDriver = appiumDriverManager.fromWebDriver(driver); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); + AppiumDriver appiumDriver = WEB_DRIVER_MANAGER.unwrapWebDriver(driver, AppiumDriver.class).get(); + driver.get("https://the-internet.herokuapp.com/"); appiumDriver.rotate(ScreenOrientation.LANDSCAPE); @@ -112,7 +100,7 @@ public void testT04_takeScreenshot() { @Test public void testT05_executeJavaScript() { - final WebDriver driver = WebDriverManager.getWebDriver(); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); driver.get("https://the-internet.herokuapp.com/"); final Object returnValue = JSUtils.executeScript(driver, "var test ='test'; return test;"); @@ -123,10 +111,10 @@ public void testT05_executeJavaScript() { @Test public void testT06_getViewport() { - final WebDriver driver = WebDriverManager.getWebDriver(); + final WebDriver driver = WEB_DRIVER_MANAGER.getWebDriver(); driver.get("https://the-internet.herokuapp.com/"); - final Viewport viewport = JSUtils.getViewport(driver); + Rectangle viewport = WebDriverUtils.getViewport(driver); Assert.assertNotNull(viewport, "JSUtils Viewport received."); Object x = JSUtils.executeScript(driver, "return window.pageXOffset.toString();"); diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/WinAppDriverTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/WinAppDriverTest.java new file mode 100644 index 0000000..ee278e4 --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/WinAppDriverTest.java @@ -0,0 +1,83 @@ +/* + * Testerra + * + * (C) 2021, Mike Reiche, T-Systems MMS GmbH, Deutsche Telekom AG + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you 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 eu.tsystems.mms.tic.testframework.mobile.test.driver; + +import eu.tsystems.mms.tic.testframework.common.Testerra; +import eu.tsystems.mms.tic.testframework.report.model.context.MethodContext; +import eu.tsystems.mms.tic.testframework.report.model.context.Screenshot; +import eu.tsystems.mms.tic.testframework.report.utils.IExecutionContextController; +import eu.tsystems.mms.tic.testframework.testing.TesterraTest; +import eu.tsystems.mms.tic.testframework.utils.UITestUtils; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WinAppDriverRequest; +import eu.tsystems.mms.tic.testframework.appium.windows.CalculatorApp; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.testing.AssertProvider; +import eu.tsystems.mms.tic.testframework.testing.PageFactoryProvider; +import eu.tsystems.mms.tic.testframework.testing.UiElementFinderFactoryProvider; +import eu.tsystems.mms.tic.testframework.testing.WebDriverManagerProvider; +import org.openqa.selenium.WebDriver; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +public class WinAppDriverTest extends TesterraTest implements + WebDriverManagerProvider, + PageFactoryProvider, + UiElementFinderFactoryProvider, + Loggable, + AssertProvider +{ + + @Test + public void test_calculate() { + WinAppDriverRequest winAppDriverRequest = new WinAppDriverRequest(); + winAppDriverRequest.setApplication("Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"); + + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(winAppDriverRequest); + CalculatorApp calculatorApp = PAGE_FACTORY.createPage(CalculatorApp.class, webDriver); + calculatorApp.typeSomething(); + calculatorApp.getResults().expect().text().contains("1.337").is(true); + } + + @Test + public void test_takeScreenshot() { + WinAppDriverRequest winAppDriverRequest = new WinAppDriverRequest(); + winAppDriverRequest.setApplication("Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"); + + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(winAppDriverRequest); + + UITestUtils.takeScreenshot(webDriver, true); + + IExecutionContextController executionContextController = Testerra.getInjector().getInstance(IExecutionContextController.class); + MethodContext methodContext = executionContextController.getCurrentMethodContext().get(); + + long numScreenshots = methodContext.readTestSteps() + .flatMap(testStep -> testStep.getCurrentTestStepAction().readEntries(Screenshot.class)) + .count(); + + ASSERT.assertEquals(1, numScreenshots); + } + + @AfterMethod + public void tearDown() { + + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobileGuiElementTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobileGuiElementTest.java index 37011c6..55f3ac3 100644 --- a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobileGuiElementTest.java +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobileGuiElementTest.java @@ -246,10 +246,10 @@ public void testT15_withFrames() { final GuiElement frameBottom = new GuiElement(driver, Locate.by(By.xpath(".//frame[@name='frame-bottom']"))); frameBottom.asserts().assertIsPresent(); - final GuiElement frameTopMiddle = new GuiElement(driver, Locate.by(By.xpath("//frame[@name='frame-middle']")), frameTop); + final GuiElement frameTopMiddle = (GuiElement) frameTop.find(By.xpath("//frame[@name='frame-middle']")); frameTopMiddle.asserts().assertIsPresent(); - final GuiElement topMiddleContent = new GuiElement(driver, Locate.by(By.xpath("//*[@id='content' and text()='MIDDLE']")), frameTopMiddle); + final GuiElement topMiddleContent = (GuiElement) frameTopMiddle.find(By.xpath("//*[@id='content' and text()='MIDDLE']")); topMiddleContent.asserts().assertIsPresent(); final GuiElement frameTopLeft = frameTop.getSubElement(Locate.by(By.xpath(".//frame[@name='frame-left']"))); diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobilePageTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobilePageTest.java index 4ef1839..411e82d 100644 --- a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobilePageTest.java +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/guielement/MobilePageTest.java @@ -22,7 +22,7 @@ package eu.tsystems.mms.tic.testframework.mobile.test.guielement; -import eu.tsystems.mms.tic.testframework.exceptions.PageNotFoundException; +import eu.tsystems.mms.tic.testframework.exceptions.PageFactoryException; import eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.LoginPage; import eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.StartPage; import eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.TablePage; @@ -52,7 +52,7 @@ public void testT01_instantiatePage() { FooterComponent footerComponent = PageFactory.create(FooterComponent.class, driver); } - @Test(expectedExceptions = PageNotFoundException.class) + @Test(expectedExceptions = PageFactoryException.class) public void testT02_instantiatePageFailed() { final WebDriver driver = WebDriverManager.getWebDriver(); diff --git a/appium/src/test/resources/test.properties b/appium/src/test/resources/test.properties index 169bc61..f48fddc 100644 --- a/appium/src/test/resources/test.properties +++ b/appium/src/test/resources/test.properties @@ -1,7 +1,12 @@ -tt.browser=mobile_chrome -#tt.browser=mobile_safari +#tt.browser=mobile_chrome +tt.browser=mobile_safari tt.watchdog.enable=false tt.baseurl=https://the-internet.herokuapp.com/ tt.mobile.device.query.ios=@os='ios' and @category='PHONE' tt.mobile.device.query.android=@os='android' and @category='PHONE' and @version='10' + +tt.winapp.server.url= + +tt.mobile.grid.url= +tt.mobile.grid.access.key diff --git a/build.gradle b/build.gradle index c4d5094..ae7c49f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,10 +6,10 @@ apply plugin: 'io.codearte.nexus-staging' ext { // Minimum required Testerra version - testerraCompileVersion = '1.9' + testerraCompileVersion = '2.0' // Unit tests use the latest Testerra version - testerraTestVersion = '[1.9,2-SNAPSHOT)' - moduleVersion = '1-SNAPSHOT' + testerraTestVersion = '[2,3-SNAPSHOT)' + moduleVersion = '2-SNAPSHOT' if (System.properties.containsKey('moduleVersion')) { moduleVersion = System.getProperty('moduleVersion') } @@ -33,11 +33,9 @@ subprojects { apply plugin: 'maven-publish' apply plugin: 'signing' - // important! repositories { mavenLocal() mavenCentral() - jcenter() } test {