diff --git a/README.md b/README.md index 162f8243..eb6871fd 100644 --- a/README.md +++ b/README.md @@ -78,268 +78,7 @@ Maven: ## Documentation -### Use appium features - -The Appium connector will register `mobile_chrome` and `mobile_safari` as available browser configurations for your `{browser}` -value. Further the connector will provide the `AppiumDriverManager` that you can use to unlock appium related features on the -implementation of `WebDriver` interface. - -*Appium test class* - -```java -public class ExampleTest extends TesterraTest { - - @Test - public void testT01_My_first_test() { - - WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(); - WEB_DRIVER_MANAGER.unwrapWebDriver(webDriver, AppiumDriver.class).ifPresent(appiumDriver -> { - appiumDriver.rotate(ScreenOrientation.LANDSCAPE); - }); - } -} -``` - -### Device filtering - -If you have to run your tests on specific mobile devices available on your mobile device farm, you can use -the `tt.mobile.device.query.android` and `tt.mobile.device.query.ios` to filter for your devices by different properties. To reserve -a device and run tests on it you can simply use `WebDriverManager.getWebDriver()` call. If one or more device(s) matches your -filter, the driver will be instantiated, otherwise the call will fail. - -The following query attributes are available for the device query strings. - -- @category -- @os -- @version -- @manufacture -- @model -- @name - -Please keep in mind, that you have to specify the device query for each operating system with the properties mentioned above. The -default values will provide you a device with given operating system and of `@category PHONE`. - -#### Screenshots - -Screenshots on test case failure works out of the box, because Appium is implementing the necessary interfaces of Selenium to achieve this. - -### 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 at Appium server, if needed. | -| tt.mobile.device.query.ios | "@os='ios' and @category='PHONE'" | Define the requested iOS device. | -| tt.mobile.device.query.android | "@os='android' and @category='PHONE'" | Define the requested Android device. | - - -### 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); -``` - -### Using Experitest SeeTest as Appium server - -For Experitest SeeTest as Appium platform we provide the additional module ``appium-seetest`` with the following features: - -* Video support -* Define Appium version - -Gradle: - -```groovy -// Version from this module -implementation 'io.testerra:appium-seetest:2.0' -``` - -Maven: - -```xml - - - io.testerra - appium-seetest - 2.0 - -``` - -#### Video support - -Add the following properties: - -```properties -# Comes from Testerra -tt.screencaster.active=true -# Optional for get a video for every test -tt.appium.seetest.video.onsuccess=true -``` - -#### Appium version - -Add the following property: - -```properties -tt.appium.seetest.appium.version=1.22.3 -``` - -#### Properties - -| Property | default | Description | -|------------------------------------------|---------|---------------------------------------------| -| tt.appium.seetest.video.onsuccess | false | Get a video for passed tests. | -| tt.appium.seetest.video.onfailed | true | Get a video for failed tests. | -| tt.appium.seetest.video.download.timeout | 20 sec | Set the timeout for video download. | -| tt.appium.seetest.appium.version | na. | Set the Appium version of SeeTest platform. | - -## 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.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));` +Please have a look to the [Wiki pages](https://github.com/telekom/testerra-appium-connector/wiki). ## Publication diff --git a/appium-seetest/src/main/java/io/testerra/plugins/appium/seetest/utils/SeeTestRestClient.java b/appium-seetest/src/main/java/io/testerra/plugins/appium/seetest/utils/SeeTestRestClient.java index 0b4f5e4c..50651ea8 100644 --- a/appium-seetest/src/main/java/io/testerra/plugins/appium/seetest/utils/SeeTestRestClient.java +++ b/appium-seetest/src/main/java/io/testerra/plugins/appium/seetest/utils/SeeTestRestClient.java @@ -52,20 +52,26 @@ public SeeTestRestClient(String url) { } public Optional getAbout() { - Response response = this.getBuilder("/applications", SeeTestApis.PM).get(); - if (response.getStatus() != Response.Status.OK.getStatusCode()) { - log().debug("No SeeTest host found. (Response status {})", response.getStatus()); - return Optional.empty(); - } + try { + Response response = this.getBuilder("/applications", SeeTestApis.PM).get(); + if (response.getStatus() != Response.Status.OK.getStatusCode()) { + log().debug("No SeeTest host found. (Response status {})", response.getStatus()); + return Optional.empty(); + } - Gson gson = new Gson(); - JsonArray jsonArray = gson.fromJson(response.readEntity(String.class), JsonArray.class); - if (jsonArray.isJsonArray()) { - return Optional.of(jsonArray); - } else { - log().debug("Cannot read about response: {}", response.readEntity(String.class)); + Gson gson = new Gson(); + JsonArray jsonArray = gson.fromJson(response.readEntity(String.class), JsonArray.class); + if (jsonArray.isJsonArray()) { + return Optional.of(jsonArray); + } else { + log().debug("Cannot read about response: {}", response.readEntity(String.class)); + return Optional.empty(); + } + } catch (Exception e) { + log().warn("Cannot get information from SeeTest server"); return Optional.empty(); } + } private Invocation.Builder getBuilder(String path, SeeTestApis api) { diff --git a/appium/build.gradle b/appium/build.gradle index bf20960e..3569e6d2 100644 --- a/appium/build.gradle +++ b/appium/build.gradle @@ -5,7 +5,11 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.0' implementation 'org.apache.commons:commons-lang3:3.12.0' + // For AppiumClassFinder + implementation 'org.reflections:reflections:0.9.12' + testImplementation 'io.testerra:driver-ui:' + testerraTestVersion +// testImplementation 'io.testerra:driver-ui' 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/AppiumContext.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/AppiumContext.java new file mode 100644 index 00000000..237978b1 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/appium/AppiumContext.java @@ -0,0 +1,51 @@ +/* + * Testerra + * + * (C) 2022, Daniel Eckardt, 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 io.appium.java_client.AppiumDriver; + +/** + * Created on 2023-03-27 + * + * @author mgn + */ +public enum AppiumContext { + + /** + * The context ({@link AppiumDriver#getContext()}) of the current view of the application is an operating system native. + */ + NATIVE_APP, + /** + * The context ({@link AppiumDriver#getContext()}) of the current view of the application is a (embedded) browser. {@see + * http://appium.io/docs/en/writing-running-appium/web/hybrid/index.html} + */ + WEBVIEW; + + public static AppiumContext parse(String string) { + // there's only two NATIVE_APP contexts (NATIVE_APP and NATIVE_APP_INSTRUMENTED), + // but an infinite amount of WEBVIEW contexts, like WEBVIEW, SAFARI, CHROMIUM, WEBVIEW_1, WEBVIEW_2 etc. + // that's why we just assume that any context starting with NATIVE_APP is native and the rest WEBVIEW: + AppiumContext result = string == null || string.toLowerCase().startsWith("native_app") ? NATIVE_APP : WEBVIEW; +// LOGGER.debug(String.format("Parsed %s as %s", string, result)); + return result; + } + +} 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 index 026c1f18..155d710b 100644 --- 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 @@ -25,7 +25,9 @@ * A list ob supported browsers */ public class Browsers { - public static final String windows ="windows"; + public static final String windows = "windows"; public static final String mobile_chrome = "mobile_chrome"; public static final String mobile_safari = "mobile_safari"; + // Default value for AppiumDriverRequest + public static final String mobile = "mobile"; } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java index a1d3660a..4735a69a 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/ioc/DriverUi_Appium.java @@ -25,13 +25,14 @@ 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.utils.AppiumExecutionUtils; +import eu.tsystems.mms.tic.testframework.utils.ExecutionUtils; import eu.tsystems.mms.tic.testframework.webdriver.WebDriverFactory; /** * Add AppiumDriverFactory and WinAppDriverFactory - * + *

* Date: 24.06.2020 * Time: 10:29 * @@ -44,5 +45,6 @@ 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); + bind(ExecutionUtils.class).to(AppiumExecutionUtils.class).in(Scopes.SINGLETON); } } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/AppiumPage.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/AppiumPage.java new file mode 100644 index 00000000..59a5593f --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/AppiumPage.java @@ -0,0 +1,54 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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; + +import eu.tsystems.mms.tic.testframework.mobile.guielement.CreateAppiumGuiElementAction; +import eu.tsystems.mms.tic.testframework.pageobjects.Page; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.AbstractPage; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.action.AbstractFieldAction; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.action.SetNameFieldAction; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Optional; + +/** + * Created on 2023-02-09 + * + * @author mgn + */ +public class AppiumPage extends Page { + + public AppiumPage(WebDriver webDriver) { + super(webDriver); + } + + @Override + protected Optional> addCustomFieldActions(Field field, AbstractPage declaringPage) { + log().info("Custom field action"); + CreateAppiumGuiElementAction action = new CreateAppiumGuiElementAction(field, declaringPage); + SetNameFieldAction nameFieldAction = new SetNameFieldAction(field, declaringPage); + return Optional.of(List.of(action, nameFieldAction)); + } + +} 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 fa6b911e..dfa46b89 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 @@ -42,6 +42,7 @@ import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.remote.MobileBrowserType; import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.events.EventFiringWebDriver; @@ -93,6 +94,17 @@ public WebDriverRequest prepareWebDriverRequest(WebDriverRequest webDriverReques } } + switch (webDriverRequest.getBrowser()) { + case Browsers.mobile_chrome: + requestCapabilities.setBrowserName(MobileBrowserType.CHROME); + break; + case Browsers.mobile_safari: + requestCapabilities.setBrowserName(MobileBrowserType.SAFARI); + break; + default: + log().info("No mobile browser requested."); + } + return finalRequest; } @@ -117,26 +129,38 @@ private WebDriver startNewAppiumSession(WebDriverRequest webDriverRequest, Sessi IExecutionContextController executionContextController = Testerra.getInjector().getInstance(IExecutionContextController.class); DefaultCapabilityUtils utils = new DefaultCapabilityUtils(); + // TODO: Move to prepareWebDriverRequest utils.putIfAbsent(finalCapabilities, AppiumDriverRequest.CAPABILITY_NAME_TEST_NAME, executionContextController.getExecutionContext().getRunConfig().getReportName()); AppiumDriver appiumDriver = null; - switch (webDriverRequest.getBrowser()) { - case Browsers.mobile_safari: { - finalCapabilities.setBrowserName(MobileBrowserType.SAFARI); + Platform mobilePlatform = new MobileOsChecker().getPlatform(webDriverRequest); + switch (mobilePlatform) { + case IOS: appiumDriver = new IOSDriver<>(appiumUrl, finalCapabilities); break; - } - case Browsers.mobile_chrome: { - finalCapabilities.setBrowserName(MobileBrowserType.CHROME); + case ANDROID: appiumDriver = new AndroidDriver<>(appiumUrl, finalCapabilities); break; - } } + +// switch (webDriverRequest.getBrowser()) { +// 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()); + throw new RuntimeException("Cannot create new Appium session - ambiguous capabilities found:\n " + finalCapabilities.toString()); +// throw new RuntimeException("Mobile Browser not supported: " + webDriverRequest.getBrowser()); } return appiumDriver; } @@ -149,19 +173,23 @@ public void setupNewWebDriverSession(EventFiringWebDriver webDriver, SessionCont .unwrapWebDriver(webDriver, AppiumDriver.class) .ifPresent(driver -> driverString.set(driver.getClass().toString())); - appiumDriverRequest.getBaseUrl().ifPresent(url -> { - log().info("Open {} on {}", url, driverString.get()); - webDriver.get(url.toString()); - }); + // In case of app automation it es not possible to call a URL + if (StringUtils.isNotBlank(appiumDriverRequest.getDesiredCapabilities().getBrowserName())) { + appiumDriverRequest.getBaseUrl().ifPresent(url -> { + log().info("Open {} on {}", url, driverString.get()); + webDriver.get(url.toString()); + }); + } } @Override public List getSupportedBrowsers() { - return Arrays.asList(Browsers.mobile_chrome, Browsers.mobile_safari); + return Arrays.asList(Browsers.mobile_chrome, Browsers.mobile_safari, Browsers.mobile); } @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 deleted file mode 100644 index 5b932273..00000000 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/AppiumDriverManager.java +++ /dev/null @@ -1,58 +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.IWebDriverManager; -import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverProxy; -import io.appium.java_client.AppiumDriver; -import io.appium.java_client.MobileElement; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.support.events.EventFiringWebDriver; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; - -/** - * Date: 08.09.2020 - * Time: 11:43 - * - * @author Eric Kubenka - * @deprecated Use {@link IWebDriverManager#unwrapWebDriver(WebDriver, Class)} instead - */ -public class AppiumDriverManager { - - /** - * Returns an {@link AppiumDriver} - * - * @param driver {@link WebDriver} - * @return AppiumDriver - */ - public AppiumDriver fromWebDriver(WebDriver driver) { - - final WebDriver rawDriver = ((EventFiringWebDriver) driver).getWrappedDriver(); - final InvocationHandler invocationHandler = Proxy.getInvocationHandler(rawDriver); - final WebDriver rawAppiumDriver = ((WebDriverProxy) invocationHandler).getWrappedWebDriver(); - - return (AppiumDriver) rawAppiumDriver; - } -} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileOsChecker.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileOsChecker.java new file mode 100644 index 00000000..52e3746d --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/driver/MobileOsChecker.java @@ -0,0 +1,76 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.driver; + +import eu.tsystems.mms.tic.testframework.appium.Browsers; +import eu.tsystems.mms.tic.testframework.common.Testerra; +import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext; +import eu.tsystems.mms.tic.testframework.webdrivermanager.IWebDriverManager; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverRequest; +import io.appium.java_client.remote.AndroidMobileCapabilityType; +import io.appium.java_client.remote.IOSMobileCapabilityType; +import io.appium.java_client.remote.MobileCapabilityType; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; + +import java.util.Map; +import java.util.Optional; + +/** + * Created on 2023-03-02 + * + * @author mgn + */ +public class MobileOsChecker { + + public Platform getPlatform(WebDriverRequest webDriverRequest) { + Map capabilities = webDriverRequest.getCapabilities(); + if (webDriverRequest.getBrowser().equals(Browsers.mobile_chrome) + || "Espresso".equals(capabilities.get(MobileCapabilityType.AUTOMATION_NAME)) + || "UiAutomator2".equals(capabilities.get(MobileCapabilityType.AUTOMATION_NAME)) + || "UiAutomator".equals(capabilities.get(MobileCapabilityType.AUTOMATION_NAME)) + || capabilities.containsKey(AndroidMobileCapabilityType.APP_PACKAGE) + || capabilities.containsKey(AndroidMobileCapabilityType.APP_ACTIVITY) + ) { + return Platform.ANDROID; + } + if (webDriverRequest.getBrowser().equals(Browsers.mobile_safari) + || "XCUITest".equals(capabilities.get(MobileCapabilityType.AUTOMATION_NAME)) + || "UIAutomation".equals(capabilities.get(MobileCapabilityType.AUTOMATION_NAME)) + || capabilities.containsKey(IOSMobileCapabilityType.BUNDLE_ID) + ) { + return Platform.IOS; + } + + return Platform.ANY; + } + + public Platform getPlatform(WebDriver driver) { + IWebDriverManager instance = Testerra.getInjector().getInstance(IWebDriverManager.class); + Optional optional = instance.getSessionContext(driver).map(SessionContext::getWebDriverRequest); + if (optional.isPresent()) { + return getPlatform(optional.get()); + } else { + return Platform.ANY; + } + } + +} 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 6e199216..f35b61e7 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,12 +22,14 @@ package eu.tsystems.mms.tic.testframework.mobile.guielement; +import eu.tsystems.mms.tic.testframework.common.Testerra; 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 eu.tsystems.mms.tic.testframework.testing.WebDriverManagerProvider; +import eu.tsystems.mms.tic.testframework.utils.ExecutionUtils; import io.appium.java_client.AppiumDriver; import io.appium.java_client.TouchAction; import io.appium.java_client.touch.LongPressOptions; @@ -40,6 +42,8 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.function.Supplier; /** * Implements {@link GuiElementCore} to fullfill Testerra {@link GuiElement} functionality. @@ -52,6 +56,8 @@ public class AppiumGuiElementCoreAdapter extends AbstractWebDriverCore implement private AppiumDriver appiumDriver; + private ExecutionUtils executionUtils = Testerra.getInjector().getInstance(ExecutionUtils.class); + public AppiumGuiElementCoreAdapter(GuiElementData guiElementData) { super(guiElementData); appiumDriver = WEB_DRIVER_MANAGER.unwrapWebDriver(this.guiElementData.getWebDriver(), AppiumDriver.class).orElseThrow(); @@ -147,9 +153,16 @@ public boolean isVisible(boolean complete) { public boolean isSelected() { 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")); +// final String text = webElement.getText(); + String value = executionUtils.getFailsafe(() -> webElement.getAttribute("value")).orElse(""); + String checked = executionUtils.getFailsafe(() -> webElement.getAttribute("checked")).orElse(""); + + atomicBoolean.set( + "true".equalsIgnoreCase(checked) + || "true".equalsIgnoreCase(selected) + || "1".equals(value) + ); }); return atomicBoolean.get(); } @@ -160,4 +173,42 @@ public boolean isSelectable() { throw new MobileActionNotSupportedException("isSelectable() is not supported on mobile elements"); } + + /** + * Appium on apps does not support webElement.getAttribute("value"), but webElement.getText() is working + */ + @Override + public void type(String text) { + if (text == null) { + log().warn("Text to type is null. Typing nothing."); + return; + } + if (text.isEmpty()) { + log().warn("Text to type is empty!"); + } + + findWebElement(webElement -> { + webElement.clear(); + webElement.sendKeys(text); + +// String valueProperty = webElement.getAttribute("value"); + String valueProperty = webElement.getText(); + if (valueProperty != null) { + if (!valueProperty.equals(text)) { + log().warn("Writing text to input field didn't work. Trying again."); + + webElement.clear(); + webElement.sendKeys(text); + +// if (!webElement.getAttribute("value").equals(text)) { + if (!webElement.getText().equals(text)) { + log().error("Writing text to input field didn't work on second try!"); + } + } + } else { + log().warn("Cannot perform value check after type() because " + this.toString() + + " doesn't have a value property. Consider using sendKeys() instead."); + } + }); + } } diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/CreateAppiumGuiElementAction.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/CreateAppiumGuiElementAction.java new file mode 100644 index 00000000..cb794f34 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/guielement/CreateAppiumGuiElementAction.java @@ -0,0 +1,120 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.guielement; + +import eu.tsystems.mms.tic.testframework.common.Testerra; +import eu.tsystems.mms.tic.testframework.internal.NameableChild; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.mobile.driver.MobileOsChecker; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElementFinder; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.AbstractPage; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.action.AbstractFieldAction; +import eu.tsystems.mms.tic.testframework.report.model.context.SessionContext; +import eu.tsystems.mms.tic.testframework.testing.UiElementFinderFactoryProvider; +import eu.tsystems.mms.tic.testframework.webdrivermanager.IWebDriverManager; +import eu.tsystems.mms.tic.testframework.webdrivermanager.WebDriverRequest; +import io.appium.java_client.pagefactory.DefaultElementByBuilder; +import io.appium.java_client.remote.MobileCapabilityType; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.By; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; + +import java.lang.reflect.Field; +import java.util.Optional; + +/** + * Created on 2023-02-09 + * + * @author mgn + */ +public class CreateAppiumGuiElementAction extends AbstractFieldAction implements UiElementFinderFactoryProvider, Loggable { + + public CreateAppiumGuiElementAction(Field field, AbstractPage declaringPage) { + super(field, declaringPage); + } + + @Override + protected boolean before() { + return true; + } + + @Override + protected void execute() { + try { + // UiElements that have already been created may not be updated with new locator. + if (field.get(this.declaringPage) != null) { + return; + } + } catch (IllegalAccessException e) { + return; + } + + Platform mobilePlatform = new MobileOsChecker().getPlatform(this.declaringPage.getWebDriver()); + String automationName = getAutomationEngine(this.declaringPage.getWebDriver(), mobilePlatform); + + // TODO: Check if no Appium annotations are available --> should be logged because of default by + DefaultElementByBuilder byBuilder = new DefaultElementByBuilder(mobilePlatform.toString(), automationName); + byBuilder.setAnnotated(field); + // Note: The DefaultElementByBuilder creates a default By 'By.id(" optional = instance.getSessionContext(driver).map(SessionContext::getWebDriverRequest); + if (optional.isPresent()) { + String automationEngine = optional.get().getCapabilities().get(MobileCapabilityType.AUTOMATION_NAME).toString(); + if (StringUtils.isNotBlank(automationEngine)) { + return automationEngine; + } else { + // Use default values for automation engine + switch (platform) { + case ANDROID: + return "UiAutomator2"; + case IOS: + return "XCUITest"; + default: + throw new RuntimeException("Cannot get automation engine: Invalid platform " + platform); + } + } + } + throw new RuntimeException("Cannot get automation engine: Cannot get request from WebDriver session."); + } +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumClassFinder.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumClassFinder.java new file mode 100644 index 00000000..3814e37a --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumClassFinder.java @@ -0,0 +1,173 @@ +package eu.tsystems.mms.tic.testframework.mobile.pageobject; + +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.mobile.driver.MobileOsChecker; +import eu.tsystems.mms.tic.testframework.pageobjects.Page; +import eu.tsystems.mms.tic.testframework.pageobjects.PageObject; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; + +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created on 2023-03-13 + * + * @author mgn + */ +public class AppiumClassFinder implements Loggable { + + private static AppiumClassFinder INSTANCE; + + /** + * This call takes some time. It has an impact to the duration of the first page check (takes ca 2-3 seconds longer). + */ + private final Reflections reflections = new Reflections(filter(configure())); + + private AppiumClassFinder() { + } + + public static AppiumClassFinder getInstance() { + if (INSTANCE == null) { + INSTANCE = new AppiumClassFinder(); + } + return INSTANCE; + } + + public Class getBestMatchingClass(Class baseClass, WebDriver webDriver) { + MobileOsChecker osChecker = new MobileOsChecker(); + Platform platform = osChecker.getPlatform(webDriver); + + Class matchedClass = AppiumClassFinder.Caches.getCache(baseClass, platform); + + if (matchedClass == null) { + // scan and fill cache + this.findSubPagesOf(baseClass); + matchedClass = AppiumClassFinder.Caches.getCache(baseClass, platform); + + if (matchedClass == null) { + throw new RuntimeException("Something went wrong scanning this class for sub types: " + baseClass.getName()); + } + } + + return (Class) matchedClass; + } + + private void findSubPagesOf(final Class pageClass) { + log().debug("Searching for subtypes of class {}", pageClass); + Class baseClass = null; + if (!Modifier.isAbstract(pageClass.getModifiers())) { + baseClass = pageClass; + AppiumClassFinder.Caches.setCache(baseClass, this.getMatchingPlatform(baseClass.getSimpleName()), baseClass); + } + + Set> subClasses = reflections.getSubTypesOf((Class) baseClass); + for (Class subClass : subClasses) { + String subClassName = subClass.getSimpleName(); + + if (Modifier.isAbstract(subClass.getModifiers())) { + log().debug("Not taking {} into consideration, because it is abstract", subClassName); + } else { + if (this.matchingPattern(subClassName, baseClass.getSimpleName())) { + AppiumClassFinder.Caches.setCache(baseClass, this.getMatchingPlatform(subClassName), subClass); + } + } + } + } + + public void clearCache() { + AppiumClassFinder.Caches.IMPLEMENTATIONS_CACHE.clear(); + } + + private boolean matchingPattern(String className, String baseClassName) { + Pattern pattern = Pattern.compile("(?i)^(ios|android)" + baseClassName); + Matcher matcher = pattern.matcher(className); + return matcher.find(); + } + + private Platform getMatchingPlatform(String className) { + if (className.toLowerCase().contains(Platform.IOS.toString().toLowerCase())) { + return Platform.IOS; + } + if (className.toLowerCase().contains(Platform.ANDROID.toString().toLowerCase())) { + return Platform.ANDROID; + } + return Platform.ANY; + } + + private ConfigurationBuilder configure() { + return new ConfigurationBuilder().setUrls(ClasspathHelper.forJavaClassPath()); + } + + /** + * This method should prune resources we are not interested in, but not change the interesting results. + */ + private ConfigurationBuilder filter(final ConfigurationBuilder configuration) { + configuration.setScanners(new SubTypesScanner()); // drops TypeAnnotationScanner + configuration.useParallelExecutor(); + return configuration; + } + + /** + * Store already found classes in a map like: + * MainClass.class: [ + * Platform.ANY: MainClass.class + * Platform.IOS: IOSMainClass.class + * Platform.ANDROID: AndroidMainClass.class + * ] + */ + private static class Caches { + + private static final Platform DEFAULT_PLATFORM = Platform.ANY; + + private static final Map, Map>> IMPLEMENTATIONS_CACHE = new ConcurrentHashMap<>(); + + private static Class getCache(Class pageClass, Platform platform) { + if (platform != Platform.ANDROID && platform != Platform.IOS) { + platform = DEFAULT_PLATFORM; + } + + synchronized (IMPLEMENTATIONS_CACHE) { + if (!IMPLEMENTATIONS_CACHE.containsKey(pageClass)) { + return null; + } + + Map> map = IMPLEMENTATIONS_CACHE.get(pageClass); + Class matchedClass = map.get(platform); + if (matchedClass == null) { + matchedClass = map.get(DEFAULT_PLATFORM); + } + return matchedClass; + } + } + + /** + * Returns the best matching class from map. If no platform specific map exists, the baseclass will return. + */ + private static void setCache(Class pageClass, Platform platform, Class prioritizedClassInfos) { + if (platform != Platform.ANDROID && platform != Platform.IOS) { + platform = DEFAULT_PLATFORM; + } + + synchronized (IMPLEMENTATIONS_CACHE) { + if (!IMPLEMENTATIONS_CACHE.containsKey(pageClass)) { + IMPLEMENTATIONS_CACHE.put(pageClass, new HashMap<>()); + } + Map> map = IMPLEMENTATIONS_CACHE.get(pageClass); + + map.put(platform, prioritizedClassInfos); + } + } + } + + +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumPageFactory.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumPageFactory.java new file mode 100644 index 00000000..706f8b0d --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/mobile/pageobject/AppiumPageFactory.java @@ -0,0 +1,27 @@ +package eu.tsystems.mms.tic.testframework.mobile.pageobject; + +import eu.tsystems.mms.tic.testframework.enums.CheckRule; +import eu.tsystems.mms.tic.testframework.pageobjects.Page; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.DefaultPageFactory; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.PageFactory; +import org.openqa.selenium.WebDriver; + +/** + * Created on 2023-03-13 + * + * @author mgn + */ +public class AppiumPageFactory extends DefaultPageFactory { + + @Override + public T createPageWithCheckRule(Class pageClass, WebDriver webDriver, CheckRule checkRule) { + return super.createPageWithCheckRule(AppiumClassFinder.getInstance().getBestMatchingClass(pageClass, webDriver), webDriver, checkRule); + } + + @Override + public PageFactory clearThreadLocalPagesPrefix() { + AppiumClassFinder.getInstance().clearCache(); + return super.clearThreadLocalPagesPrefix(); + } + +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumExecutionUtils.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumExecutionUtils.java new file mode 100644 index 00000000..91e820a4 --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumExecutionUtils.java @@ -0,0 +1,47 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.utils; + +import java.util.Optional; +import java.util.function.Supplier; + +/** + * Created on 2023-03-10 + * + * @author mgn + */ +public class AppiumExecutionUtils implements ExecutionUtils { + + /** + * The only change against default is the log level in the catch block. + * This is needed to keep clean the logs because of some restrictions of Appium for native apps. + */ + public Optional getFailsafe(Supplier supplier) { + try { + T returnVal = supplier.get(); + return Optional.ofNullable(returnVal); + } catch (Throwable e) { + log().debug("Property is not supported at this platform\n", e); + return Optional.empty(); + } + } +} diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumProperties.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumProperties.java index 64a7ae5a..cdf4cc70 100644 --- a/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumProperties.java +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumProperties.java @@ -12,8 +12,9 @@ public enum AppiumProperties implements IProperties { MOBILE_GRID_URL("tt.mobile.grid.url", ""), MOBILE_GRID_ACCESS_KEY("tt.mobile.grid.access.key", ""), // MOBILE_APPIUM_DEVICE_QUERY("tt.mobile.device.query", ""), - MOBILE_APPIUM_DEVICE_QUERY_IOS("tt.mobile.device.query.ios", "@os='ios' and @category='PHONE'"), - MOBILE_APPIUM_DEVICE_QUERY_ANDROID("tt.mobile.device.query.android", "@os='android' and @category='PHONE'"), +// MOBILE_APPIUM_DEVICE_QUERY_IOS("tt.mobile.device.query.ios", "@os='ios' and @category='PHONE'"), + MOBILE_APPIUM_DEVICE_QUERY_IOS("tt.mobile.device.query.ios", ""), + MOBILE_APPIUM_DEVICE_QUERY_ANDROID("tt.mobile.device.query.android", ""), ; private final String property; diff --git a/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumUtils.java b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumUtils.java new file mode 100644 index 00000000..6673048e --- /dev/null +++ b/appium/src/main/java/eu/tsystems/mms/tic/testframework/utils/AppiumUtils.java @@ -0,0 +1,93 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.utils; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import eu.tsystems.mms.tic.testframework.appium.AppiumContext; +import eu.tsystems.mms.tic.testframework.logging.Loggable; +import eu.tsystems.mms.tic.testframework.mobile.driver.MobileOsChecker; +import eu.tsystems.mms.tic.testframework.testing.WebDriverManagerProvider; +import io.appium.java_client.AppiumDriver; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; + +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; + +/** + * Created on 2023-03-08 + * + * @author mgn + */ +public class AppiumUtils implements WebDriverManagerProvider, Loggable { + + public String runCommand(WebDriver driver, String command, String... args) { + log().info("Shell command (native): {}, {}", command, Arrays.toString(args)); // ImmutableList does NOT work here... + return String.valueOf(JSUtils.executeScript(driver, "mobile: shell", ImmutableMap.of("command", command, "args", Lists.newArrayList(args)))); + } + + public void launchIOSApp(WebDriver driver, String bundleId) { + log().info("Start application '{}'", bundleId); + MobileOsChecker checker = new MobileOsChecker(); + Platform platform = checker.getPlatform(driver); + if (platform == Platform.IOS) { + JSUtils.executeScript(driver, "mobile: launchApp", ImmutableMap.of("bundleId", bundleId)); + JSUtils.executeScript(driver, "mobile: activateApp", ImmutableMap.of("bundleId", bundleId)); + } else { + throw new RuntimeException("Cannot start application " + bundleId + " at " + platform); + } + } + + /** + * Switch the Appium Context if available + */ + public void switchContext(WebDriver driver, AppiumContext desiredContext) { + Optional appiumDriver = WEB_DRIVER_MANAGER.unwrapWebDriver(driver, AppiumDriver.class); + if (appiumDriver.isEmpty()) { + throw new RuntimeException("Current Webdriver is not an Appium driver."); + } + String currentContext = appiumDriver.get().getContext(); + AppiumContext parsedInitialContext = AppiumContext.parse(currentContext); + log().info("Current context: {} ({})", currentContext, parsedInitialContext); + + if (parsedInitialContext.equals(desiredContext)) { + log().info("Already in context {}", desiredContext); + return; + } + + Set contextHandles = appiumDriver.get().getContextHandles(); + log().info("Available contexts: {}", contextHandles); + contextHandles.stream() + .filter(contextHandle -> AppiumContext.parse(contextHandle).equals(desiredContext)) + .findFirst() + .ifPresentOrElse(handle -> { + log().info("Switch to context {} ({})", handle, desiredContext); + appiumDriver.get().context(handle); + }, () -> { + log().error("Couldn't find a {} context in {}", desiredContext, contextHandles); + throw new RuntimeException(String.format("Cannot switch in %s, because it does not exist. (%s)", desiredContext, contextHandles)); + }); + + } + +} 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 index 07c5e750..4c5be9a8 100644 --- 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 @@ -21,7 +21,10 @@ package eu.tsystems.mms.tic.testframework.webdrivermanager; +import eu.tsystems.mms.tic.testframework.appium.Browsers; import eu.tsystems.mms.tic.testframework.utils.AppiumProperties; +import io.appium.java_client.remote.MobileCapabilityType; +import org.apache.commons.lang3.StringUtils; import java.net.MalformedURLException; import java.net.URL; @@ -35,6 +38,7 @@ public class AppiumDriverRequest extends SeleniumWebDriverRequest { public AppiumDriverRequest() { setAccessKey(AppiumProperties.MOBILE_GRID_ACCESS_KEY.asString()); + this.setBrowser(Browsers.mobile); } @Override @@ -50,10 +54,44 @@ public Optional getServerUrl() { } public void setDeviceQuery(String deviceQuery) { - this.getDesiredCapabilities().setCapability(DEVICE_QUERY, deviceQuery); + if (StringUtils.isNotBlank(deviceQuery)) { + this.getDesiredCapabilities().setCapability(DEVICE_QUERY, deviceQuery); + } } public void setAccessKey(String accessKey) { this.getDesiredCapabilities().setCapability(ACCESS_KEY, accessKey); } + + public void setAppiumEngine(String engine) { + this.getDesiredCapabilities().setCapability(MobileCapabilityType.AUTOMATION_NAME, engine); + } + + public String getAppiumEngine() { + return this.getDesiredCapabilities().getCapability(MobileCapabilityType.AUTOMATION_NAME).toString(); + } + + public void setDeviceName(String deviceName) { + this.getDesiredCapabilities().setCapability(MobileCapabilityType.DEVICE_NAME, deviceName); + } + + public String getDeviceName() { + return this.getDesiredCapabilities().getCapability(MobileCapabilityType.DEVICE_NAME).toString(); + } + + public void setPlatformVersion(String platformVersion) { + this.getDesiredCapabilities().setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion); + } + + public String getPlatformVersion() { + return this.getDesiredCapabilities().getCapability(MobileCapabilityType.PLATFORM_VERSION).toString(); + } + + public void setDeviceId(String id) { + this.getDesiredCapabilities().setCapability(MobileCapabilityType.UDID, id); + } + + public String getDeviceId() { + return this.getDesiredCapabilities().getCapability(MobileCapabilityType.UDID).toString(); + } } diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/ioc/AppiumConnectorTestModule.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/ioc/AppiumConnectorTestModule.java new file mode 100644 index 00000000..12b0700d --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/ioc/AppiumConnectorTestModule.java @@ -0,0 +1,19 @@ +package eu.tsystems.mms.tic.testframework.mobile.systemundertest.ioc; + +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; +import eu.tsystems.mms.tic.testframework.mobile.pageobject.AppiumPageFactory; +import eu.tsystems.mms.tic.testframework.pageobjects.internal.PageFactory; + +/** + * Created on 2023-03-13 + * + * @author mgn + */ +public class AppiumConnectorTestModule extends AbstractModule { + + protected void configure() { + bind(PageFactory.class).to(AppiumPageFactory.class).in(Scopes.SINGLETON); + } + +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/StartPage.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/StartPage.java index ec3c7928..9a993f0f 100644 --- a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/StartPage.java +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/StartPage.java @@ -23,9 +23,7 @@ package eu.tsystems.mms.tic.testframework.mobile.systemundertest.page; import eu.tsystems.mms.tic.testframework.pageobjects.Check; -import eu.tsystems.mms.tic.testframework.pageobjects.GuiElement; -import eu.tsystems.mms.tic.testframework.pageobjects.Locate; -import eu.tsystems.mms.tic.testframework.pageobjects.factory.PageFactory; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; @@ -40,10 +38,10 @@ public class StartPage extends AbstractInternetPage { @Check - private GuiElement linkLogin = new GuiElement(this.getWebDriver(), Locate.by(By.linkText("Form Authentication"))); + private UiElement linkLogin = find(By.linkText("Form Authentication")); @Check - private GuiElement linkTables = new GuiElement(this.getWebDriver(), Locate.by(By.linkText("Sortable Data Tables"))); + private UiElement linkTables = find(By.linkText("Sortable Data Tables")); /** * Constructor for existing sessions. @@ -51,21 +49,18 @@ public class StartPage extends AbstractInternetPage { * @param driver . */ public StartPage(WebDriver driver) { - super(driver); } public LoginPage goToLoginPage() { - this.linkLogin.scrollIntoView(); this.linkLogin.click(); - return PageFactory.create(LoginPage.class, this.getWebDriver()); + return createPage(LoginPage.class); } public TablePage goToTablePage() { - this.linkTables.scrollIntoView(); this.linkTables.click(); - return PageFactory.create(TablePage.class, this.getWebDriver()); + return createPage(TablePage.class); } } diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/IosWifiSettingsPage.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/IosWifiSettingsPage.java new file mode 100644 index 00000000..1e9810cd --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/IosWifiSettingsPage.java @@ -0,0 +1,15 @@ +package eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.nativeos; + +import org.openqa.selenium.WebDriver; + +/** + * Created on 2023-03-13 + * + * @author mgn + */ +public class IosWifiSettingsPage extends WifiSettingsPage{ + public IosWifiSettingsPage(WebDriver webDriver) { + super(webDriver); + log().info("You are using the specific IOS WifiSettings page."); + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/SettingsPage.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/SettingsPage.java new file mode 100644 index 00000000..1b7d1a1e --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/SettingsPage.java @@ -0,0 +1,52 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.systemundertest.page.nativeos; + +import eu.tsystems.mms.tic.testframework.mobile.AppiumPage; +import eu.tsystems.mms.tic.testframework.pageobjects.Check; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; +import io.appium.java_client.pagefactory.iOSXCUITFindBy; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; + +/** + * Created on 2023-03-08 + * + * @author mgn + */ +public class SettingsPage extends AppiumPage { + + @Check + private UiElement title = find(By.xpath("//XCUIElementTypeStaticText[@value='Einstellungen']")); + + @Check + @iOSXCUITFindBy(xpath = "//*[@type='XCUIElementTypeStaticText' and @value='WLAN']") + private UiElement buttonWLAN; + + public SettingsPage(WebDriver webDriver) { + super(webDriver); + } + + public WifiSettingsPage gotoWifiSettings() { + this.buttonWLAN.click(); + return createPage(WifiSettingsPage.class); + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/WifiSettingsPage.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/WifiSettingsPage.java new file mode 100644 index 00000000..62c0fe14 --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/systemundertest/page/nativeos/WifiSettingsPage.java @@ -0,0 +1,64 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.systemundertest.page.nativeos; + +import eu.tsystems.mms.tic.testframework.mobile.AppiumPage; +import eu.tsystems.mms.tic.testframework.pageobjects.Check; +import eu.tsystems.mms.tic.testframework.pageobjects.TestableUiElement; +import eu.tsystems.mms.tic.testframework.pageobjects.UiElement; +import io.appium.java_client.pagefactory.AndroidFindBy; +import io.appium.java_client.pagefactory.iOSXCUITFindBy; +import org.openqa.selenium.WebDriver; + +/** + * Created on 2023-03-08 + * + * @author mgn + */ +public class WifiSettingsPage extends AppiumPage { + + @Check + @AndroidFindBy(xpath = "//*[@text='WLAN' or @text='Wi-Fi']") // this is language specific (english + german) + @iOSXCUITFindBy(xpath = "//*[@type='XCUIElementTypeNavigationBar']//*[@label='WLAN']") // native Appium identifier + protected UiElement headline; + + @Check + @AndroidFindBy(xpath = "//*[@class='android.widget.Switch' or @id='switch_widget' or @id='checkbox' or @id='switchWidget']") + // this is language specific (english + german) + @iOSXCUITFindBy(xpath = "//*[@label='WLAN' and @type='XCUIElementTypeSwitch']") + protected UiElement wlanToggle; + + public WifiSettingsPage(WebDriver webDriver) { + super(webDriver); + } + + public void deactivateWifi() { + this.wlanToggle.select(false); + } + + public void activateWifi() { + this.wlanToggle.select(true); + } + + public TestableUiElement getWlanToggle() { + return this.wlanToggle; + } +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/TesterraMobileAppTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/TesterraMobileAppTest.java new file mode 100644 index 00000000..82cdf42b --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/TesterraMobileAppTest.java @@ -0,0 +1,132 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.apps; + +import eu.tsystems.mms.tic.testframework.mobile.driver.MobileOsChecker; +import eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.nativeos.SettingsPage; +import eu.tsystems.mms.tic.testframework.mobile.systemundertest.page.nativeos.WifiSettingsPage; +import eu.tsystems.mms.tic.testframework.mobile.test.AbstractAppiumTest; +import eu.tsystems.mms.tic.testframework.report.model.steps.TestStep; +import eu.tsystems.mms.tic.testframework.utils.AppiumUtils; +import eu.tsystems.mms.tic.testframework.utils.TimerUtils; +import eu.tsystems.mms.tic.testframework.utils.UITestUtils; +import eu.tsystems.mms.tic.testframework.webdrivermanager.AppiumDriverRequest; +import io.appium.java_client.remote.AndroidMobileCapabilityType; +import io.appium.java_client.remote.MobileCapabilityType; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebDriver; +import org.testng.annotations.Test; + +import java.net.MalformedURLException; + +/** + * Created on 2023-02-07 + * + * @author mgn + */ +public class TesterraMobileAppTest extends AbstractAppiumTest { + + @Test + public void testT01AndroidApp() { + AppiumDriverRequest request = new AppiumDriverRequest(); + request.setDeviceQuery("contains(@name, 'Galaxy S20')"); + request.getDesiredCapabilities().setCapability("appiumVersion", "1.22.3"); + + request.getDesiredCapabilities().setCapability(MobileCapabilityType.APP, "cloud:eu.tsystems.mms.tic.mdc.app.android/.HomeActivity"); + request.getDesiredCapabilities().setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "eu.tsystems.mms.tic.mdc.app.android"); + request.getDesiredCapabilities().setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".HomeActivity"); + request.setAppiumEngine("UiAutomator2"); + + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(request); + TimerUtils.sleep(4000); + } + + @Test + public void testT10NativeIOSSettings() { + AppiumDriverRequest request = new AppiumDriverRequest(); + request.setDeviceQuery("contains(@name, 'iPhone X')"); + request.getDesiredCapabilities().setCapability("appiumVersion", "1.22.3"); + request.setAppiumEngine("XCUITest"); + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(request); + + WifiSettingsPage wifiSettingsPage = openWifiSettings(webDriver); + switchWiFi(wifiSettingsPage); + TimerUtils.sleep(5000); + + UITestUtils.takeScreenshots(); + } + + @Test + public void testT12NativeAndWebAccessIOS() { + AppiumDriverRequest request = new AppiumDriverRequest(); + request.setDeviceQuery("contains(@name, 'iPhone X')"); + request.getDesiredCapabilities().setCapability("appiumVersion", "1.22.3"); + request.setAppiumEngine("XCUITest"); + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(request); + + + + WifiSettingsPage wifiSettingsPage = openWifiSettings(webDriver); + switchWiFi(wifiSettingsPage); + + + UITestUtils.takeScreenshots(); + } + + @Test + public void testT11NativeAndroidSettings() { + AppiumDriverRequest request = new AppiumDriverRequest(); + request.setDeviceQuery("contains(@name, 'Samsung Galaxy S20')"); + request.getDesiredCapabilities().setCapability("appiumVersion", "1.22.3"); + request.setAppiumEngine("UiAutomator2"); + WebDriver webDriver = WEB_DRIVER_MANAGER.getWebDriver(request); + + WifiSettingsPage wifiSettingsPage = openWifiSettings(webDriver); + switchWiFi(wifiSettingsPage); + } + + private WifiSettingsPage openWifiSettings(WebDriver driver) { + TestStep.begin("Open Wifi settings"); + MobileOsChecker checker = new MobileOsChecker(); + Platform platform = checker.getPlatform(driver); + switch (platform) { + case IOS: + new AppiumUtils().launchIOSApp(driver, "com.apple.Preferences"); + SettingsPage settingsPage = PAGE_FACTORY.createPage(SettingsPage.class, driver); + return settingsPage.gotoWifiSettings(); + case ANDROID: + new AppiumUtils().runCommand(driver, "am", "start", "-a", "android.settings.WIFI_SETTINGS"); + return PAGE_FACTORY.createPage(WifiSettingsPage.class, driver); + } + throw new RuntimeException("Invalid platform"); + } + + private void switchWiFi(WifiSettingsPage wifiSettingsPage) { + TestStep.begin("Deactivate wifi"); + wifiSettingsPage.deactivateWifi(); + wifiSettingsPage.getWlanToggle().assertThat().selected(false); + + TestStep.begin("Activate wifi"); + wifiSettingsPage.activateWifi(); + wifiSettingsPage.getWlanToggle().assertThat().selected(true); + } + +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/VanillaAppiumAppTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/VanillaAppiumAppTest.java new file mode 100644 index 00000000..39b5be04 --- /dev/null +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/apps/VanillaAppiumAppTest.java @@ -0,0 +1,79 @@ +/* + * Testerra + * + * (C) 2023, Martin Großmann, 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.apps; + +import eu.tsystems.mms.tic.testframework.mobile.test.AbstractAppiumTest; +import eu.tsystems.mms.tic.testframework.utils.AppiumProperties; +import eu.tsystems.mms.tic.testframework.utils.TimerUtils; +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.remote.AndroidMobileCapabilityType; +import io.appium.java_client.remote.MobileCapabilityType; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Created on 2023-02-07 + * + * @author mgn + */ +public class VanillaAppiumAppTest extends AbstractAppiumTest { + + protected AndroidDriver driver = null; + + @BeforeMethod + public void setUp() throws MalformedURLException { + final String accessKey = AppiumProperties.MOBILE_GRID_ACCESS_KEY.asString(); + Assert.assertNotNull(accessKey, "No access key loaded"); + + DesiredCapabilities dc = new DesiredCapabilities(); + dc.setCapability("testName", "Demo Tests"); + dc.setCapability("accessKey", accessKey); + dc.setCapability("deviceQuery", "contains(@name, 'Galaxy S20') and @version='13.0'"); + dc.setCapability("appiumVersion", "1.22.3"); + dc.setCapability(MobileCapabilityType.APP, "cloud:eu.tsystems.mms.tic.mdc.app.android/.HomeActivity"); + dc.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "eu.tsystems.mms.tic.mdc.app.android"); + dc.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".HomeActivity"); + dc.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UIAutomator2"); + URL url = new URL(AppiumProperties.MOBILE_GRID_URL.asString()); + // driver = new IOSDriver<>(new URL(PropertyManager.getProperty("tt.mobile.grid.url")), dc); + driver = new AndroidDriver<>(url, dc); + } + + @Test + public void testT01CheckApp() { + TimerUtils.sleep(5000); + } + + @AfterMethod + public void tearDown() { + + log().info("Report URL: " + driver.getCapabilities().getCapability("reportUrl")); + driver.quit(); + } + +} diff --git a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/VanillaAppiumDriverTest.java b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/VanillaAppiumDriverTest.java index 6be21efd..27b54b1d 100644 --- a/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/VanillaAppiumDriverTest.java +++ b/appium/src/test/java/eu/tsystems/mms/tic/testframework/mobile/test/driver/VanillaAppiumDriverTest.java @@ -28,8 +28,8 @@ import eu.tsystems.mms.tic.testframework.report.Report; import eu.tsystems.mms.tic.testframework.report.TesterraListener; import eu.tsystems.mms.tic.testframework.utils.AppiumProperties; -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.openqa.selenium.By; import org.openqa.selenium.OutputType; @@ -56,8 +56,8 @@ */ public class VanillaAppiumDriverTest extends AbstractAppiumTest implements Loggable, PropertyManagerProvider { - // protected IOSDriver driver = null; - protected AndroidDriver driver = null; + protected IOSDriver driver = null; +// protected AndroidDriver driver = null; @BeforeMethod public void setUp() throws MalformedURLException { @@ -67,18 +67,24 @@ public void setUp() throws MalformedURLException { DesiredCapabilities dc = new DesiredCapabilities(); dc.setCapability("testName", "Demo Tests"); dc.setCapability("accessKey", accessKey); - // dc.setCapability("deviceQuery", "@os='ios' and @category='PHONE'"); - dc.setCapability("deviceQuery", AppiumProperties.MOBILE_APPIUM_DEVICE_QUERY_ANDROID); - // dc.setBrowserName(MobileBrowserType.SAFARI); - dc.setBrowserName(MobileBrowserType.CHROMIUM); + dc.setCapability("appiumVersion", "1.22.3"); +// dc.setCapability("deviceQuery", "contains(@name, 'Samsung Galaxy S20')"); +// dc.setCapability("deviceQuery", "contains(@name, 'Google Pixel 6')"); +// dc.setCapability("deviceQuery", AppiumProperties.MOBILE_APPIUM_DEVICE_QUERY_ANDROID); + dc.setCapability("deviceQuery", "contains(@name, 'Apple iPhone X (')"); +// dc.setCapability(MobileCapabilityType.UDID, "..."); + dc.setBrowserName(MobileBrowserType.SAFARI); +// dc.setBrowserName(MobileBrowserType.CHROME); URL url = new URL(AppiumProperties.MOBILE_GRID_URL.asString()); - // driver = new IOSDriver<>(new URL(PropertyManager.getProperty("tt.mobile.grid.url")), dc); - driver = new AndroidDriver<>(url, dc); + log().info(dc.toString()); + + driver = new IOSDriver<>(url, dc); +// driver = new AndroidDriver<>(url, dc); + } @Test public void testT01_DoGoogleSearch() { - driver.rotate(ScreenOrientation.PORTRAIT); driver.get("https://www.google.com"); new WebDriverWait(driver, 10).until(driver1 -> { @@ -94,7 +100,6 @@ public void testT01_DoGoogleSearch() { @AfterMethod public void tearDown() { - log().info("Report URL: " + driver.getCapabilities().getCapability("reportUrl")); driver.quit(); } diff --git a/appium/src/test/resources/test.properties b/appium/src/test/resources/test.properties index 77f0b741..c95bb1a6 100644 --- a/appium/src/test/resources/test.properties +++ b/appium/src/test/resources/test.properties @@ -1,6 +1,6 @@ #tt.browser=mobile_chrome -tt.browser=mobile_safari +#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' diff --git a/settings.gradle b/settings.gradle index f38489f2..907772ff 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,5 @@ rootProject.name = 'testerra-appium-connector' include 'appium' include 'appium-seetest' -//includeBuild("../testerra") +//includeBuild("../github_testerra")