diff --git a/README.md b/README.md index 93e9a645f..48a470d16 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.appium/java-client/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.appium/java-client) [![Javadoc](https://javadoc-emblem.rhcloud.com/doc/io.appium/java-client/badge.svg)](http://www.javadoc.io/doc/io.appium/java-client) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f365c5e9458b42bf8a5b1d928d7e4f48)](https://www.codacy.com/app/appium/java-client) This is the Java language binding for writing Appium Tests, conforms to [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) @@ -82,7 +83,14 @@ You can get it on [WIKI](https://github.com/appium/java-client/wiki) - `getSessionDetails()` was added. Thanks to [@saikrishna321](https://github.com/saikrishna321) for the contribution. - FIX [#362](https://github.com/appium/java-client/issues/362), [#220](https://github.com/appium/java-client/issues/220), [#323](https://github.com/appium/java-client/issues/323). Details read there: [#413](https://github.com/appium/java-client/pull/413) - FIX [#392](https://github.com/appium/java-client/issues/392). Thanks to [@truebit](https://github.com/truebit) for the bug report. -- The dependency on `cglib` was replaced by the dependency on `cglib-nodep`. FIX [#418](https://github.com/appium/java-client/issues/418) +- The dependency on `cglib` was replaced by the dependency on `cglib-nodep`. FIX [#418](https://github.com/appium/java-client/issues/418) +- The casting to the weaker interface `HasIdentity` instead of class `RemoteWebElement` was added. It is the internal refactoring of the `TouchAction`. [#432](https://github.com/appium/java-client/pull/432). Thanks to [@asolntsev](https://github.com/asolntsev) for the contribution. +- The `setValue` method was moved to `MobileElement`. It works against text input elements on Android. +- The dependency on `org.springframework` `spring-context` v`4.3.1.RELEASE` was added +- The dependency on `org.aspectj` `aspectjweaver` v`1.8.9` was added +- ENHANCEMENT: The alternative event firing engine. The feature request: [#242](https://github.com/appium/java-client/issues/242). +Implementation: [#437](https://github.com/appium/java-client/pull/437). Also [new WIKI chapter](https://github.com/appium/java-client/blob/master/docs/The-event_firing.md) was added. +- ENHANCEMENT: Convenient access to specific commands for each supported mobile OS. Details: [#445](https://github.com/appium/java-client/pull/445) *4.0.0* - all code marked `@Deprecated` was removed. Java client won't support old servers (v<1.5.0) diff --git a/docs/The-event_firing.md b/docs/The-event_firing.md new file mode 100644 index 000000000..38071a9a7 --- /dev/null +++ b/docs/The-event_firing.md @@ -0,0 +1,125 @@ +since 4.1.0 + +# The purpose + +This feature allows end user to organize the event logging on the client side. Also this feature may be useful in a binding with standard or custom reporting +frameworks. + + +# The API + +The API was designed the way which allows end user to select events (searching, navigation, exception throwing etc.) which should be listened to. It contains +the following list of interfaces (new items may be added further): + +- `io.appium.java_client.events.api.Listener` is the basic interface +- `io.appium.java_client.events.api.general.AlertEventListener` is for the listening to alerts +- `io.appium.java_client.events.api.general.ElementEventListener` is for the listening to actions related to elements +- `io.appium.java_client.events.api.general.JavaScriptEventListener` is for the listening to java script executing +- `io.appium.java_client.events.api.general.ListensToException` is for the listening to exceptions which are thrown +- `io.appium.java_client.events.api.general.NavigationEventListener` is for the listening to events related to navigation +- `io.appium.java_client.events.api.general.SearchingEventListener` is for the listening to events related to the searching. +- `io.appium.java_client.events.api.general.WindowEventListener` is for the listening to actions on a window +- `io.appium.java_client.events.api.mobile.ContextEventListener` is for the listening to the switching to mobile context +- `io.appium.java_client.events.api.mobile.RotationEventListener` is for the listening to screen rotation +- `io.appium.java_client.events.api.general.AppiumWebDriverEventListener` was added to provide the compatibility with +user's implementation of `org.openqa.selenium.support.events.WebDriverEventListener`. Also it extends some interfaces above. + +# Briefly about the engine. + +This is pretty similar solution as the `org.openqa.selenium.support.events.EventFiringWebDriver` of the Selenium project. You +can read about this thing there [The blog post](http://seleniumworks.blogspot.ru/2014/02/eventfiringwebdriver.html). + +Here we were trying to improve existing drawbacks and restrictions using: + +- API splitting, see above. + +- the binding of some [Spring framework engines](https://projects.spring.io/spring-framework/) with [AspectJ](https://en.wikipedia.org/wiki/AspectJ). + +# How to use + +It is easy. + +```java +import io.appium.java_client.events.api.general.AlertEventListener; + +public class AlertListener implements AlertEventListener { +... +} + +... +import io.appium.java_client.events.api.general.ElementEventListener; + +public class ElementListener implements ElementEventListener { +... +} + +//and so on +... +import io.appium.java_client.events.EventFiringWebDriverFactory; +import io.appium.java_client.events.api.Listener; +... + +AndroidDriver driver = new AndroidDriver(parameters); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver, new AlertListener(), + new ElementListener()); + +//or +AndroidDriver driver2 = new AndroidDriver(parameters); +List listeners = new ArrayList<>(); +listeners.add(new AlertListener()); +listeners.add(new ElementListener()); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver2, listeners); +``` + +## What if there are listeners which used everywhere by default. + +In order to avoid the repeating actions an end user is free to do these things: + +- create folders `/META-INF/services` and put the file `io.appium.java_client.events.api.Listener` there. Please read about +[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html). + +![image](https://cloud.githubusercontent.com/assets/4927589/16731325/24eab680-4780-11e6-8551-a3c72d4b9c38.png) + +- define the list of default listeners at the `io.appium.java_client.events.api.Listener` + +![image](https://cloud.githubusercontent.com/assets/4927589/16731509/2734a4e0-4781-11e6-81cb-ab64a5924c35.png) + +And then it is enough + +```java + +//and so on +... +import io.appium.java_client.events.EventFiringWebDriverFactory; +... + +AndroidDriver driver = new AndroidDriver(parameters); +driver = EventFiringWebDriverFactory.getEventFiringWebDriver(driver); +``` + +If there are listeners defined externally when this collection is merged with default set of listeners. + +# How to reuse customized WebDriverEventListener + +If an end user has their own `org.openqa.selenium.support.events.WebDriverEventListener` implementation then in order to +make it compatible with this engine it is enough to do the following. + + +```java +import org.openqa.selenium.support.events.WebDriverEventListener; +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; + +public class UsersWebDriverEventListener implements WebDriverEventListener, AppiumWebDriverEventListener { +... +} +``` + +or just + +```java +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; + +public class UsersWebDriverEventListener implements AppiumWebDriverEventListener { +... +} +``` diff --git a/pom.xml b/pom.xml index 8ecbafa05..99710d0b7 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ org.seleniumhq.selenium selenium-java - 2.53.0 + 2.53.1 cglib @@ -69,6 +69,18 @@ cglib-nodep 3.2.3 + + org.springframework + spring-context + 4.3.1.RELEASE + compile + + + org.aspectj + aspectjweaver + 1.8.9 + compile + jar java-client diff --git a/src/main/java/io/appium/java_client/AppiumDriver.java b/src/main/java/io/appium/java_client/AppiumDriver.java index 01eaaed4d..b41879151 100644 --- a/src/main/java/io/appium/java_client/AppiumDriver.java +++ b/src/main/java/io/appium/java_client/AppiumDriver.java @@ -35,6 +35,7 @@ import static io.appium.java_client.MobileCommand.REMOVE_APP; import static io.appium.java_client.MobileCommand.RUN_APP_IN_BACKGROUND; import static io.appium.java_client.MobileCommand.SET_SETTINGS; +import static io.appium.java_client.MobileCommand.prepareArguments; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -46,7 +47,6 @@ import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; -import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.By; import org.openqa.selenium.Capabilities; import org.openqa.selenium.Dimension; @@ -57,7 +57,6 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.html5.Location; -import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.DriverCommand; import org.openqa.selenium.remote.ErrorHandler; @@ -67,7 +66,6 @@ import org.openqa.selenium.remote.Response; import org.openqa.selenium.remote.html5.RemoteLocationContext; import org.openqa.selenium.remote.http.HttpClient; -import org.openqa.selenium.remote.http.HttpMethod; import org.openqa.selenium.remote.internal.JsonToWebElementConverter; import java.lang.reflect.Constructor; @@ -189,39 +187,6 @@ protected static Capabilities substituteMobilePlatform(Capabilities originalCapa return dc; } - /** - * @param param is a parameter name. - * @param value is the parameter value. - * @return built {@link ImmutableMap}. - */ - protected static ImmutableMap getCommandImmutableMap(String param, - Object value) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put(param, value); - return builder.build(); - } - - /** - * @param params is the array with parameter names. - * @param values is the array with parameter values. - * @return built {@link ImmutableMap}. - */ - protected static ImmutableMap getCommandImmutableMap(String[] params, - Object[] values) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (int i = 0; i < params.length; i++) { - if (!StringUtils.isBlank(params[i]) && (values[i] != null)) { - builder.put(params[i], values[i]); - } - } - return builder.build(); - } - - @SuppressWarnings("unused") - private static CommandInfo deleteC(String url) { - return new CommandInfo(url, HttpMethod.DELETE); - } - @Override public List findElements(By by) { return super.findElements(by); } @@ -572,7 +537,7 @@ public JsonObject getSettings() { * @param settings Map of setting keys and values. */ private void setSettings(ImmutableMap settings) { - execute(SET_SETTINGS, getCommandImmutableMap("settings", settings)); + execute(SET_SETTINGS, prepareArguments("settings", settings)); } /** @@ -584,7 +549,7 @@ private void setSettings(ImmutableMap settings) { * @param value value of the setting. */ protected void setSetting(AppiumSetting setting, Object value) { - setSettings(getCommandImmutableMap(setting.toString(), value)); + setSettings(prepareArguments(setting.toString(), value)); } @Override public WebDriver context(String name) { @@ -654,7 +619,7 @@ protected void setSetting(AppiumSetting setting, Object value) { * @see HasAppStrings#getAppStringMap(String). */ @Override public Map getAppStringMap(String language) { - Response response = execute(GET_STRINGS, getCommandImmutableMap("language", language)); + Response response = execute(GET_STRINGS, prepareArguments("language", language)); return (Map) response.getValue(); } @@ -667,7 +632,7 @@ protected void setSetting(AppiumSetting setting, Object value) { @Override public Map getAppStringMap(String language, String stringFile) { String[] parameters = new String[] {"language", "stringFile"}; Object[] values = new Object[] {language, stringFile}; - Response response = execute(GET_STRINGS, getCommandImmutableMap(parameters, values)); + Response response = execute(GET_STRINGS, prepareArguments(parameters, values)); return (Map) response.getValue(); } @@ -689,8 +654,8 @@ public URL getRemoteAddress() { * @return a map with values that hold session details. * */ - public Map getSessionDetails() { + public Map getSessionDetails() { Response response = execute(GET_SESSION); - return (Map) response.getValue(); + return (Map) response.getValue(); } } diff --git a/src/main/java/io/appium/java_client/CommandExecutionHelper.java b/src/main/java/io/appium/java_client/CommandExecutionHelper.java new file mode 100644 index 000000000..393f0e388 --- /dev/null +++ b/src/main/java/io/appium/java_client/CommandExecutionHelper.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client; + +import org.openqa.selenium.remote.Response; + +import java.util.Map; + +public final class CommandExecutionHelper { + + public static T execute(MobileElement element, + Map.Entry> keyValuePair) { + return handleResponse(element.execute(keyValuePair.getKey(), keyValuePair.getValue())); + } + + public static T execute(MobileDriver driver, + Map.Entry> keyValuePair) { + return handleResponse(driver.execute(keyValuePair.getKey(), keyValuePair.getValue())); + } + + private static T handleResponse(Response responce) { + if (responce != null) { + return (T) responce.getValue(); + } + return null; + } +} diff --git a/src/main/java/io/appium/java_client/MobileCommand.java b/src/main/java/io/appium/java_client/MobileCommand.java index dcd7f2000..8249902a9 100644 --- a/src/main/java/io/appium/java_client/MobileCommand.java +++ b/src/main/java/io/appium/java_client/MobileCommand.java @@ -18,9 +18,11 @@ import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.http.HttpMethod; +import java.util.HashMap; import java.util.Map; /** @@ -28,93 +30,150 @@ * wire protocol. */ public class MobileCommand { + //General + protected static final String RESET = "reset"; + protected static final String GET_STRINGS = "getStrings"; + protected static final String SET_VALUE = "setValue"; + protected static final String PULL_FILE = "pullFile"; + protected static final String PULL_FOLDER = "pullFolder"; + protected static final String HIDE_KEYBOARD = "hideKeyboard"; + protected static final String RUN_APP_IN_BACKGROUND = "runAppInBackground"; + protected static final String PERFORM_TOUCH_ACTION = "performTouchAction"; + protected static final String PERFORM_MULTI_TOUCH = "performMultiTouch"; + protected static final String IS_APP_INSTALLED = "isAppInstalled"; + protected static final String INSTALL_APP = "installApp"; + protected static final String REMOVE_APP = "removeApp"; + protected static final String LAUNCH_APP = "launchApp"; + protected static final String CLOSE_APP = "closeApp"; + protected static final String LOCK = "lock"; + protected static final String COMPLEX_FIND = "complexFind"; + protected static final String GET_SETTINGS = "getSettings"; + protected static final String SET_SETTINGS = "setSettings"; + protected static final String GET_DEVICE_TIME = "getDeviceTime"; + protected static final String GET_SESSION = "getSession"; + //iOS + protected static final String SHAKE = "shake"; + //Android + protected static final String CURRENT_ACTIVITY = "currentActivity"; + protected static final String END_TEST_COVERAGE = "endTestCoverage"; + protected static final String GET_NETWORK_CONNECTION = "getNetworkConnection"; + protected static final String IS_LOCKED = "isLocked"; + protected static final String LONG_PRESS_KEY_CODE = "longPressKeyCode"; + protected static final String OPEN_NOTIFICATIONS = "openNotifications"; + protected static final String PRESS_KEY_CODE = "pressKeyCode"; + protected static final String PUSH_FILE = "pushFile"; + protected static final String SET_NETWORK_CONNECTION = "setNetworkConnection"; + protected static final String START_ACTIVITY = "startActivity"; + protected static final String TOGGLE_LOCATION_SERVICES = "toggleLocationServices"; + protected static final String UNLOCK = "unlock"; + protected static final String REPLACE_VALUE = "replaceValue"; - public static final String RESET = "reset"; - public static final String GET_STRINGS = "getStrings"; - public static final String PRESS_KEY_CODE = "pressKeyCode"; - public static final String LONG_PRESS_KEY_CODE = "longPressKeyCode"; - public static final String CURRENT_ACTIVITY = "currentActivity"; - public static final String SET_VALUE = "setValue"; - public static final String REPLACE_VALUE = "replaceValue"; - public static final String PULL_FILE = "pullFile"; - public static final String PUSH_FILE = "pushFile"; - public static final String PULL_FOLDER = "pullFolder"; - public static final String HIDE_KEYBOARD = "hideKeyboard"; - public static final String RUN_APP_IN_BACKGROUND = "runAppInBackground"; - public static final String PERFORM_TOUCH_ACTION = "performTouchAction"; - public static final String PERFORM_MULTI_TOUCH = "performMultiTouch"; - public static final String IS_APP_INSTALLED = "isAppInstalled"; - public static final String INSTALL_APP = "installApp"; - public static final String REMOVE_APP = "removeApp"; - public static final String LAUNCH_APP = "launchApp"; - public static final String CLOSE_APP = "closeApp"; - public static final String END_TEST_COVERAGE = "endTestCoverage"; - public static final String LOCK = "lock"; - public static final String IS_LOCKED = "isLocked"; - public static final String SHAKE = "shake"; - public static final String COMPLEX_FIND = "complexFind"; - public static final String OPEN_NOTIFICATIONS = "openNotifications"; - public static final String GET_NETWORK_CONNECTION = "getNetworkConnection"; - public static final String SET_NETWORK_CONNECTION = "setNetworkConnection"; - public static final String GET_SETTINGS = "getSettings"; - public static final String SET_SETTINGS = "setSettings"; - public static final String START_ACTIVITY = "startActivity"; - public static final String TOGGLE_LOCATION_SERVICES = "toggleLocationServices"; - public static final String GET_DEVICE_TIME = "getDeviceTime"; - public static final String UNLOCK = "unlock"; - public static final String GET_SESSION = "getSession"; - public static final Map commandRepository = getMobileCommands(); + public static final Map commandRepository = createCommandRepository(); + private static Map createCommandRepository() { + HashMap result = new HashMap(); + result.put(RESET, postC("/session/:sessionId/appium/app/reset")); + result.put(GET_STRINGS, postC("/session/:sessionId/appium/app/strings")); + result.put(SET_VALUE, postC("/session/:sessionId/appium/element/:id/value")); + result.put(PULL_FILE, postC("/session/:sessionId/appium/device/pull_file")); + result.put(PULL_FOLDER, postC("/session/:sessionId/appium/device/pull_folder")); + result.put(HIDE_KEYBOARD, postC("/session/:sessionId/appium/device/hide_keyboard")); + result.put(RUN_APP_IN_BACKGROUND, postC("/session/:sessionId/appium/app/background")); + result.put(PERFORM_TOUCH_ACTION, postC("/session/:sessionId/touch/perform")); + result.put(PERFORM_MULTI_TOUCH, postC("/session/:sessionId/touch/multi/perform")); + result.put(IS_APP_INSTALLED, postC("/session/:sessionId/appium/device/app_installed")); + result.put(INSTALL_APP, postC("/session/:sessionId/appium/device/install_app")); + result.put(REMOVE_APP, postC("/session/:sessionId/appium/device/remove_app")); + result.put(LAUNCH_APP, postC("/session/:sessionId/appium/app/launch")); + result.put(CLOSE_APP, postC("/session/:sessionId/appium/app/close")); + result.put(LOCK, postC("/session/:sessionId/appium/device/lock")); + result.put(COMPLEX_FIND, postC("/session/:sessionId/appium/app/complex_find")); + result.put(GET_SETTINGS, getC("/session/:sessionId/appium/settings")); + result.put(SET_SETTINGS, postC("/session/:sessionId/appium/settings")); + result.put(GET_DEVICE_TIME, getC("/session/:sessionId/appium/device/system_time")); + result.put(GET_SESSION,getC("/session/:sessionId/")); + //iOS + result.put(SHAKE, postC("/session/:sessionId/appium/device/shake")); + //Android + result.put(CURRENT_ACTIVITY, + getC("/session/:sessionId/appium/device/current_activity")); + result.put(END_TEST_COVERAGE, + postC("/session/:sessionId/appium/app/end_test_coverage")); + result.put(GET_NETWORK_CONNECTION, getC("/session/:sessionId/network_connection")); + result.put(IS_LOCKED, postC("/session/:sessionId/appium/device/is_locked")); + result.put(LONG_PRESS_KEY_CODE, + postC("/session/:sessionId/appium/device/long_press_keycode")); + result.put(OPEN_NOTIFICATIONS, + postC("/session/:sessionId/appium/device/open_notifications")); + result.put(PRESS_KEY_CODE, + postC("/session/:sessionId/appium/device/press_keycode")); + result.put(PUSH_FILE, postC("/session/:sessionId/appium/device/push_file")); + result.put(SET_NETWORK_CONNECTION, + postC("/session/:sessionId/network_connection")); + result.put(START_ACTIVITY, + postC("/session/:sessionId/appium/device/start_activity")); + result.put(TOGGLE_LOCATION_SERVICES, + postC("/session/:sessionId/appium/device/toggle_location_services")); + result.put(UNLOCK, postC("/session/:sessionId/appium/device/unlock")); + result.put(REPLACE_VALUE, postC("/session/:sessionId/appium/element/:id/replace_value")); + return result; + } + + /** + * This methods forms GET commands. + * + * @param url is the command URL + * @return an instance of {@link org.openqa.selenium.remote.CommandInfo} + */ public static CommandInfo getC(String url) { return new CommandInfo(url, HttpMethod.GET); } + /** + * This methods forms POST commands. + * + * @param url is the command URL + * @return an instance of {@link org.openqa.selenium.remote.CommandInfo} + */ public static CommandInfo postC(String url) { return new CommandInfo(url, HttpMethod.POST); } - private static Map getMobileCommands() { - if (commandRepository != null) { - return commandRepository; - } + /** + * This methods forms DELETE commands. + * + * @param url is the command URL + * @return an instance of {@link org.openqa.selenium.remote.CommandInfo} + */ + public static CommandInfo deleteC(String url) { + return new CommandInfo(url, HttpMethod.DELETE); + } - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put(RESET, postC("/session/:sessionId/appium/app/reset")) - .put(GET_STRINGS, postC("/session/:sessionId/appium/app/strings")) - .put(PRESS_KEY_CODE, postC("/session/:sessionId/appium/device/press_keycode")) - .put(LONG_PRESS_KEY_CODE, postC("/session/:sessionId/appium/device/long_press_keycode")) - .put(CURRENT_ACTIVITY, getC("/session/:sessionId/appium/device/current_activity")) - .put(SET_VALUE, postC("/session/:sessionId/appium/element/:id/value")) - .put(REPLACE_VALUE, postC("/session/:sessionId/appium/element/:id/replace_value")) - .put(PULL_FILE, postC("/session/:sessionId/appium/device/pull_file")) - .put(PULL_FOLDER, postC("/session/:sessionId/appium/device/pull_folder")) - .put(HIDE_KEYBOARD, postC("/session/:sessionId/appium/device/hide_keyboard")) - .put(PUSH_FILE, postC("/session/:sessionId/appium/device/push_file")) - .put(RUN_APP_IN_BACKGROUND, postC("/session/:sessionId/appium/app/background")) - .put(PERFORM_TOUCH_ACTION, postC("/session/:sessionId/touch/perform")) - .put(PERFORM_MULTI_TOUCH, postC("/session/:sessionId/touch/multi/perform")) - .put(IS_APP_INSTALLED, postC("/session/:sessionId/appium/device/app_installed")) - .put(INSTALL_APP, postC("/session/:sessionId/appium/device/install_app")) - .put(REMOVE_APP, postC("/session/:sessionId/appium/device/remove_app")) - .put(LAUNCH_APP, postC("/session/:sessionId/appium/app/launch")) - .put(CLOSE_APP, postC("/session/:sessionId/appium/app/close")) - .put(END_TEST_COVERAGE, postC("/session/:sessionId/appium/app/end_test_coverage")) - .put(LOCK, postC("/session/:sessionId/appium/device/lock")) - .put(IS_LOCKED, postC("/session/:sessionId/appium/device/is_locked")) - .put(SHAKE, postC("/session/:sessionId/appium/device/shake")) - .put(COMPLEX_FIND, postC("/session/:sessionId/appium/app/complex_find")) - .put(OPEN_NOTIFICATIONS, postC("/session/:sessionId/appium/device/open_notifications")) - .put(GET_NETWORK_CONNECTION, getC("/session/:sessionId/network_connection")) - .put(SET_NETWORK_CONNECTION, postC("/session/:sessionId/network_connection")) - .put(GET_SETTINGS, getC("/session/:sessionId/appium/settings")) - .put(SET_SETTINGS, postC("/session/:sessionId/appium/settings")) - .put(START_ACTIVITY, postC("/session/:sessionId/appium/device/start_activity")) - .put(TOGGLE_LOCATION_SERVICES, - postC("/session/:sessionId/appium/device/toggle_location_services")) - .put(GET_DEVICE_TIME, getC("/session/:sessionId/appium/device/system_time")) - .put(UNLOCK, postC("/session/:sessionId/appium/device/unlock")) - .put(GET_SESSION,getC("/session/:sessionId/")); + /** + * @param param is a parameter name. + * @param value is the parameter value. + * @return built {@link ImmutableMap}. + */ + protected static ImmutableMap prepareArguments(String param, + Object value) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put(param, value); + return builder.build(); + } + /** + * @param params is the array with parameter names. + * @param values is the array with parameter values. + * @return built {@link ImmutableMap}. + */ + protected static ImmutableMap prepareArguments(String[] params, + Object[] values) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (int i = 0; i < params.length; i++) { + if (!StringUtils.isBlank(params[i]) && (values[i] != null)) { + builder.put(params[i], values[i]); + } + } return builder.build(); } } diff --git a/src/main/java/io/appium/java_client/MobileElement.java b/src/main/java/io/appium/java_client/MobileElement.java index ecb878bb1..958c409ed 100644 --- a/src/main/java/io/appium/java_client/MobileElement.java +++ b/src/main/java/io/appium/java_client/MobileElement.java @@ -16,6 +16,8 @@ package io.appium.java_client; +import com.google.common.collect.ImmutableMap; + import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.Point; @@ -106,4 +108,16 @@ public List findElementsByXPath(String using) { @Override public List findElementsByAccessibilityId(String using) { return super.findElementsByAccessibilityId(using); } + + /** + * This method sets the new value of the attribute "value". + * + * @param value is the new value which should be set + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public void setValue(String value) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("id", id).put("value", value); + execute(MobileCommand.SET_VALUE, builder.build()); + } } diff --git a/src/main/java/io/appium/java_client/TouchAction.java b/src/main/java/io/appium/java_client/TouchAction.java index 23726b9d5..3d1ba66a2 100644 --- a/src/main/java/io/appium/java_client/TouchAction.java +++ b/src/main/java/io/appium/java_client/TouchAction.java @@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableMap; import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebElement; +import org.openqa.selenium.internal.HasIdentity; /** @@ -50,7 +50,7 @@ public TouchAction(MobileDriver driver) { * @return this TouchAction, for chaining. */ public TouchAction press(WebElement el) { - ActionParameter action = new ActionParameter("press", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("press", (HasIdentity) el); parameterBuilder.add(action); return this; } @@ -79,7 +79,7 @@ public TouchAction press(int x, int y) { * @return this TouchAction, for chaining. */ public TouchAction press(WebElement el, int x, int y) { - ActionParameter action = new ActionParameter("press", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("press", (HasIdentity) el); action.addParameter("x", x); action.addParameter("y", y); parameterBuilder.add(action); @@ -104,7 +104,7 @@ public TouchAction release() { * @return this TouchAction, for chaining. */ public TouchAction moveTo(WebElement el) { - ActionParameter action = new ActionParameter("moveTo", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("moveTo", (HasIdentity) el); parameterBuilder.add(action); return this; } @@ -135,7 +135,7 @@ public TouchAction moveTo(int x, int y) { * @return this TouchAction, for chaining. */ public TouchAction moveTo(WebElement el, int x, int y) { - ActionParameter action = new ActionParameter("moveTo", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("moveTo", (HasIdentity) el); action.addParameter("x", x); action.addParameter("y", y); parameterBuilder.add(action); @@ -149,7 +149,7 @@ public TouchAction moveTo(WebElement el, int x, int y) { * @return this TouchAction, for chaining. */ public TouchAction tap(WebElement el) { - ActionParameter action = new ActionParameter("tap", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("tap", (HasIdentity) el); parameterBuilder.add(action); return this; } @@ -178,7 +178,7 @@ public TouchAction tap(int x, int y) { * @return this TouchAction, for chaining. */ public TouchAction tap(WebElement el, int x, int y) { - ActionParameter action = new ActionParameter("tap", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("tap", (HasIdentity) el); action.addParameter("x", x); action.addParameter("y", y); parameterBuilder.add(action); @@ -216,7 +216,7 @@ public TouchAction waitAction(int ms) { * @return this TouchAction, for chaining. */ public TouchAction longPress(WebElement el) { - ActionParameter action = new ActionParameter("longPress", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("longPress", (HasIdentity) el); parameterBuilder.add(action); return this; } @@ -229,7 +229,7 @@ public TouchAction longPress(WebElement el) { * @return this TouchAction, for chaining. */ public TouchAction longPress(WebElement el, int duration) { - ActionParameter action = new ActionParameter("longPress", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("longPress", (HasIdentity) el); action.addParameter("duration", duration); parameterBuilder.add(action); return this; @@ -279,7 +279,7 @@ public TouchAction longPress(int x, int y, int duration) { * @return this TouchAction, for chaining. */ public TouchAction longPress(WebElement el, int x, int y) { - ActionParameter action = new ActionParameter("longPress", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("longPress", (HasIdentity) el); action.addParameter("x", x); action.addParameter("y", y); parameterBuilder.add(action); @@ -297,7 +297,7 @@ public TouchAction longPress(WebElement el, int x, int y) { * @return this TouchAction, for chaining. */ public TouchAction longPress(WebElement el, int x, int y, int duration) { - ActionParameter action = new ActionParameter("longPress", (RemoteWebElement) el); + ActionParameter action = new ActionParameter("longPress", (HasIdentity) el); action.addParameter("x", x); action.addParameter("y", y); action.addParameter("duration", duration); @@ -355,7 +355,7 @@ public ActionParameter(String actionName) { optionsBuilder = ImmutableMap.builder(); } - public ActionParameter(String actionName, RemoteWebElement el) { + public ActionParameter(String actionName, HasIdentity el) { this.actionName = actionName; optionsBuilder = ImmutableMap.builder(); addParameter("element", el.getId()); diff --git a/src/main/java/io/appium/java_client/android/AndroidDriver.java b/src/main/java/io/appium/java_client/android/AndroidDriver.java index 3ff12cc97..2ba445996 100644 --- a/src/main/java/io/appium/java_client/android/AndroidDriver.java +++ b/src/main/java/io/appium/java_client/android/AndroidDriver.java @@ -16,26 +16,25 @@ package io.appium.java_client.android; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static io.appium.java_client.MobileCommand.CURRENT_ACTIVITY; -import static io.appium.java_client.MobileCommand.END_TEST_COVERAGE; -import static io.appium.java_client.MobileCommand.GET_NETWORK_CONNECTION; -import static io.appium.java_client.MobileCommand.IS_LOCKED; -import static io.appium.java_client.MobileCommand.LOCK; -import static io.appium.java_client.MobileCommand.LONG_PRESS_KEY_CODE; -import static io.appium.java_client.MobileCommand.OPEN_NOTIFICATIONS; -import static io.appium.java_client.MobileCommand.PRESS_KEY_CODE; -import static io.appium.java_client.MobileCommand.PUSH_FILE; -import static io.appium.java_client.MobileCommand.SET_NETWORK_CONNECTION; -import static io.appium.java_client.MobileCommand.START_ACTIVITY; -import static io.appium.java_client.MobileCommand.TOGGLE_LOCATION_SERVICES; -import static io.appium.java_client.MobileCommand.UNLOCK; - -import com.google.common.collect.ImmutableMap; + +import static io.appium.java_client.android.AndroidMobileCommandHelper.currentActivityCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.endTestCoverageCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.getNetworkConnectionCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.isLockedCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.lockDeviceCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.longPressKeyCodeCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.openNotificationsCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.pressKeyCodeCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.pushFileCommandCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.setConnectionCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.startActivityCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.toggleLocationServicesCommand; +import static io.appium.java_client.android.AndroidMobileCommandHelper.unlockCommand; import io.appium.java_client.AppiumDriver; import io.appium.java_client.AppiumSetting; +import io.appium.java_client.CommandExecutionHelper; import io.appium.java_client.FindsByAndroidUIAutomator; import io.appium.java_client.android.internal.JsonToAndroidElementConverter; import io.appium.java_client.remote.MobilePlatform; @@ -43,12 +42,10 @@ import io.appium.java_client.service.local.AppiumServiceBuilder; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.Capabilities; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.HttpCommandExecutor; -import org.openqa.selenium.remote.Response; import org.openqa.selenium.remote.http.HttpClient; import java.io.File; @@ -195,7 +192,7 @@ public AndroidDriver(Capabilities desiredCapabilities) { * @param key code for the key pressed on the device. */ @Override public void pressKeyCode(int key) { - execute(PRESS_KEY_CODE, getCommandImmutableMap("keycode", key)); + CommandExecutionHelper.execute(this, pressKeyCodeCommand(key)); } /** @@ -206,9 +203,7 @@ public AndroidDriver(Capabilities desiredCapabilities) { * @see AndroidDeviceActionShortcuts#pressKeyCode(int, Integer). */ @Override public void pressKeyCode(int key, Integer metastate) { - String[] parameters = new String[] {"keycode", "metastate"}; - Object[] values = new Object[] {key, metastate}; - execute(PRESS_KEY_CODE, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, pressKeyCodeCommand(key, metastate)); } /** @@ -217,7 +212,7 @@ public AndroidDriver(Capabilities desiredCapabilities) { * @param key code for the long key pressed on the device. */ @Override public void longPressKeyCode(int key) { - execute(LONG_PRESS_KEY_CODE, getCommandImmutableMap("keycode", key)); + CommandExecutionHelper.execute(this, longPressKeyCodeCommand(key)); } /** @@ -228,21 +223,15 @@ public AndroidDriver(Capabilities desiredCapabilities) { * @see AndroidDeviceActionShortcuts#pressKeyCode(int, Integer) */ @Override public void longPressKeyCode(int key, Integer metastate) { - String[] parameters = new String[] {"keycode", "metastate"}; - Object[] values = new Object[] {key, metastate}; - execute(LONG_PRESS_KEY_CODE, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, longPressKeyCodeCommand(key, metastate)); } @Override public void setConnection(Connection connection) { - String[] parameters = new String[] {"name", "parameters"}; - Object[] values = - new Object[] {"network_connection", ImmutableMap.of("type", connection.bitMask)}; - execute(SET_NETWORK_CONNECTION, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, setConnectionCommand(connection)); } @Override public Connection getConnection() { - Response response = execute(GET_NETWORK_CONNECTION); - int bitMask = Integer.parseInt(response.getValue().toString()); + long bitMask = CommandExecutionHelper.execute(this, getNetworkConnectionCommand()); Connection[] types = Connection.values(); for (Connection connection: types) { @@ -255,9 +244,7 @@ public AndroidDriver(Capabilities desiredCapabilities) { } @Override public void pushFile(String remotePath, byte[] base64Data) { - String[] parameters = new String[] {"path", "data"}; - Object[] values = new Object[] {remotePath, base64Data}; - execute(PUSH_FILE, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, pushFileCommandCommand(remotePath, base64Data)); } @Override public void pushFile(String remotePath, File file) throws IOException { @@ -276,31 +263,9 @@ public AndroidDriver(Capabilities desiredCapabilities) { String intentCategory, String intentFlags, String optionalIntentArguments,boolean stopApp ) throws IllegalArgumentException { - - checkArgument((!StringUtils.isBlank(appPackage) - && !StringUtils.isBlank(appActivity)), - String.format("'%s' and '%s' are required.", "appPackage", "appActivity")); - - appWaitPackage = !StringUtils.isBlank(appWaitPackage) ? appWaitPackage : ""; - appWaitActivity = !StringUtils.isBlank(appWaitActivity) ? appWaitActivity : ""; - intentAction = !StringUtils.isBlank(intentAction) ? intentAction : ""; - intentCategory = !StringUtils.isBlank(intentCategory) ? intentCategory : ""; - intentFlags = !StringUtils.isBlank(intentFlags) ? intentFlags : ""; - optionalIntentArguments = !StringUtils.isBlank(optionalIntentArguments) - ? optionalIntentArguments : ""; - - ImmutableMap parameters = ImmutableMap - .builder().put("appPackage", appPackage) - .put("appActivity", appActivity) - .put("appWaitPackage", appWaitPackage) - .put("appWaitActivity", appWaitActivity) - .put("dontStopAppOnReset", !stopApp) - .put("intentAction", intentAction) - .put("intentCategory", intentCategory) - .put("intentFlags", intentFlags) - .put("optionalIntentArguments", optionalIntentArguments) - .build(); - execute(START_ACTIVITY, parameters); + CommandExecutionHelper.execute(this, startActivityCommand(appPackage, appActivity, + appWaitPackage, appWaitActivity, intentAction, intentCategory, intentFlags, + optionalIntentArguments, stopApp)); } @@ -344,9 +309,7 @@ public void startActivity(String appPackage, String appActivity, * @param path path to .ec file. */ public void endTestCoverage(String intent, String path) { - String[] parameters = new String[] {"intent", "path"}; - Object[] values = new Object[] {intent, path}; - execute(END_TEST_COVERAGE, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, endTestCoverageCommand(intent, path)); } /** @@ -355,15 +318,14 @@ public void endTestCoverage(String intent, String path) { * @return a current activity being run on the mobile device. */ public String currentActivity() { - Response response = execute(CURRENT_ACTIVITY); - return response.getValue().toString(); + return CommandExecutionHelper.execute(this, currentActivityCommand()); } /** * Open the notification shade, on Android devices. */ public void openNotifications() { - execute(OPEN_NOTIFICATIONS); + CommandExecutionHelper.execute(this, openNotificationsCommand()); } /** @@ -372,12 +334,11 @@ public void openNotifications() { * @return true if device is locked. False otherwise */ public boolean isLocked() { - Response response = execute(IS_LOCKED); - return Boolean.parseBoolean(response.getValue().toString()); + return CommandExecutionHelper.execute(this, isLockedCommand()); } public void toggleLocationServices() { - execute(TOGGLE_LOCATION_SERVICES); + CommandExecutionHelper.execute(this, toggleLocationServicesCommand()); } /** @@ -398,35 +359,33 @@ public void ignoreUnimportantViews(Boolean compress) { * @throws WebDriverException This method is not * applicable with browser/webview UI. */ - @SuppressWarnings("unchecked") @Override public T findElementByAndroidUIAutomator(String using) throws WebDriverException { - return (T) findElement("-android uiautomator", using); + return findElement("-android uiautomator", using); } /** * @throws WebDriverException This method is not * applicable with browser/webview UI. */ - @SuppressWarnings("unchecked") @Override public List findElementsByAndroidUIAutomator(String using) throws WebDriverException { - return (List) findElements("-android uiautomator", using); + return findElements("-android uiautomator", using); } /** * This method locks a device. */ public void lockDevice() { - execute(LOCK, ImmutableMap.of("seconds", 0)); + CommandExecutionHelper.execute(this, lockDeviceCommand()); } /** * This method unlocks a device. */ public void unlockDevice() { - execute(UNLOCK); + CommandExecutionHelper.execute(this, unlockCommand()); } } diff --git a/src/main/java/io/appium/java_client/android/AndroidElement.java b/src/main/java/io/appium/java_client/android/AndroidElement.java index 8bb762993..82bab2088 100644 --- a/src/main/java/io/appium/java_client/android/AndroidElement.java +++ b/src/main/java/io/appium/java_client/android/AndroidElement.java @@ -16,10 +16,10 @@ package io.appium.java_client.android; -import com.google.common.collect.ImmutableMap; +import static io.appium.java_client.android.AndroidMobileCommandHelper.replaceElementValueCommand; +import io.appium.java_client.CommandExecutionHelper; import io.appium.java_client.FindsByAndroidUIAutomator; -import io.appium.java_client.MobileCommand; import io.appium.java_client.MobileElement; import org.openqa.selenium.WebDriverException; @@ -50,9 +50,7 @@ public class AndroidElement extends MobileElement * This method replace current text value. * @param value a new value */ - @SuppressWarnings({"rawtypes", "unchecked"}) public void replaceValue(String value) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put("id", getId()).put("value", new String[] {value}); - execute(MobileCommand.REPLACE_VALUE, builder.build()); + public void replaceValue(String value) { + CommandExecutionHelper.execute(this, replaceElementValueCommand(this, value)); } } diff --git a/src/main/java/io/appium/java_client/android/AndroidMobileCommandHelper.java b/src/main/java/io/appium/java_client/android/AndroidMobileCommandHelper.java new file mode 100644 index 000000000..2ebd75b43 --- /dev/null +++ b/src/main/java/io/appium/java_client/android/AndroidMobileCommandHelper.java @@ -0,0 +1,298 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.android; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.ImmutableMap; + +import io.appium.java_client.MobileCommand; + +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.internal.HasIdentity; + +import java.util.AbstractMap; +import java.util.Map; + +/** + * This util class helps to prepare parameters of Android-specific JSONWP + * commands. + */ +public class AndroidMobileCommandHelper extends MobileCommand { + + /** + * This method forms a {@link java.util.Map} of parameters for the + * getting of the current activity. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> currentActivityCommand() { + return new AbstractMap.SimpleEntry>(CURRENT_ACTIVITY, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * ending of the test coverage. + * + * @param intent intent to broadcast. + * @param path path to .ec file. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> endTestCoverageCommand(String intent, + String path) { + String[] parameters = new String[] {"intent", "path"}; + Object[] values = new Object[] {intent, path}; + return new AbstractMap.SimpleEntry>(END_TEST_COVERAGE, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * getting of a network connection value. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> getNetworkConnectionCommand() { + return new AbstractMap.SimpleEntry>(GET_NETWORK_CONNECTION, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * checking of the device state (is it locked or not). + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> isLockedCommand() { + return new AbstractMap.SimpleEntry>(IS_LOCKED, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * key event invocation. + * + * @param key code for the key pressed on the device. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> pressKeyCodeCommand(int key) { + return new AbstractMap.SimpleEntry>(PRESS_KEY_CODE, prepareArguments("keycode", key)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * key event invocation. + * + * @param key code for the key pressed on the Android device. + * @param metastate metastate for the keypress. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> pressKeyCodeCommand(int key, + Integer metastate) { + String[] parameters = new String[] {"keycode", "metastate"}; + Object[] values = new Object[] {key, metastate}; + return new AbstractMap.SimpleEntry>(PRESS_KEY_CODE, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * long key event invocation. + * + * @param key code for the long key pressed on the device. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> longPressKeyCodeCommand(int key) { + return new AbstractMap.SimpleEntry>(LONG_PRESS_KEY_CODE, prepareArguments("keycode", key)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * long key event invocation. + * + * @param key code for the long key pressed on the Android device. + * @param metastate metastate for the long key press. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> longPressKeyCodeCommand(int key, + Integer metastate) { + String[] parameters = new String[] {"keycode", "metastate"}; + Object[] values = new Object[] {key, metastate}; + return new AbstractMap.SimpleEntry>(LONG_PRESS_KEY_CODE, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * notification opening. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> openNotificationsCommand() { + return new AbstractMap.SimpleEntry>(OPEN_NOTIFICATIONS, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * file pushing + * + * @param remotePath Path to file to write data to on remote device + * @param base64Data Base64 encoded byte array of data to write to remote device + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> pushFileCommandCommand(String remotePath, + byte[] base64Data) { + String[] parameters = new String[] {"path", "data"}; + Object[] values = new Object[] {remotePath, base64Data}; + return new AbstractMap.SimpleEntry>(PUSH_FILE, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * setting of device network connection. + * + * @param connection The bitmask of the desired connection + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> setConnectionCommand(Connection connection) { + String[] parameters = new String[] {"name", "parameters"}; + Object[] values = + new Object[] {"network_connection", ImmutableMap.of("type", connection.bitMask)}; + return new AbstractMap.SimpleEntry>(SET_NETWORK_CONNECTION, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * activity starting. + * + * @param appPackage The package containing the activity. [Required] + * @param appActivity The activity to start. [Required] + * @param appWaitPackage Automation will begin after this package starts. [Optional] + * @param appWaitActivity Automation will begin after this activity starts. [Optional] + * @param intentAction Intent action which will be used to start activity [Optional] + * @param intentCategory Intent category which will be used to start activity [Optional] + * @param intentFlags Flags that will be used to start activity [Optional] + * @param optionalIntentArguments Additional intent arguments that will be used to + * start activity [Optional] + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + * @throws IllegalArgumentException when any required argument is empty + */ + public static Map.Entry> startActivityCommand(String appPackage, + String appActivity, String appWaitPackage, String appWaitActivity, + String intentAction, String intentCategory, String intentFlags, + String optionalIntentArguments, boolean stopApp) throws IllegalArgumentException { + + checkArgument((!StringUtils.isBlank(appPackage) + && !StringUtils.isBlank(appActivity)), + String.format("'%s' and '%s' are required.", "appPackage", "appActivity")); + + String targetWaitPackage = !StringUtils.isBlank(appWaitPackage) ? appWaitPackage : ""; + String targetWaitActivity = !StringUtils.isBlank(appWaitActivity) ? appWaitActivity : ""; + String targetIntentAction = !StringUtils.isBlank(intentAction) ? intentAction : ""; + String targetIntentCategory = !StringUtils.isBlank(intentCategory) ? intentCategory : ""; + String targetIntentFlags = !StringUtils.isBlank(intentFlags) ? intentFlags : ""; + String targetOptionalIntentArguments = !StringUtils.isBlank(optionalIntentArguments) + ? optionalIntentArguments : ""; + + ImmutableMap parameters = ImmutableMap + .builder().put("appPackage", appPackage) + .put("appActivity", appActivity) + .put("appWaitPackage", targetWaitPackage) + .put("appWaitActivity", targetWaitActivity) + .put("dontStopAppOnReset", !stopApp) + .put("intentAction", targetIntentAction) + .put("intentCategory", targetIntentCategory) + .put("intentFlags", targetIntentFlags) + .put("optionalIntentArguments", targetOptionalIntentArguments) + .build(); + return new AbstractMap.SimpleEntry>(START_ACTIVITY, parameters); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * toggling of location services. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> toggleLocationServicesCommand() { + return new AbstractMap.SimpleEntry>(TOGGLE_LOCATION_SERVICES, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * device unlocking. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> unlockCommand() { + return new AbstractMap.SimpleEntry>(UNLOCK, ImmutableMap.of()); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * device locking. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> lockDeviceCommand() { + return new AbstractMap.SimpleEntry>(LOCK, prepareArguments("seconds", 0)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the element + * value replacement. It is used against input elements + * + * @param hasIdentityObject an instance which contains an element ID + * @param value a new value + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> replaceElementValueCommand( + HasIdentity hasIdentityObject, String value) { + String[] parameters = new String[] {"id", "value"}; + Object[] values = + new Object[] {hasIdentityObject.getId(), value}; + + return new AbstractMap.SimpleEntry>(REPLACE_VALUE, prepareArguments(parameters, values)); + } +} diff --git a/src/main/java/io/appium/java_client/android/Connection.java b/src/main/java/io/appium/java_client/android/Connection.java index 7c22a592d..f21117ec0 100644 --- a/src/main/java/io/appium/java_client/android/Connection.java +++ b/src/main/java/io/appium/java_client/android/Connection.java @@ -31,4 +31,8 @@ public enum Connection { Connection(int bitMask) { this.bitMask = bitMask; } + + public int getBitMask() { + return this.bitMask; + } } diff --git a/src/main/java/io/appium/java_client/events/DefaultAspect.java b/src/main/java/io/appium/java_client/events/DefaultAspect.java new file mode 100644 index 000000000..aea8385d1 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/DefaultAspect.java @@ -0,0 +1,549 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events; + + +import com.google.common.collect.ImmutableList; + +import io.appium.java_client.events.api.Listener; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.ContextAware; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.security.Credentials; +import org.springframework.context.support.AbstractApplicationContext; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Aspect +class DefaultAspect { + + private static final List> listenable = ImmutableList.of(WebDriver.class, + WebElement.class, WebDriver.Navigation.class, WebDriver.TargetLocator.class, + ContextAware.class, Alert.class, WebDriver.Options.class, WebDriver.Window.class); + + private static final String EXECUTION_NAVIGATION_TO = "execution(* org.openqa.selenium.WebDriver." + + "Navigation.get(..)) || " + + "execution(* org.openqa.selenium.WebDriver.Navigation.to(..)) || " + + "execution(* org.openqa.selenium.WebDriver.get(..))"; + private static final String EXECUTION_NAVIGATION_BACK = "execution(* org.openqa.selenium.WebDriver." + + "Navigation.back(..))"; + private static final String EXECUTION_NAVIGATION_FORWARD = "execution(* org.openqa.selenium.WebDriver." + + "Navigation.forward(..))"; + private static final String EXECUTION_NAVIGATION_REFRESH = "execution(* org.openqa.selenium.WebDriver." + + "Navigation.refresh(..))"; + private static final String EXECUTION_SEARCH = "execution(* org.openqa.selenium.SearchContext." + + "findElement(..)) || " + + "execution(* org.openqa.selenium.SearchContext.findElements(..))"; + private static final String EXECUTION_CLICK = "execution(* org.openqa.selenium.WebElement.click(..))"; + private static final String EXECUTION_CHANGE_VALUE = "execution(* org.openqa.selenium.WebElement." + + "sendKeys(..)) || " + + "execution(* org.openqa.selenium.WebElement.clear(..)) || " + + "execution(* io.appium.java_client.android.AndroidElement.replaceValue(..)) || " + + "execution(* io.appium.java_client.ios.IOSElement.setValue(..))"; + private static final String EXECUTION_SCRIPT = "execution(* org.openqa.selenium.JavascriptExecutor." + + "executeScript(..)) || " + + "execution(* org.openqa.selenium.JavascriptExecutor.executeAsyncScript(..))"; + private static final String EXECUTION_ALERT_ACCEPT = "execution(* org.openqa.selenium.Alert." + + "accept(..))"; + private static final String EXECUTION_ALERT_DISMISS = "execution(* org.openqa.selenium.Alert." + + "dismiss(..))"; + private static final String EXECUTION_ALERT_SEND_KEYS = "execution(* org.openqa.selenium.Alert." + + "sendKeys(..))"; + private static final String EXECUTION_ALERT_AUTHENTICATION = "execution(* org.openqa.selenium." + + "Alert.setCredentials(..)) || " + + "execution(* org.openqa.selenium.Alert.authenticateUsing(..))"; + private static final String EXECUTION_WINDOW_SET_SIZE = "execution(* org.openqa.selenium." + + "WebDriver.Window.setSize(..))"; + private static final String EXECUTION_WINDOW_SET_POSITION = "execution(* org.openqa.selenium.WebDriver." + + "Window.setPosition(..))"; + private static final String EXECUTION_WINDOW_MAXIMIZE = "execution(* org.openqa.selenium.WebDriver." + + "Window.maximize(..))"; + private static final String EXECUTION_ROTATE = "execution(* org.openqa.selenium.Rotatable" + + ".rotate(..))"; + private static final String EXECUTION_CONTEXT = "execution(* org.openqa.selenium.ContextAware." + + "context(..))"; + private static final String AROUND = "execution(* org.openqa.selenium.WebDriver.*(..)) || " + + "execution(* org.openqa.selenium.WebElement.*(..)) || " + + "execution(* org.openqa.selenium.WebDriver.Navigation.*(..)) || " + + "execution(* org.openqa.selenium.WebDriver.Options.*(..)) || " + + "execution(* org.openqa.selenium.WebDriver.TargetLocator.*(..)) || " + + "execution(* org.openqa.selenium.WebDriver.TargetLocator.*(..)) || " + + "execution(* org.openqa.selenium.JavascriptExecutor.*(..)) || " + + "execution(* org.openqa.selenium.ContextAware.*(..)) || " + + "execution(* io.appium.java_client.FindsByAccessibilityId.*(..)) || " + + "execution(* io.appium.java_client.FindsByAndroidUIAutomator.*(..)) || " + + "execution(* io.appium.java_client.FindsByIosUIAutomation.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByClassName.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByCssSelector.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsById.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByLinkText.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByName.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByTagName.*(..)) || " + + "execution(* org.openqa.selenium.internal.FindsByXPath.*(..)) || " + + "execution(* org.openqa.selenium.WebDriver.Window.*(..)) || " + + "execution(* io.appium.java_client.android.AndroidElement.*(..)) || " + + "execution(* io.appium.java_client.ios.IOSElement.*(..)) || " + + "execution(* io.appium.java_client.android.AndroidDriver.*(..)) || " + + "execution(* io.appium.java_client.ios.IOSDriver.*(..)) || " + + "execution(* io.appium.java_client.AppiumDriver.*(..)) || " + + "execution(* io.appium.java_client.MobileElement.*(..)) || " + + "execution(* org.openqa.selenium.remote.RemoteWebDriver.*(..)) || " + + "execution(* org.openqa.selenium.remote.RemoteWebElement.*(..)) || " + + "execution(* org.openqa.selenium.Alert.*(..))"; + + private final AbstractApplicationContext context; + private final WebDriver driver; + private final DefaultListener listener = new DefaultListener(); + + private static Throwable getRootCause(Throwable thrown) { + Class throwableClass = thrown.getClass(); + + if (!InvocationTargetException.class.equals(throwableClass) && !RuntimeException.class.equals(throwableClass)) { + return thrown; + } + if (thrown.getCause() != null) { + return getRootCause(thrown.getCause()); + } + return thrown; + } + + private static Class getClassForProxy(Class classOfObject) { + Class returnStatement = null; + for (Class c : listenable) { + if (!c.isAssignableFrom(classOfObject)) { + continue; + } + returnStatement = c; + } + return returnStatement; + } + + DefaultAspect(AbstractApplicationContext context, WebDriver driver) { + this.context = context; + this.driver = driver; + } + + private Object transformToListenable(Object toBeTransformed) { + if (toBeTransformed == null) { + return null; + } + + Object result = null; + if (getClassForProxy(toBeTransformed.getClass()) != null) { + result = context.getBean(DefaultBeanConfiguration.COMPONENT_BEAN, toBeTransformed); + } + return result; + } + + private List returnProxyList(List originalList) throws Exception { + try { + List proxyList = new ArrayList<>(); + for (Object o : originalList) { + if (getClassForProxy(o.getClass()) == null) { + proxyList.add(o); + } else { + proxyList.add(context.getBean(DefaultBeanConfiguration.COMPONENT_BEAN, o)); + } + } + return proxyList; + } catch (Exception e) { + throw e; + } + + } + + public void add(Collection listeners) { + listener.add(listeners); + } + + @Before(EXECUTION_NAVIGATION_TO) + public void beforeNavigateTo(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeNavigateTo(String.valueOf(joinPoint.getArgs()[0]), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_NAVIGATION_TO) + public void afterNavigateTo(JoinPoint joinPoint) throws Throwable { + try { + listener.afterNavigateTo(String.valueOf(joinPoint.getArgs()[0]), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_NAVIGATION_BACK) + public void beforeNavigateBack(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeNavigateBack(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_NAVIGATION_BACK) + public void afterNavigateBack(JoinPoint joinPoint) throws Throwable { + try { + listener.afterNavigateBack(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_NAVIGATION_FORWARD) + public void beforeNavigateForward(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeNavigateForward(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_NAVIGATION_FORWARD) + public void afterNavigateForward(JoinPoint joinPoint) throws Throwable { + try { + listener.afterNavigateForward(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_NAVIGATION_REFRESH) + public void beforeNavigateRefresh(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeNavigateRefresh(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_NAVIGATION_REFRESH) + public void afterNavigateRefresh(JoinPoint joinPoint) throws Throwable { + try { + listener.afterNavigateRefresh(driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @SuppressWarnings("unchecked") + private T castArgument(JoinPoint joinPoint, int argIndex) { + return (T) joinPoint.getArgs()[argIndex]; + } + + @SuppressWarnings("unchecked") + private T castTarget(JoinPoint joinPoint) { + return (T) joinPoint.getTarget(); + } + + @Before(EXECUTION_SEARCH) + public void beforeFindBy(JoinPoint joinPoint) throws Throwable { + try { + Object target = joinPoint.getTarget(); + if (!WebElement.class.isAssignableFrom(target.getClass())) { + listener.beforeFindBy((By) castArgument(joinPoint, 0), null, driver); + } else { + listener.beforeFindBy((By) castArgument(joinPoint, 0), + (WebElement) castTarget(joinPoint), driver); + } + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_SEARCH) + public void afterFindBy(JoinPoint joinPoint) throws Throwable { + try { + Object target = joinPoint.getTarget(); + if (!WebElement.class.isAssignableFrom(target.getClass())) { + listener.afterFindBy((By) castArgument(joinPoint, 0), null, driver); + } else { + listener.afterFindBy((By) castArgument(joinPoint, 0), + (WebElement) castTarget(joinPoint), driver); + } + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_CLICK) + public void beforeClickOn(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeClickOn((WebElement) castTarget(joinPoint), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_CLICK) + public void afterClickOn(JoinPoint joinPoint) throws Throwable { + try { + listener.afterClickOn((WebElement) castTarget(joinPoint), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_CHANGE_VALUE) + public void beforeChangeValueOf(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeChangeValueOf((WebElement) castTarget(joinPoint), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_CHANGE_VALUE) + public void afterChangeValueOf(JoinPoint joinPoint) throws Throwable { + try { + listener.afterChangeValueOf((WebElement) castTarget(joinPoint), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_SCRIPT) + public void beforeScript(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeScript(String.valueOf(joinPoint.getArgs()[0]), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_SCRIPT) + public void afterScript(JoinPoint joinPoint) throws Throwable { + try { + listener.afterScript(String.valueOf(joinPoint.getArgs()[0]), driver); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_ALERT_ACCEPT) + public void beforeAlertAccept(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeAlertAccept(driver, (Alert) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_ALERT_ACCEPT) + public void afterAlertAccept(JoinPoint joinPoint) throws Throwable { + try { + listener.afterAlertAccept(driver, (Alert) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_ALERT_DISMISS) + public void beforeAlertDismiss(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeAlertDismiss(driver, (Alert) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_ALERT_DISMISS) + public void afterAlertDismiss(JoinPoint joinPoint) throws Throwable { + try { + listener.afterAlertDismiss(driver, (Alert) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_ALERT_SEND_KEYS) + public void beforeAlertSendKeys(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeAlertSendKeys(driver, (Alert) castTarget(joinPoint), + String.valueOf(joinPoint.getArgs()[0])); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_ALERT_SEND_KEYS) + public void afterAlertSendKeys(JoinPoint joinPoint) throws Throwable { + try { + listener.afterAlertSendKeys(driver, (Alert) castTarget(joinPoint), + String.valueOf(joinPoint.getArgs()[0])); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_ALERT_AUTHENTICATION) + public void beforeAlertAuthentication(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeAuthentication(driver, + (Alert) castTarget(joinPoint), (Credentials) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_ALERT_AUTHENTICATION) + public void afterAlertAuthentication(JoinPoint joinPoint) throws Throwable { + try { + listener.afterAuthentication(driver, (Alert) castTarget(joinPoint), + (Credentials) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_WINDOW_SET_SIZE) + public void beforeWindowIsResized(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeWindowChangeSize(driver, + (WebDriver.Window) castTarget(joinPoint), (Dimension) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_WINDOW_SET_SIZE) + public void afterWindowIsResized(JoinPoint joinPoint) throws Throwable { + try { + listener.afterWindowChangeSize(driver, (WebDriver.Window) castTarget(joinPoint), + (Dimension) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_WINDOW_SET_POSITION) + public void beforeWindowIsMoved(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeWindowIsMoved(driver, (WebDriver.Window) castTarget(joinPoint), + (Point) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_WINDOW_SET_POSITION) + public void afterWindowIsMoved(JoinPoint joinPoint) throws Throwable { + try { + listener.afterWindowIsMoved(driver, (WebDriver.Window) castTarget(joinPoint), + (Point) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_WINDOW_MAXIMIZE) + public void beforeMaximization(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeWindowIsMaximized(driver, (WebDriver.Window) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @After(EXECUTION_WINDOW_MAXIMIZE) + public void afterMaximization(JoinPoint joinPoint) throws Throwable { + try { + listener.afterWindowIsMaximized(driver, (WebDriver.Window) castTarget(joinPoint)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_ROTATE) + public void beforeRotation(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeRotation(driver, (ScreenOrientation) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + + } + + @After(EXECUTION_ROTATE) + public void afterRotation(JoinPoint joinPoint) throws Throwable { + try { + listener.afterRotation(driver, (ScreenOrientation) castArgument(joinPoint, 0)); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Before(EXECUTION_CONTEXT) + public void beforeSwitchingToContext(JoinPoint joinPoint) throws Throwable { + try { + listener.beforeSwitchingToContext(driver, String.valueOf(joinPoint.getArgs()[0])); + } catch (Throwable t) { + throw getRootCause(t); + } + + } + + @After(EXECUTION_CONTEXT) + public void afterSwitchingToContextn(JoinPoint joinPoint) throws Throwable { + try { + listener.afterSwitchingToContext(driver, String.valueOf(joinPoint.getArgs()[0])); + } catch (Throwable t) { + throw getRootCause(t); + } + } + + @Around(AROUND) + public Object doAround(ProceedingJoinPoint point) throws Throwable { + Throwable t = null; + Object result = null; + try { + result = point.proceed(); + } catch (Throwable e) { + t = e; + } + if (t != null) { + Throwable rootCause = getRootCause(t); + listener.onException(rootCause, driver); + throw rootCause; + } + + if (result == null) { // maybe it was "void" + return result; + } + if (List.class.isAssignableFrom(result.getClass())) { + return returnProxyList((List) result); + } + + return transformToListenable(result); + } +} diff --git a/src/main/java/io/appium/java_client/events/DefaultBeanConfiguration.java b/src/main/java/io/appium/java_client/events/DefaultBeanConfiguration.java new file mode 100644 index 000000000..48921d1a8 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/DefaultBeanConfiguration.java @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Scope; +import org.springframework.context.support.AbstractApplicationContext; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@EnableAspectJAutoProxy(proxyTargetClass = true) +class DefaultBeanConfiguration { + + public static final String COMPONENT_BEAN = "component"; + public static final String WEB_DRIVER_BEAN = "webdriver"; + + private final List listeners = new ArrayList<>(); + private WebDriver driver; + private AbstractApplicationContext context; + + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + @Bean(name = WEB_DRIVER_BEAN) + public T getListenableWebdriver(T driver, List listeners, + AbstractApplicationContext context) { + this.driver = driver; + this.listeners.addAll(listeners); + this.context = context; + return driver; + } + + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + @Bean(name = "webdriverAspect") + public DefaultAspect getAspect() { + DefaultAspect aspect = new DefaultAspect(context, driver); + aspect.add(listeners); + return aspect; + } + + @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + @Bean(name = COMPONENT_BEAN) + public Object getComponent(Object component) { + return component; + } +} diff --git a/src/main/java/io/appium/java_client/events/DefaultListener.java b/src/main/java/io/appium/java_client/events/DefaultListener.java new file mode 100644 index 000000000..e665ad5e3 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/DefaultListener.java @@ -0,0 +1,209 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events; + +import io.appium.java_client.events.api.Listener; +import io.appium.java_client.events.api.general.AlertEventListener; +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; +import io.appium.java_client.events.api.general.ElementEventListener; +import io.appium.java_client.events.api.general.JavaScriptEventListener; +import io.appium.java_client.events.api.general.ListensToException; +import io.appium.java_client.events.api.general.NavigationEventListener; +import io.appium.java_client.events.api.general.SearchingEventListener; +import io.appium.java_client.events.api.general.WindowEventListener; +import io.appium.java_client.events.api.mobile.ContextEventListener; +import io.appium.java_client.events.api.mobile.RotationEventListener; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.security.Credentials; +import org.openqa.selenium.support.events.WebDriverEventListener; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class DefaultListener implements Listener, AppiumWebDriverEventListener, ListensToException, + SearchingEventListener, NavigationEventListener, + JavaScriptEventListener, ElementEventListener, AlertEventListener, + WindowEventListener, ContextEventListener, RotationEventListener { + + private final List listeners = new ArrayList<>(); + + private Object dispatcher = Proxy.newProxyInstance(Listener.class.getClassLoader(), + new Class[] {AlertEventListener.class, + ContextEventListener.class, ElementEventListener.class, JavaScriptEventListener.class, + ListensToException.class, NavigationEventListener.class, RotationEventListener.class, + SearchingEventListener.class, WindowEventListener.class, + WebDriverEventListener.class}, + new ListenerInvocationHandler(listeners)); + + @Override public void beforeNavigateTo(String url, WebDriver driver) { + ((NavigationEventListener) dispatcher).beforeNavigateTo(url, driver); + } + + @Override public void afterNavigateTo(String url, WebDriver driver) { + ((NavigationEventListener) dispatcher).afterNavigateTo(url, driver); + } + + @Override public void beforeNavigateBack(WebDriver driver) { + ((NavigationEventListener) dispatcher).beforeNavigateBack(driver); + } + + @Override public void afterNavigateBack(WebDriver driver) { + ((NavigationEventListener) dispatcher).afterNavigateBack(driver); + } + + @Override public void beforeNavigateForward(WebDriver driver) { + ((NavigationEventListener) dispatcher).beforeNavigateForward(driver); + } + + @Override public void afterNavigateForward(WebDriver driver) { + ((NavigationEventListener) dispatcher).afterNavigateForward(driver); + } + + @Override public void beforeNavigateRefresh(WebDriver driver) { + ((NavigationEventListener) dispatcher).beforeNavigateRefresh(driver); + } + + @Override public void afterNavigateRefresh(WebDriver driver) { + ((NavigationEventListener) dispatcher).afterNavigateRefresh(driver); + } + + @Override public void beforeFindBy(By by, WebElement element, WebDriver driver) { + ((SearchingEventListener) dispatcher).beforeFindBy(by, element, driver); + } + + @Override public void afterFindBy(By by, WebElement element, WebDriver driver) { + ((SearchingEventListener) dispatcher).afterFindBy(by, element, driver); + } + + @Override public void beforeClickOn(WebElement element, WebDriver driver) { + ((ElementEventListener) dispatcher).beforeClickOn(element, driver); + } + + @Override public void afterClickOn(WebElement element, WebDriver driver) { + ((ElementEventListener) dispatcher).afterClickOn(element, driver); + } + + @Override public void beforeChangeValueOf(WebElement element, WebDriver driver) { + ((ElementEventListener) dispatcher).beforeChangeValueOf(element, driver); + } + + @Override public void afterChangeValueOf(WebElement element, WebDriver driver) { + ((ElementEventListener) dispatcher).afterChangeValueOf(element, driver); + } + + @Override public void beforeScript(String script, WebDriver driver) { + ((JavaScriptEventListener) dispatcher).beforeScript(script, driver); + } + + @Override public void afterScript(String script, WebDriver driver) { + ((JavaScriptEventListener) dispatcher).afterScript(script, driver); + } + + @Override public void onException(Throwable throwable, WebDriver driver) { + ((ListensToException) dispatcher).onException(throwable, driver); + } + + public void add(Collection listeners) { + this.listeners.addAll(listeners); + } + + @Override public void beforeAlertAccept(WebDriver driver, Alert alert) { + ((AlertEventListener) dispatcher).beforeAlertAccept(driver, alert); + } + + @Override public void afterAlertAccept(WebDriver driver, Alert alert) { + ((AlertEventListener) dispatcher).afterAlertAccept(driver, alert); + } + + @Override public void afterAlertDismiss(WebDriver driver, Alert alert) { + ((AlertEventListener) dispatcher).afterAlertDismiss(driver, alert); + } + + @Override public void beforeAlertDismiss(WebDriver driver, Alert alert) { + ((AlertEventListener) dispatcher).beforeAlertDismiss(driver, alert); + } + + @Override public void beforeAlertSendKeys(WebDriver driver, Alert alert, String keys) { + ((AlertEventListener) dispatcher).beforeAlertSendKeys(driver, alert, keys); + } + + @Override public void afterAlertSendKeys(WebDriver driver, Alert alert, String keys) { + ((AlertEventListener) dispatcher).afterAlertSendKeys(driver, alert, keys); + } + + @Override + public void beforeAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + ((AlertEventListener) dispatcher).beforeAuthentication(driver, alert, credentials); + } + + @Override + public void afterAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + ((AlertEventListener) dispatcher).afterAuthentication(driver, alert, credentials); + } + + @Override public void beforeWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + ((WindowEventListener) dispatcher).beforeWindowChangeSize(driver, window, targetSize); + } + + @Override public void afterWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + ((WindowEventListener) dispatcher).afterWindowChangeSize(driver, window, targetSize); + } + + @Override + public void beforeWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + ((WindowEventListener) dispatcher).beforeWindowIsMoved(driver, window, targetPoint); + } + + @Override + public void afterWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + ((WindowEventListener) dispatcher).afterWindowIsMoved(driver, window, targetPoint); + } + + @Override public void beforeWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + ((WindowEventListener) dispatcher).beforeWindowIsMaximized(driver, window); + } + + @Override public void afterWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + ((WindowEventListener) dispatcher).afterWindowIsMaximized(driver, window); + } + + @Override public void beforeSwitchingToContext(WebDriver driver, String context) { + ((ContextEventListener) dispatcher).beforeSwitchingToContext(driver, context); + } + + @Override public void afterSwitchingToContext(WebDriver driver, String context) { + ((ContextEventListener) dispatcher).afterSwitchingToContext(driver, context); + } + + @Override public void beforeRotation(WebDriver driver, ScreenOrientation orientation) { + ((RotationEventListener) dispatcher).beforeRotation(driver, orientation); + } + + @Override public void afterRotation(WebDriver driver, ScreenOrientation orientation) { + ((RotationEventListener) dispatcher).afterRotation(driver, orientation); + } +} diff --git a/src/main/java/io/appium/java_client/events/EventFiringWebDriverFactory.java b/src/main/java/io/appium/java_client/events/EventFiringWebDriverFactory.java new file mode 100644 index 000000000..0d242f594 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/EventFiringWebDriverFactory.java @@ -0,0 +1,88 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events; + + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.support.AbstractApplicationContext; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; + +public class EventFiringWebDriverFactory { + + /** + * This method makes an event firing instance of {@link org.openqa.selenium.WebDriver} + * + * @param driver an original instance of {@link org.openqa.selenium.WebDriver} that is + * supposed to be listenable + * @param T + * @return an instance of {@link org.openqa.selenium.WebDriver} that fires events + */ + public static T getEventFiringWebDriver(T driver) { + return getEventFiringWebDriver(driver, Collections.emptyList()); + } + + /** + * This method makes an event firing instance of {@link org.openqa.selenium.WebDriver} + * + * @param driver an original instance of {@link org.openqa.selenium.WebDriver} that is + * supposed to be listenable + * @param listeners is a set of {@link io.appium.java_client.events.api.Listener} that + * is supposed to be used for the event firing + * @param T + * @return an instance of {@link org.openqa.selenium.WebDriver} that fires events + */ + public static T getEventFiringWebDriver(T driver, Listener ... listeners) { + return getEventFiringWebDriver(driver, Arrays.asList(listeners)); + } + + /** + * This method makes an event firing instance of {@link org.openqa.selenium.WebDriver} + * + * @param driver an original instance of {@link org.openqa.selenium.WebDriver} that is + * supposed to be listenable + * @param listeners is a collection of {@link io.appium.java_client.events.api.Listener} that + * is supposed to be used for the event firing + * @param T + * @return an instance of {@link org.openqa.selenium.WebDriver} that fires events + */ + @SuppressWarnings("unchecked") + public static T getEventFiringWebDriver(T driver, Collection listeners) { + List listenerList = new ArrayList<>(); + Iterator providers = ServiceLoader.load( + Listener.class).iterator(); + + while (providers.hasNext()) { + listenerList.add(providers.next()); + } + + listenerList.addAll(listeners); + + AbstractApplicationContext context = new AnnotationConfigApplicationContext( + DefaultBeanConfiguration.class); + return (T) context.getBean( + DefaultBeanConfiguration.WEB_DRIVER_BEAN, driver, listenerList, context); + } +} diff --git a/src/main/java/io/appium/java_client/events/ListenerInvocationHandler.java b/src/main/java/io/appium/java_client/events/ListenerInvocationHandler.java new file mode 100644 index 000000000..d6e51c0ce --- /dev/null +++ b/src/main/java/io/appium/java_client/events/ListenerInvocationHandler.java @@ -0,0 +1,62 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.support.events.WebDriverEventListener; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.List; + +class ListenerInvocationHandler implements InvocationHandler { + + private final List listeners; + + ListenerInvocationHandler(List listeners) { + this.listeners = listeners; + } + + private Method findElementInWebDriverEventListener(Method m) { + try { + return WebDriverEventListener.class.getMethod(m.getName(), m.getParameterTypes()); + } catch (NoSuchMethodException e) { + return null; + } + } + + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + for (Listener l: listeners) { + boolean isInvoked = false; + if (method.getDeclaringClass().isAssignableFrom(l.getClass())) { + method.invoke(l, args); + isInvoked = true; + } + + if (isInvoked) { + continue; + } + + Method webDriverEventListenerMethod = findElementInWebDriverEventListener(method); + if (webDriverEventListenerMethod != null + && WebDriverEventListener.class.isAssignableFrom(l.getClass())) { + webDriverEventListenerMethod.invoke(l, args); + } + } + return null; + } +} diff --git a/src/main/java/io/appium/java_client/events/api/Listener.java b/src/main/java/io/appium/java_client/events/api/Listener.java new file mode 100644 index 000000000..e1ea35c1d --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/Listener.java @@ -0,0 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api; + +/** + * This interface just marks event listeners. + */ +public interface Listener { +} diff --git a/src/main/java/io/appium/java_client/events/api/general/AlertEventListener.java b/src/main/java/io/appium/java_client/events/api/general/AlertEventListener.java new file mode 100644 index 000000000..eae7ebdf7 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/AlertEventListener.java @@ -0,0 +1,101 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.Alert; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.security.Credentials; + +public interface AlertEventListener extends Listener { + + /** + * This action will be performed each time before {@link org.openqa.selenium.Alert#accept()} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which is being accepted + */ + void beforeAlertAccept(WebDriver driver, Alert alert); + + /** + * This action will be performed each time after {@link Alert#accept()} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which has been accepted + */ + void afterAlertAccept(WebDriver driver, Alert alert); + + /** + * This action will be performed each time before {@link Alert#dismiss()} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which which is being dismissed + */ + void afterAlertDismiss(WebDriver driver, Alert alert); + + /** + * This action will be performed each time after {@link Alert#dismiss()} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which has been dismissed + */ + void beforeAlertDismiss(WebDriver driver, Alert alert); + + /** + * This action will be performed each time before + * {@link org.openqa.selenium.Alert#sendKeys(String)} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which is receiving keys + * @param keys Keys which are being sent + */ + void beforeAlertSendKeys(WebDriver driver, Alert alert, String keys); + + /** + * This action will be performed each time after + * {@link org.openqa.selenium.Alert#sendKeys(String)} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which has received keys + * @param keys Keys which have been sent + */ + void afterAlertSendKeys(WebDriver driver, Alert alert, String keys); + + /** + * This action will be performed each time before + * {@link org.openqa.selenium.Alert#setCredentials(Credentials)} and + * {@link org.openqa.selenium.Alert#authenticateUsing(Credentials)} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which is receiving user credentials + * @param credentials which are being sent + */ + void beforeAuthentication(WebDriver driver, Alert alert, + Credentials credentials); + + /** + * This action will be performed each time after + * {@link org.openqa.selenium.Alert#setCredentials(Credentials)} and + * {@link org.openqa.selenium.Alert#authenticateUsing(Credentials)} + * + * @param driver WebDriver + * @param alert {@link org.openqa.selenium.Alert} which has received user credentials + * @param credentials which have been sent + */ + void afterAuthentication(WebDriver driver, Alert alert, + Credentials credentials); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/AppiumWebDriverEventListener.java b/src/main/java/io/appium/java_client/events/api/general/AppiumWebDriverEventListener.java new file mode 100644 index 000000000..5b249b3f2 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/AppiumWebDriverEventListener.java @@ -0,0 +1,25 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.support.events.WebDriverEventListener; + +public interface AppiumWebDriverEventListener extends Listener, WebDriverEventListener, ListensToException, + SearchingEventListener, NavigationEventListener, + JavaScriptEventListener, ElementEventListener { +} diff --git a/src/main/java/io/appium/java_client/events/api/general/ElementEventListener.java b/src/main/java/io/appium/java_client/events/api/general/ElementEventListener.java new file mode 100644 index 000000000..83a521da8 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/ElementEventListener.java @@ -0,0 +1,59 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public interface ElementEventListener extends Listener { + /** + * Called before {@link org.openqa.selenium.WebElement#click WebElement.click()}. + * + * @param driver WebDriver + * @param element the WebElement being used for the action + */ + void beforeClickOn(WebElement element, WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebElement#click WebElement.click()}. + * Not called, if an exception is thrown. + * + * @param driver WebDriver + * @param element the WebElement being used for the action + */ + void afterClickOn(WebElement element, WebDriver driver); + + /** + * Called before {@link org.openqa.selenium.WebElement#clear WebElement.clear()}, + * {@link org.openqa.selenium.WebElement#sendKeys WebElement.sendKeys(...)}. + * + * @param driver WebDriver + * @param element the WebElement being used for the action + */ + void beforeChangeValueOf(WebElement element, WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebElement#clear WebElement.clear()}, + * {@link org.openqa.selenium.WebElement#sendKeys WebElement.sendKeys(...)} . + * Not called, if an exception is thrown. + * + * @param driver WebDriver + * @param element the WebElement being used for the action + */ + void afterChangeValueOf(WebElement element, WebDriver driver); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/JavaScriptEventListener.java b/src/main/java/io/appium/java_client/events/api/general/JavaScriptEventListener.java new file mode 100644 index 000000000..4edaaf708 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/JavaScriptEventListener.java @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; + +public interface JavaScriptEventListener extends Listener { + /** + * Called before + * {@link org.openqa.selenium.JavascriptExecutor#executeScript(java.lang.String, + * java.lang.Object[]) } + * + * @param driver WebDriver + * @param script the script to be executed + */ + void beforeScript(String script, WebDriver driver); + + /** + * Called after + * {@link org.openqa.selenium.remote.RemoteWebDriver#executeScript(java.lang.String, + * java.lang.Object[]) }. + * Not called if an exception is thrown + * + * @param driver WebDriver + * @param script the script that was executed + */ + void afterScript(String script, WebDriver driver); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/ListensToException.java b/src/main/java/io/appium/java_client/events/api/general/ListensToException.java new file mode 100644 index 000000000..89733e693 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/ListensToException.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; + +public interface ListensToException extends Listener { + /** + * Called whenever an exception would be thrown. + * @param throwable the exception that will be thrown + * @param driver WebDriver + */ + void onException(Throwable throwable, WebDriver driver); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/NavigationEventListener.java b/src/main/java/io/appium/java_client/events/api/general/NavigationEventListener.java new file mode 100644 index 000000000..d8662960f --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/NavigationEventListener.java @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; + +public interface NavigationEventListener extends Listener { + + /** + * Called before {@link org.openqa.selenium.WebDriver#get get(String url)} + * respectively {@link org.openqa.selenium.WebDriver.Navigation#to + * navigate().to(String url)}. + * + * @param url URL + * @param driver WebDriver + */ + void beforeNavigateTo(String url, WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebDriver#get get(String url)} + * respectively {@link org.openqa.selenium.WebDriver.Navigation#to + * navigate().to(String url)}. Not called, if an exception is thrown. + * + * @param url URL + * @param driver WebDriver + */ + void afterNavigateTo(String url, WebDriver driver); + + /** + * Called before {@link org.openqa.selenium.WebDriver.Navigation#back + * navigate().back()}. + * + * @param driver WebDriver + */ + void beforeNavigateBack(WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebDriver.Navigation + * navigate().back()}. Not called, if an exception is thrown. + * + * @param driver WebDriver + */ + void afterNavigateBack(WebDriver driver); + + /** + * Called before {@link org.openqa.selenium.WebDriver.Navigation#forward + * navigate().forward()}. + * + * @param driver WebDriver + */ + void beforeNavigateForward(WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebDriver.Navigation#forward + * navigate().forward()}. Not called, if an exception is thrown. + * + * @param driver WebDriver + */ + void afterNavigateForward(WebDriver driver); + + /** + * Called before {@link org.openqa.selenium.WebDriver.Navigation#refresh navigate().refresh()}. + * + * @param driver WebDriver + */ + void beforeNavigateRefresh(WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebDriver.Navigation#refresh navigate().refresh()}. + * + * @param driver WebDriver + */ + void afterNavigateRefresh(WebDriver driver); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/SearchingEventListener.java b/src/main/java/io/appium/java_client/events/api/general/SearchingEventListener.java new file mode 100644 index 000000000..bd5d47ae3 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/SearchingEventListener.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public interface SearchingEventListener extends Listener { + + /** + * Called before {@link org.openqa.selenium.WebDriver#findElement WebDriver.findElement(...)}, + * or + * {@link org.openqa.selenium.WebDriver#findElements WebDriver.findElements(...)}, or + * {@link org.openqa.selenium.WebElement#findElement WebElement.findElement(...)}, or + * {@link org.openqa.selenium.WebElement#findElement WebElement.findElements(...)}. + * + * @param element will be null, if a find method of WebDriver + * is called. + * @param by locator being used + * @param driver WebDriver + */ + void beforeFindBy(By by, WebElement element, WebDriver driver); + + /** + * Called after {@link org.openqa.selenium.WebDriver#findElement WebDriver.findElement(...)}, + * or + * {@link org.openqa.selenium.WebDriver#findElements WebDriver.findElements(...)}, or + * {@link org.openqa.selenium.WebElement#findElement WebElement.findElement(...)}, or + * {@link org.openqa.selenium.WebElement#findElement WebElement.findElements(...)}. + * + * @param element will be null, if a find method of WebDriver + * is called. + * @param by locator being used + * @param driver WebDriver + */ + void afterFindBy(By by, WebElement element, WebDriver driver); +} diff --git a/src/main/java/io/appium/java_client/events/api/general/WindowEventListener.java b/src/main/java/io/appium/java_client/events/api/general/WindowEventListener.java new file mode 100644 index 000000000..b9ab08fbc --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/general/WindowEventListener.java @@ -0,0 +1,87 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.general; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebDriver; + +public interface WindowEventListener extends Listener { + /** + * This action will be performed each time before + * {@link org.openqa.selenium.WebDriver.Window#setSize(Dimension)} + * + * @param driver WebDriver + * @param window is the window whose size is going to be changed + * @param targetSize is the new size + */ + void beforeWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize); + + /** + * This action will be performed each time after + * {@link WebDriver.Window#setSize(Dimension)} + * + * @param driver WebDriver + * @param window is the window whose size has been changed + * @param targetSize is the new size + */ + void afterWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize); + + /** + * This action will be performed each time before + * {@link WebDriver.Window#setPosition(org.openqa.selenium.Point)} + * + * @param driver WebDriver + * @param window is the window whose position is going to be changed + * @param targetPoint is the new window coordinates + */ + void beforeWindowIsMoved(WebDriver driver, WebDriver.Window window, + Point targetPoint); + + /** + * This action will be performed each time after + * {@link WebDriver.Window#setPosition(org.openqa.selenium.Point)} + * + * @param driver WebDriver + * @param window is the window whose position has been changed + * @param targetPoint is the new window coordinates + */ + void afterWindowIsMoved(WebDriver driver, WebDriver.Window window, + Point targetPoint); + + + /** + * This action will be performed each time before + * {@link WebDriver.Window#maximize()} + * + * @param driver WebDriver + * @param window is the window which is going to be maximized + */ + void beforeWindowIsMaximized(WebDriver driver, WebDriver.Window window); + + /** + * This action will be performed each time after + * {@link WebDriver.Window#maximize()} + * + * @param driver WebDriver + * @param window is the window which has been maximized + */ + void afterWindowIsMaximized(WebDriver driver, WebDriver.Window window); +} diff --git a/src/main/java/io/appium/java_client/events/api/mobile/ContextEventListener.java b/src/main/java/io/appium/java_client/events/api/mobile/ContextEventListener.java new file mode 100644 index 000000000..f3282df76 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/mobile/ContextEventListener.java @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.mobile; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.WebDriver; + +public interface ContextEventListener extends Listener { + + /** + * Called before {@link org.openqa.selenium.ContextAware#context(String)}. + * + * @param driver Webdriver + * @param context The context that is needed to switch to. + */ + void beforeSwitchingToContext(WebDriver driver, String context); + + /** + * Called after {@link org.openqa.selenium.ContextAware#context(String)}. + * + * @param driver Webdriver + * @param context The context that is needed to switch to. + */ + void afterSwitchingToContext(WebDriver driver, String context); +} diff --git a/src/main/java/io/appium/java_client/events/api/mobile/RotationEventListener.java b/src/main/java/io/appium/java_client/events/api/mobile/RotationEventListener.java new file mode 100644 index 000000000..a500c3d76 --- /dev/null +++ b/src/main/java/io/appium/java_client/events/api/mobile/RotationEventListener.java @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.events.api.mobile; + +import io.appium.java_client.events.api.Listener; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; + +public interface RotationEventListener extends Listener { + + /** + * Called before {@link org.openqa.selenium.Rotatable#rotate(ScreenOrientation)}. + * + * @param driver WebDriver + * @param orientation the desired screen orientation + */ + void beforeRotation(WebDriver driver, ScreenOrientation orientation); + + /** + * Called after {@link org.openqa.selenium.Rotatable#rotate(ScreenOrientation)}. + * + * @param driver WebDriver + * @param orientation the desired screen orientation + */ + void afterRotation(WebDriver driver, ScreenOrientation orientation); +} diff --git a/src/main/java/io/appium/java_client/ios/IOSDriver.java b/src/main/java/io/appium/java_client/ios/IOSDriver.java index d7587823b..3c8d839f8 100644 --- a/src/main/java/io/appium/java_client/ios/IOSDriver.java +++ b/src/main/java/io/appium/java_client/ios/IOSDriver.java @@ -16,13 +16,12 @@ package io.appium.java_client.ios; -import static io.appium.java_client.MobileCommand.HIDE_KEYBOARD; -import static io.appium.java_client.MobileCommand.LOCK; -import static io.appium.java_client.MobileCommand.SHAKE; - -import com.google.common.collect.ImmutableMap; +import static io.appium.java_client.ios.IOSMobileCommandHelper.hideKeyboardCommand; +import static io.appium.java_client.ios.IOSMobileCommandHelper.lockDeviceCommand; +import static io.appium.java_client.ios.IOSMobileCommandHelper.shakeCommand; import io.appium.java_client.AppiumDriver; +import io.appium.java_client.CommandExecutionHelper; import io.appium.java_client.FindsByIosUIAutomation; import io.appium.java_client.ios.internal.JsonToIOSElementConverter; import io.appium.java_client.remote.MobilePlatform; @@ -176,44 +175,40 @@ public IOSDriver(Capabilities desiredCapabilities) { * @see IOSDeviceActionShortcuts#hideKeyboard(String, String). */ @Override public void hideKeyboard(String strategy, String keyName) { - String[] parameters = new String[] {"strategy", "key"}; - Object[] values = new Object[] {strategy, keyName}; - execute(HIDE_KEYBOARD, getCommandImmutableMap(parameters, values)); + CommandExecutionHelper.execute(this, hideKeyboardCommand(strategy, keyName)); } /** * @see IOSDeviceActionShortcuts#hideKeyboard(String). */ @Override public void hideKeyboard(String keyName) { - execute(HIDE_KEYBOARD, ImmutableMap.of("keyName", keyName)); + CommandExecutionHelper.execute(this, hideKeyboardCommand(keyName)); } /** * @see IOSDeviceActionShortcuts#shake(). */ @Override public void shake() { - execute(SHAKE); + CommandExecutionHelper.execute(this, shakeCommand()); } /** * @throws WebDriverException * This method is not applicable with browser/webview UI. */ - @SuppressWarnings("unchecked") @Override public T findElementByIosUIAutomation(String using) throws WebDriverException { - return (T) findElement("-ios uiautomation", using); + return findElement("-ios uiautomation", using); } /** * @throws WebDriverException This method is not applicable with browser/webview UI. */ - @SuppressWarnings("unchecked") @Override public List findElementsByIosUIAutomation(String using) throws WebDriverException { - return (List) findElements("-ios uiautomation", using); + return findElements("-ios uiautomation", using); } /** @@ -223,6 +218,6 @@ public List findElementsByIosUIAutomation(String using) * @param seconds number of seconds to lock the screen for */ public void lockDevice(int seconds) { - execute(LOCK, ImmutableMap.of("seconds", seconds)); + CommandExecutionHelper.execute(this, lockDeviceCommand(seconds)); } } diff --git a/src/main/java/io/appium/java_client/ios/IOSElement.java b/src/main/java/io/appium/java_client/ios/IOSElement.java index 2059a854a..723989b09 100644 --- a/src/main/java/io/appium/java_client/ios/IOSElement.java +++ b/src/main/java/io/appium/java_client/ios/IOSElement.java @@ -16,10 +16,7 @@ package io.appium.java_client.ios; -import com.google.common.collect.ImmutableMap; - import io.appium.java_client.FindsByIosUIAutomation; -import io.appium.java_client.MobileCommand; import io.appium.java_client.MobileElement; import org.openqa.selenium.WebDriverException; @@ -44,16 +41,4 @@ public class IOSElement extends MobileElement throws WebDriverException { return findElements("-ios uiautomation", using); } - - /** - * This method sets the new value of the attribute "value". - * - * @param value is the new value which should be set - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public void setValue(String value) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put("id", id).put("value", value); - execute(MobileCommand.SET_VALUE, builder.build()); - } } diff --git a/src/main/java/io/appium/java_client/ios/IOSMobileCommandHelper.java b/src/main/java/io/appium/java_client/ios/IOSMobileCommandHelper.java new file mode 100644 index 000000000..4de1bb8c7 --- /dev/null +++ b/src/main/java/io/appium/java_client/ios/IOSMobileCommandHelper.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.ios; + +import com.google.common.collect.ImmutableMap; + +import io.appium.java_client.MobileCommand; + +import java.util.AbstractMap; +import java.util.Map; + +public class IOSMobileCommandHelper extends MobileCommand { + + /** + * This method forms a {@link java.util.Map} of parameters for the + * keyboard hiding. + * + * @param keyName The button pressed by the mobile driver to attempt hiding the + * keyboard. + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> hideKeyboardCommand(String keyName) { + return new AbstractMap.SimpleEntry>(HIDE_KEYBOARD, prepareArguments("keyName", keyName)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * keyboard hiding. + * + * @param strategy HideKeyboardStrategy. + * @param keyName a String, representing the text displayed on the button of the + * keyboard you want to press. For example: "Done". + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> hideKeyboardCommand(String strategy, + String keyName) { + String[] parameters = new String[] {"strategy", "key"}; + Object[] values = new Object[] {strategy, keyName}; + return new AbstractMap.SimpleEntry>(HIDE_KEYBOARD, prepareArguments(parameters, values)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * device locking. + * + * @param seconds seconds number of seconds to lock the screen for + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> lockDeviceCommand(int seconds) { + return new AbstractMap.SimpleEntry>(LOCK, prepareArguments("seconds", seconds)); + } + + /** + * This method forms a {@link java.util.Map} of parameters for the + * device shaking. + * + * @return a key-value pair. The key is the command name. The value is a + * {@link java.util.Map} command arguments. + */ + public static Map.Entry> shakeCommand() { + return new AbstractMap.SimpleEntry>(SHAKE, ImmutableMap.of()); + } +} diff --git a/src/main/java/io/appium/java_client/remote/YouiEngineCapabilityType.java b/src/main/java/io/appium/java_client/remote/YouiEngineCapabilityType.java new file mode 100644 index 000000000..80301762d --- /dev/null +++ b/src/main/java/io/appium/java_client/remote/YouiEngineCapabilityType.java @@ -0,0 +1,13 @@ +package io.appium.java_client.remote; + +import org.openqa.selenium.remote.CapabilityType; + +/** + * The list of youiengine-specific capabilities. + */ +public interface YouiEngineCapabilityType extends CapabilityType { + /** + * IP address of the app to execute commands against. + */ + String APP_ADDRESS = "youiEngineAppAddress"; +} diff --git a/src/main/java/io/appium/java_client/youiengine/YouiEngineDriver.java b/src/main/java/io/appium/java_client/youiengine/YouiEngineDriver.java new file mode 100644 index 000000000..51d038a46 --- /dev/null +++ b/src/main/java/io/appium/java_client/youiengine/YouiEngineDriver.java @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.youiengine; + +import io.appium.java_client.AppiumDriver; +import io.appium.java_client.youiengine.internal.JsonToYouiEngineElementConverter; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.WebElement; + +import java.net.URL; + +public class YouiEngineDriver extends AppiumDriver { + + /** Constructor takes in the Appium Server URL and the capabilities you want to use for this + * test execution. **/ + public YouiEngineDriver(URL remoteAddress, Capabilities desiredCapabilities) { + super(remoteAddress, desiredCapabilities, JsonToYouiEngineElementConverter.class); + } + + @Override + public void swipe(int startx, int starty, int endx, int endy, int duration) { + super.doSwipe(startx, starty, endx, endy, duration); + } +} diff --git a/src/main/java/io/appium/java_client/youiengine/YouiEngineElement.java b/src/main/java/io/appium/java_client/youiengine/YouiEngineElement.java new file mode 100644 index 000000000..7e20df81b --- /dev/null +++ b/src/main/java/io/appium/java_client/youiengine/YouiEngineElement.java @@ -0,0 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.youiengine; + +import io.appium.java_client.MobileElement; + +public class YouiEngineElement extends MobileElement { + +} diff --git a/src/main/java/io/appium/java_client/youiengine/internal/JsonToYouiEngineElementConverter.java b/src/main/java/io/appium/java_client/youiengine/internal/JsonToYouiEngineElementConverter.java new file mode 100644 index 000000000..7aa09a52c --- /dev/null +++ b/src/main/java/io/appium/java_client/youiengine/internal/JsonToYouiEngineElementConverter.java @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * 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 io.appium.java_client.youiengine.internal; + +import io.appium.java_client.MobileElement; +import io.appium.java_client.internal.JsonToMobileElementConverter; +import io.appium.java_client.youiengine.YouiEngineElement; + +import org.openqa.selenium.remote.RemoteWebDriver; + + +public class JsonToYouiEngineElementConverter extends JsonToMobileElementConverter { + public JsonToYouiEngineElementConverter(RemoteWebDriver driver) { + super(driver); + } + + @Override + protected MobileElement newMobileElement() { + YouiEngineElement toReturn = new YouiEngineElement(); + toReturn.setParent(driver); + return toReturn; + } +} + diff --git a/src/test/java/io/appium/java_client/YouiEngineAppiumSample-debug.apk b/src/test/java/io/appium/java_client/YouiEngineAppiumSample-debug.apk new file mode 100644 index 000000000..c8f37de8e Binary files /dev/null and b/src/test/java/io/appium/java_client/YouiEngineAppiumSample-debug.apk differ diff --git a/src/test/java/io/appium/java_client/YouiEngineAppiumSample.app.zip b/src/test/java/io/appium/java_client/YouiEngineAppiumSample.app.zip new file mode 100644 index 000000000..d866cd38c Binary files /dev/null and b/src/test/java/io/appium/java_client/YouiEngineAppiumSample.app.zip differ diff --git a/src/test/java/io/appium/java_client/android/AndroidDriverTest.java b/src/test/java/io/appium/java_client/android/AndroidDriverTest.java index 0dfc88f6b..26f88bdfb 100644 --- a/src/test/java/io/appium/java_client/android/AndroidDriverTest.java +++ b/src/test/java/io/appium/java_client/android/AndroidDriverTest.java @@ -18,10 +18,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; - import io.appium.java_client.AppiumSetting; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; @@ -30,6 +30,7 @@ import org.openqa.selenium.html5.Location; import java.io.File; +import java.util.Map; public class AndroidDriverTest extends BaseAndroidTest { @@ -138,7 +139,12 @@ public class AndroidDriverTest extends BaseAndroidTest { } @Test public void getDeviceUDIDTest() { - String deviceSerial = driver.getSessionDetails().get("deviceUDID"); + String deviceSerial = driver.getSessionDetails().get("deviceUDID").toString(); assertNotNull(deviceSerial); } + + @Test public void getSessionMapData() { + Map map = (Map) driver.getSessionDetails().get("desired"); + assertNotEquals(map.size(), 0); + } } diff --git a/src/test/java/io/appium/java_client/android/AndroidElementTest.java b/src/test/java/io/appium/java_client/android/AndroidElementTest.java index 3dac6c520..00281537d 100644 --- a/src/test/java/io/appium/java_client/android/AndroidElementTest.java +++ b/src/test/java/io/appium/java_client/android/AndroidElementTest.java @@ -74,4 +74,14 @@ public class AndroidElementTest extends BaseAndroidTest { + "new UiSelector().text(\"Radio Group\"));")); assertNotNull(radioGroup.getLocation()); } + + @Test public void setValueTest() { + String value = "new value"; + + driver.startActivity("io.appium.android.apis", ".view.Controls1"); + AndroidElement editElement = driver + .findElementByAndroidUIAutomator("resourceId(\"io.appium.android.apis:id/edit\")"); + editElement.setValue(value); + assertEquals(value, editElement.getText()); + } } diff --git a/src/test/java/io/appium/java_client/events/AbilityToDefineListenersExternally.java b/src/test/java/io/appium/java_client/events/AbilityToDefineListenersExternally.java new file mode 100644 index 000000000..452b67c2f --- /dev/null +++ b/src/test/java/io/appium/java_client/events/AbilityToDefineListenersExternally.java @@ -0,0 +1,103 @@ +package io.appium.java_client.events; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.events.listeners.AlertListener2; +import io.appium.java_client.events.listeners.ContextListener2; +import io.appium.java_client.events.listeners.ElementListener2; +import io.appium.java_client.events.listeners.ExceptionListener2; +import io.appium.java_client.events.listeners.JavaScriptListener2; +import io.appium.java_client.events.listeners.NavigationListener2; +import io.appium.java_client.events.listeners.RotationListener2; +import io.appium.java_client.events.listeners.SearchingListener2; +import io.appium.java_client.events.listeners.WindowListener2; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AbilityToDefineListenersExternally extends BaseListenerTest { + + private static final String PREFIX = "Externally defined listener: "; + private static EmptyWebDriver emptyWebDriver; + private static SearchingListener2 searchingListener = new SearchingListener2(); + private static NavigationListener2 navigationListener = new NavigationListener2(); + private static ElementListener2 elementListener = new ElementListener2(); + private static JavaScriptListener2 javaScriptListener = new JavaScriptListener2(); + private static ExceptionListener2 exceptionListener = new ExceptionListener2(); + private static AlertListener2 alertListener = new AlertListener2(); + private static ContextListener2 contextListener = new ContextListener2(); + private static RotationListener2 rotationListener = new RotationListener2(); + private static WindowListener2 windowListener = new WindowListener2(); + + @BeforeClass public static void beforeClass() throws Exception { + emptyWebDriver = new EmptyWebDriver(); + emptyWebDriver = EventFiringWebDriverFactory.getEventFiringWebDriver(emptyWebDriver, + searchingListener, navigationListener, elementListener, javaScriptListener, + exceptionListener, alertListener, contextListener, rotationListener, windowListener); + } + + @Test + public void searchContextEventTest() { + assertThat(super.assertThatSearchListenerWorks(emptyWebDriver, searchingListener, PREFIX), + is(true)); + } + + @Test + public void searchContextEventTest2() { + assertThat( + super + .assertThatSearchListenerWorksAgainstElements(emptyWebDriver, searchingListener, PREFIX), + is(true)); + } + + @Test + public void navigationEventTest() throws Exception { + assertThat( + super + .assertThatNavigationListenerWorks(emptyWebDriver, navigationListener, PREFIX), + is(true)); + } + + @Test + public void elementEventTest() { + assertThat(super.assertThatElementListenerWorks(emptyWebDriver, elementListener, PREFIX), + is(true)); + } + + @Test + public void javaScriptEventTest() { + assertThat(super + .assertThatJavaScriptListenerWorks(emptyWebDriver, javaScriptListener, PREFIX), + is(true)); + } + + @Test + public void exceptionEventTest() { + assertThat(super.assertThatExceptionListenerWorks(emptyWebDriver, exceptionListener, PREFIX), + is(true)); + } + + @Test + public void alertEventTest() { + assertThat(super.assertThatAlertListenerWorks(emptyWebDriver, alertListener, PREFIX), + is(true)); + } + + @Test + public void contextEventListener() { + assertThat(super.assertThatConrextListenerWorks(emptyWebDriver, contextListener, PREFIX), + is(true)); + } + + @Test + public void rotationEventListener() { + assertThat(super.assertThatRotationListenerWorks(emptyWebDriver, rotationListener, PREFIX), + is(true)); + } + + @Test + public void windowEventListener() { + assertThat(super.assertThatWindowListenerWorks(emptyWebDriver, windowListener, PREFIX), + is(true)); + } +} diff --git a/src/test/java/io/appium/java_client/events/BaseListenerTest.java b/src/test/java/io/appium/java_client/events/BaseListenerTest.java new file mode 100644 index 000000000..99f11f139 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/BaseListenerTest.java @@ -0,0 +1,326 @@ +package io.appium.java_client.events; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.MobileBy; +import io.appium.java_client.events.listeners.TestListener; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.Point; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.security.Credentials; + +import java.net.URL; +import java.util.List; + +public class BaseListenerTest { + + protected boolean assertThatSearchListenerWorks(EmptyWebDriver driver, TestListener listener, + String prefix) { + try { + driver.findElement(By.id("someId")); + assertThat(listener.messages, + hasItems(prefix + "Attempt to find something using By.id: someId. The root element is null", + prefix + "The searching for something using By.id: someId has beed finished. " + + "The root element was null")); + + driver.findElements(By.id("someId2")); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to find something using By.id: someId. The root element is null", + prefix + "The searching for something using By.id: someId has beed finished. " + + "The root element was null", + prefix + "Attempt to find something using By.id: someId2. The root element is null", + prefix + "The searching for something using By.id: someId2 has beed finished. " + + "The root element was null")); + + driver.findElement(By.id("someId")).findElement(By.className("someClazz")); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to find something using By.id: someId. The root element is null", + prefix + "The searching for something using By.id: someId has beed finished. " + + "The root element was null", + prefix + "Attempt to find something using By.id: someId2. The root element is null", + prefix + "The searching for something using By.id: someId2 has beed finished. " + + "The root element was null", + prefix + "Attempt to find something using By.className: someClazz. The root element is " + + "io.appium.java_client.events.StubWebElement", + prefix + "The searching for something using By.className: someClazz has beed finished. " + + "The root element was " + + "io.appium.java_client.events.StubWebElement")); + + driver.findElements(By.id("someId2")).get(0).findElements(By.className("someClazz2")); + assertThat(listener.messages, + hasItems(prefix + "Attempt to find something using By.id: someId. The root element is null", + prefix + "The searching for something using By.id: someId has beed finished. " + + "The root element was null", + prefix + "Attempt to find something using By.id: someId2. The root element is null", + prefix + "The searching for something using By.id: someId2 has beed finished. " + + "The root element was null", + prefix + "Attempt to find something using By.className: someClazz. The root element is " + + "io.appium.java_client.events.StubWebElement", + prefix + "The searching for something using By.className: someClazz has beed finished. " + + "The root element was " + + "io.appium.java_client.events.StubWebElement", + prefix + "Attempt to find something using By.className: someClazz2. The root element is " + + "io.appium.java_client.events.StubWebElement", + prefix + "The searching for something using By.className: someClazz2 has beed finished. " + + "The root element was " + + "io.appium.java_client.events.StubWebElement")); + + assertThat(listener.messages.size(), is(12)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatSearchListenerWorksAgainstElements(EmptyWebDriver driver, TestListener listener, + String prefix) { + try { + List els = driver.findElementsByAccessibilityId("SomeAccessibility"); + StubWebElement e = driver.findElementByXPath("Some Path"); + + e.findElementByAccessibilityId("SomeAccessibility") + .findElement(MobileBy.AndroidUIAutomator("Android UI Automator")); + els.get(0).findElementByAccessibilityId("SomeAccessibility") + .findElement(MobileBy.IosUIAutomation("iOS UI Automation")); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to find something using By.AndroidUIAutomator: Android UI Automator. " + + "The root element is io.appium.java_client.events.StubWebElement", + prefix + "The searching for something using By.AndroidUIAutomator: " + + "Android UI Automator has " + + "beed finished. " + + "The root element was io.appium.java_client.events.StubWebElement", + prefix + "Attempt to find something using By.IosUIAutomation: iOS UI Automation. " + + "The root element is io.appium.java_client.events.StubWebElement", + prefix + "The searching for something using By.IosUIAutomation: iOS UI Automation " + + "has beed finished. " + + "The root element was io.appium.java_client.events.StubWebElement")); + assertThat(listener.messages.size(), is(4)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatNavigationListenerWorks(EmptyWebDriver driver, + TestListener listener, String prefix) throws Exception { + try { + driver.get("www.google.com"); + driver.navigate().to("www.google2.com"); + driver.navigate().to(new URL("https://www.google3.com")); + driver.navigate().forward(); + driver.navigate().back(); + driver.navigate().refresh(); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to navigate to www.google.com", + prefix + "Navigation to www.google.com was successful", + prefix + "Attempt to navigate to www.google2.com", + prefix + "Navigation to www.google2.com was successful", + prefix + "Attempt to navigate to https://www.google3.com", + prefix + "Navigation to https://www.google3.com was successful", + prefix + "Attempt to navigate forward", + prefix + "Navigation forward was successful", + prefix + "Attempt to navigate back", + prefix + "Navigation back was successful", + prefix + "Attempt to refresh", + prefix + "The refreshing was successful")); + assertThat(listener.messages.size(), is(12)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatElementListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + StubWebElement e = driver.findElementByXPath("Some Path"); + e.click(); + e.sendKeys("Test keys"); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to click on the element", + prefix + "Thee element was clicked", + prefix + "Attempt to change value of the element", + prefix + "The value of the element was changed")); + assertThat(listener.messages.size(), is(4)); + return true; + } finally { + listener.messages.clear(); + } + } + + + protected boolean assertThatJavaScriptListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + driver.executeScript("Some test script"); + driver.executeAsyncScript("Some test async script"); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to perform java script: Some test script", + prefix + "Java script Some test script was performed", + prefix + "Attempt to perform java script: Some test async script", + prefix + "Java script Some test async script was performed")); + assertThat(listener.messages.size(), is(4)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatExceptionListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + try { + driver.getWindowHandle(); + } catch (Exception ignored) { + ignored.printStackTrace(); + } + + try { + driver.findElementByXPath("Some Path").getScreenshotAs(OutputType.BASE64); + } catch (Exception ignored) { + ignored.printStackTrace(); + } + + assertThat(listener.messages, + hasItems(prefix + "The exception was thrown: " + + WebDriverException.class, + prefix + "The exception was thrown: " + + WebDriverException.class)); + assertThat(listener.messages.size(), is(2)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatAlertListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + Alert alert = driver.switchTo().alert(); + alert.accept(); + alert.dismiss(); + alert.sendKeys("Keys"); + Credentials credentials = new Credentials() { + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + return "Test credentials 1"; + } + }; + + Credentials credentials2 = new Credentials() { + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + return "Test credentials 2"; + } + }; + + alert.setCredentials(credentials); + alert.authenticateUsing(credentials2); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to accept alert", + prefix + "The alert was accepted", + prefix + "Attempt to dismiss alert", + prefix + "The alert was dismissed", + prefix + "Attempt to send keys to alert", + prefix + "Keys were sent to alert", + prefix + "Attempt to send credentials " + credentials.toString() + " to alert", + prefix + "Credentials " + credentials.toString() + " were sent to alert", + prefix + "Attempt to send credentials " + credentials2.toString() + " to alert", + prefix + "Credentials " + credentials2.toString() + " were sent to alert")); + assertThat(listener.messages.size(), is(10)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatConrextListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + driver.context("NATIVE_APP"); + driver.context("WEB_VIEW"); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to change current context to NATIVE_APP", + prefix + "The previous context has been changed to NATIVE_APP", + prefix + "Attempt to change current context to WEB_VIEW", + prefix + "The previous context has been changed to WEB_VIEW")); + assertThat(listener.messages.size(), is(4)); + return true; + } finally { + listener.messages.clear(); + } + } + + + protected boolean assertThatRotationListenerWorks(EmptyWebDriver driver, TestListener listener, + String prefix) { + try { + driver.rotate(ScreenOrientation.LANDSCAPE); + driver.rotate(ScreenOrientation.PORTRAIT); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to change screen orientation. The new screen orientation is " + + ScreenOrientation.LANDSCAPE.toString(), + prefix + "The screen orientation has been changed to " + + ScreenOrientation.LANDSCAPE.toString(), + prefix + "Attempt to change screen orientation. The new screen orientation is " + + ScreenOrientation.PORTRAIT.toString(), + prefix + "The screen orientation has been changed to " + + ScreenOrientation.PORTRAIT.toString())); + assertThat(listener.messages.size(), is(4)); + return true; + } finally { + listener.messages.clear(); + } + } + + protected boolean assertThatWindowListenerWorks(EmptyWebDriver driver, TestListener listener, String prefix) { + try { + WebDriver.Window window = driver.manage().window(); + Dimension d = new Dimension(500, 500); + window.setSize(d); + + Point p = new Point(50, 50); + window.setPosition(p); + + window.maximize(); + + assertThat(listener.messages, + hasItems(prefix + "Attempt to change size of the window. The height is " + d.getHeight() + + " the width is " + d.getWidth(), + prefix + "Size of the window has been changed. The height is " + d.getHeight() + + " the width is " + d.getWidth(), + prefix + "Attempt to change position of the window. The X is " + p.getX() + + " the Y is " + p.getY(), + prefix + "The position the window has been changed. The X is " + p.getX() + + " the Y is " + p.getY(), + prefix + "Attempt to maximize the window.", + prefix + "The window has been maximized")); + assertThat(listener.messages.size(), is(6)); + return true; + } finally { + listener.messages.clear(); + } + + } +} diff --git a/src/test/java/io/appium/java_client/events/DefaultEventListenerTest.java b/src/test/java/io/appium/java_client/events/DefaultEventListenerTest.java new file mode 100644 index 000000000..084b5918c --- /dev/null +++ b/src/test/java/io/appium/java_client/events/DefaultEventListenerTest.java @@ -0,0 +1,91 @@ +package io.appium.java_client.events; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.events.listeners.AlertListener; +import io.appium.java_client.events.listeners.ContextListener; +import io.appium.java_client.events.listeners.ElementListener; +import io.appium.java_client.events.listeners.ExceptionListener; +import io.appium.java_client.events.listeners.JavaScriptListener; +import io.appium.java_client.events.listeners.NavigationListener; +import io.appium.java_client.events.listeners.RotationListener; +import io.appium.java_client.events.listeners.SearchingListener; +import io.appium.java_client.events.listeners.SingleListeners; +import io.appium.java_client.events.listeners.WindowListener; +import org.apache.commons.lang3.StringUtils; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class DefaultEventListenerTest extends BaseListenerTest { + + private static EmptyWebDriver driver; + + @BeforeClass public static void beforeClass() throws Exception { + EmptyWebDriver emptyWebDriver = new EmptyWebDriver(); + driver = EventFiringWebDriverFactory.getEventFiringWebDriver(emptyWebDriver); + } + + @Test + public void searchContextEventTest() { + assertThat(super.assertThatSearchListenerWorks(driver, SingleListeners + .listeners.get(SearchingListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void searchContextEventTest2() { + assertThat(super.assertThatSearchListenerWorksAgainstElements(driver, SingleListeners + .listeners.get(SearchingListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void navigationEventTest() throws Exception { + assertThat(super.assertThatNavigationListenerWorks(driver, SingleListeners + .listeners.get(NavigationListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void elementEventTest() { + assertThat(super.assertThatElementListenerWorks(driver, SingleListeners + .listeners.get(ElementListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void javaScriptEventTest() { + assertThat(super.assertThatJavaScriptListenerWorks(driver, SingleListeners + .listeners.get(JavaScriptListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void exceptionEventTest() { + assertThat(super.assertThatExceptionListenerWorks(driver, SingleListeners + .listeners.get(ExceptionListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void alertEventTest() { + assertThat(super.assertThatAlertListenerWorks(driver, SingleListeners + .listeners.get(AlertListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void contextEventListener() { + assertThat(super.assertThatConrextListenerWorks(driver, SingleListeners + .listeners.get(ContextListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void rotationEventListener() { + assertThat(super.assertThatRotationListenerWorks(driver, SingleListeners + .listeners.get(RotationListener.class), StringUtils.EMPTY), is(true)); + } + + @Test + public void windowEventListener() { + assertThat(super.assertThatWindowListenerWorks(driver, SingleListeners + .listeners.get(WindowListener.class), StringUtils.EMPTY), is(true)); + } +} diff --git a/src/test/java/io/appium/java_client/events/EmptyWebDriver.java b/src/test/java/io/appium/java_client/events/EmptyWebDriver.java new file mode 100644 index 000000000..b37630400 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/EmptyWebDriver.java @@ -0,0 +1,307 @@ +package io.appium.java_client.events; + +import com.google.common.collect.ImmutableList; + +import io.appium.java_client.FindsByAccessibilityId; +import io.appium.java_client.FindsByAndroidUIAutomator; +import io.appium.java_client.FindsByIosUIAutomation; + +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.ContextAware; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Rotatable; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.FindsByClassName; +import org.openqa.selenium.internal.FindsByCssSelector; +import org.openqa.selenium.internal.FindsById; +import org.openqa.selenium.internal.FindsByLinkText; +import org.openqa.selenium.internal.FindsByTagName; +import org.openqa.selenium.internal.FindsByXPath; +import org.openqa.selenium.logging.Logs; + +import java.net.URL; +import java.util.List; +import java.util.Set; + +public class EmptyWebDriver implements WebDriver, ContextAware, Rotatable, FindsByClassName, + FindsByCssSelector, FindsById, FindsByLinkText, FindsByTagName, FindsByXPath, + FindsByAccessibilityId, FindsByAndroidUIAutomator, + FindsByIosUIAutomation, JavascriptExecutor { + + private static List createStubList() { + return ImmutableList.of(new StubWebElement(), new StubWebElement()); + } + + @Override public WebDriver context(String name) { + return null; + } + + @Override public Set getContextHandles() { + return null; + } + + @Override public String getContext() { + return StringUtils.EMPTY; + } + + @Override public void rotate(ScreenOrientation orientation) { + //The rotation does nothing there + } + + @Override public ScreenOrientation getOrientation() { + return null; + } + + @Override public void get(String url) { + //There is no navigation. It is added only for event firing + } + + @Override public String getCurrentUrl() { + return null; + } + + @Override public String getTitle() { + return null; + } + + @Override public List findElements(By by) { + return createStubList(); + } + + @Override public StubWebElement findElement(By by) { + return new StubWebElement(); + } + + @Override public String getPageSource() { + return null; + } + + @Override public void close() { + //There is no closing + } + + @Override public void quit() { + //It is only the stub + } + + @Override public Set getWindowHandles() { + return null; + } + + @Override public String getWindowHandle() { + throw new WebDriverException(); + } + + @Override public TargetLocator switchTo() { + return new StubTargetLocator(this); + } + + @Override public Navigation navigate() { + return new StubNavigation(); + } + + @Override public Options manage() { + return new StubOptions(); + } + + @Override public StubWebElement findElementByClassName(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByClassName(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByCssSelector(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByCssSelector(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementById(String using) { + return new StubWebElement(); + } + + @Override public List findElementsById(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByLinkText(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByLinkText(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByPartialLinkText(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByPartialLinkText(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByTagName(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByTagName(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByXPath(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByXPath(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByAccessibilityId(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByAccessibilityId(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByAndroidUIAutomator(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByAndroidUIAutomator(String using) { + return createStubList(); + } + + @Override public StubWebElement findElementByIosUIAutomation(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByIosUIAutomation(String using) { + return createStubList(); + } + + @Override public Object executeScript(String script, Object... args) { + return null; + } + + @Override public Object executeAsyncScript(String script, Object... args) { + return null; + } + + private class StubTargetLocator implements TargetLocator { + + private final WebDriver driver; + + StubTargetLocator(WebDriver driver) { + this.driver = driver; + } + + @Override public WebDriver frame(int index) { + return driver; + } + + @Override public WebDriver frame(String nameOrId) { + return driver; + } + + @Override public WebDriver frame(WebElement frameElement) { + return driver; + } + + @Override public WebDriver parentFrame() { + return driver; + } + + @Override public WebDriver window(String nameOrHandle) { + return driver; + } + + @Override public WebDriver defaultContent() { + return driver; + } + + @Override public WebElement activeElement() { + return new StubWebElement(); + } + + @Override public Alert alert() { + return new StubAlert(); + } + } + + private class StubOptions implements Options { + + @Override public void addCookie(Cookie cookie) { + //STUB: No adding cookie + } + + @Override public void deleteCookieNamed(String name) { + //STUB No removal cookie + } + + @Override public void deleteCookie(Cookie cookie) { + //STUB No deleting cookie + } + + @Override public void deleteAllCookies() { + //STUB it does nothing + } + + @Override public Set getCookies() { + return null; + } + + @Override public Cookie getCookieNamed(String name) { + return null; + } + + @Override public Timeouts timeouts() { + return null; + } + + @Override public ImeHandler ime() { + return null; + } + + @Override public Window window() { + return new StubWindow(); + } + + @Override public Logs logs() { + return null; + } + } + + private class StubNavigation implements Navigation { + + @Override public void back() { + //STUB: It doesn't navigate back + } + + @Override public void forward() { + //STUB: No the navigation forward + } + + @Override public void to(String url) { + //STUB: Added only for event firing + } + + @Override public void to(URL url) { + //STUB: Event firing of the navigation to some URL + } + + @Override public void refresh() { + //STUB: The firing of the refreshing + } + } +} diff --git a/src/test/java/io/appium/java_client/events/ExtendedEventListenerTest.java b/src/test/java/io/appium/java_client/events/ExtendedEventListenerTest.java new file mode 100644 index 000000000..fe3aace5d --- /dev/null +++ b/src/test/java/io/appium/java_client/events/ExtendedEventListenerTest.java @@ -0,0 +1,60 @@ +package io.appium.java_client.events; + +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.MobileBy; +import io.appium.java_client.android.AndroidElement; +import io.appium.java_client.events.listeners.ElementListener; +import io.appium.java_client.events.listeners.SearchingListener; +import io.appium.java_client.events.listeners.SingleListeners; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.By; + +public class ExtendedEventListenerTest { + + private static ExtendedWebDriver stubWebDriver; + + @BeforeClass public static void beforeClass() throws Exception { + stubWebDriver = new ExtendedWebDriver(); + stubWebDriver = EventFiringWebDriverFactory.getEventFiringWebDriver(stubWebDriver); + } + + @Test + public void replaceValueTest() { + AndroidElement androidElement = stubWebDriver.findElement(By.id("someId")); + androidElement.replaceValue("New value"); + + ElementListener listener = (ElementListener) SingleListeners + .listeners.get(ElementListener.class); + + assertThat(listener.messages, + hasItems( + "Attempt to change value of the element", + "The value of the element was changed")); + } + + @Test + public void searchingTest() { + AndroidElement androidElement = stubWebDriver.findElement(By.id("someId")); + androidElement.findElement("-some-criteria", "some value") + .findElements(MobileBy.AndroidUIAutomator("Android UI Automator")); + androidElement.findElements("-some-criteria2", "some value2").get(0) + .findElements(MobileBy.AndroidUIAutomator("Android UI Automator2")); + + SearchingListener listener = (SearchingListener) SingleListeners + .listeners.get(SearchingListener.class); + assertThat(listener.messages, + hasItems("Attempt to find something using By.AndroidUIAutomator: Android UI Automator. " + + "The root element is io.appium.java_client.events.StubAndroidElement", + "The searching for something using By.AndroidUIAutomator: Android UI Automator has " + + "beed finished. " + + "The root element was io.appium.java_client.events.StubAndroidElement", + "Attempt to find something using By.AndroidUIAutomator: Android UI Automator2. " + + "The root element is io.appium.java_client.events.StubAndroidElement", + "The searching for something using By.AndroidUIAutomator: Android UI Automator2 " + + "has beed finished. " + + "The root element was io.appium.java_client.events.StubAndroidElement")); + } +} diff --git a/src/test/java/io/appium/java_client/events/ExtendedWebDriver.java b/src/test/java/io/appium/java_client/events/ExtendedWebDriver.java new file mode 100644 index 000000000..08c2f1445 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/ExtendedWebDriver.java @@ -0,0 +1,65 @@ +package io.appium.java_client.events; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.List; +import java.util.Set; + +public class ExtendedWebDriver implements WebDriver { + + + + @Override public void get(String url) { + //STUB it does nothing + } + + @Override public String getCurrentUrl() { + return null; + } + + @Override public String getTitle() { + return null; + } + + @Override public List findElements(By by) { + return null; + } + + @Override public StubAndroidElement findElement(By by) { + return new StubAndroidElement(); + } + + @Override public String getPageSource() { + return null; + } + + @Override public void close() { + //STUB it does nothing + } + + @Override public void quit() { + //STUB it does nothing + } + + @Override public Set getWindowHandles() { + return null; + } + + @Override public String getWindowHandle() { + return null; + } + + @Override public TargetLocator switchTo() { + return null; + } + + @Override public Navigation navigate() { + return null; + } + + @Override public Options manage() { + return null; + } +} diff --git a/src/test/java/io/appium/java_client/events/FewInstancesTest.java b/src/test/java/io/appium/java_client/events/FewInstancesTest.java new file mode 100644 index 000000000..7eeb4fbdc --- /dev/null +++ b/src/test/java/io/appium/java_client/events/FewInstancesTest.java @@ -0,0 +1,184 @@ +package io.appium.java_client.events; + +import static com.thoughtworks.selenium.SeleneseTestCase.assertNotEquals; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.events.listeners.AlertListener; +import io.appium.java_client.events.listeners.ContextListener; +import io.appium.java_client.events.listeners.ElementListener; +import io.appium.java_client.events.listeners.ExceptionListener; +import io.appium.java_client.events.listeners.JavaScriptListener; +import io.appium.java_client.events.listeners.NavigationListener; +import io.appium.java_client.events.listeners.RotationListener; +import io.appium.java_client.events.listeners.SearchingListener; +import io.appium.java_client.events.listeners.SingleListeners; +import io.appium.java_client.events.listeners.WindowListener; +import org.apache.commons.lang3.StringUtils; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.WebDriver; + +public class FewInstancesTest extends BaseListenerTest { + + private static EmptyWebDriver emptyWebDriver1; + private static SearchingListener searchingListener1; + private static NavigationListener navigationListener1; + private static ElementListener elementListener1; + private static JavaScriptListener javaScriptListener1; + private static ExceptionListener exceptionListener1; + private static AlertListener alertListener1; + private static ContextListener contextListener1; + private static RotationListener rotationListener1; + private static WindowListener windowListener1; + + private static SearchingListener searchingListener2; + private static NavigationListener navigationListener2; + private static ElementListener elementListener2; + private static JavaScriptListener javaScriptListener2; + private static ExceptionListener exceptionListener2; + private static AlertListener alertListener2; + private static ContextListener contextListener2; + private static RotationListener rotationListener2; + private static WindowListener windowListener2; + + @BeforeClass public static void beforeClass() throws Exception { + emptyWebDriver1 = new EmptyWebDriver(); + emptyWebDriver1 = EventFiringWebDriverFactory.getEventFiringWebDriver(emptyWebDriver1); + + searchingListener1 = (SearchingListener) SingleListeners + .listeners.get(SearchingListener.class); + navigationListener1 = (NavigationListener) SingleListeners + .listeners.get(NavigationListener.class); + elementListener1 = (ElementListener) SingleListeners + .listeners.get(ElementListener.class); + javaScriptListener1 = (JavaScriptListener) SingleListeners + .listeners.get(JavaScriptListener.class); + exceptionListener1 = (ExceptionListener) SingleListeners + .listeners.get(ExceptionListener.class); + alertListener1 = (AlertListener) SingleListeners + .listeners.get(AlertListener.class); + contextListener1 = (ContextListener) SingleListeners + .listeners.get(ContextListener.class); + rotationListener1 = (RotationListener) SingleListeners + .listeners.get(RotationListener.class); + windowListener1 = + (WindowListener) SingleListeners.listeners.get(WindowListener.class); + + WebDriver stubWebDriver2 = new EmptyWebDriver(); + EventFiringWebDriverFactory.getEventFiringWebDriver(stubWebDriver2); + + searchingListener2 = (SearchingListener) SingleListeners + .listeners.get(SearchingListener.class); + navigationListener2 = (NavigationListener) SingleListeners + .listeners.get(NavigationListener.class); + elementListener2 = (ElementListener) SingleListeners + .listeners.get(ElementListener.class); + javaScriptListener2 = (JavaScriptListener) SingleListeners + .listeners.get(JavaScriptListener.class); + exceptionListener2 = (ExceptionListener) SingleListeners + .listeners.get(ExceptionListener.class); + alertListener2 = (AlertListener) SingleListeners + .listeners.get(AlertListener.class); + contextListener2 = (ContextListener) SingleListeners + .listeners.get(ContextListener.class); + rotationListener2 = (RotationListener) SingleListeners + .listeners.get(RotationListener.class); + windowListener2 = + (WindowListener) SingleListeners.listeners.get(WindowListener.class); + } + + @Test + public void listenersAreDifferent() { + assertNotEquals(searchingListener1, searchingListener2); + assertNotEquals(elementListener1, elementListener2); + assertNotEquals(navigationListener1, navigationListener2); + assertNotEquals(javaScriptListener1, javaScriptListener2); + assertNotEquals(exceptionListener1, exceptionListener2); + assertNotEquals(alertListener1, alertListener2); + assertNotEquals(contextListener1, contextListener2); + assertNotEquals(rotationListener1, rotationListener2); + assertNotEquals(windowListener1, windowListener2); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother() { + assertThat(super.assertThatSearchListenerWorks(emptyWebDriver1, + searchingListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second searching listener should have no messages", + searchingListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother2() { + assertThat(super.assertThatSearchListenerWorksAgainstElements(emptyWebDriver1, + searchingListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second searching listener should have no messages", + searchingListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother3() throws Exception { + assertThat(super.assertThatNavigationListenerWorks(emptyWebDriver1, + navigationListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second navigation listener should have no messages", + navigationListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother4() { + assertThat(super.assertThatJavaScriptListenerWorks(emptyWebDriver1, + javaScriptListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second java script listener should have no messages", + javaScriptListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother5() { + assertThat(super.assertThatExceptionListenerWorks(emptyWebDriver1, + exceptionListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second exception listener should have no messages", + exceptionListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother6() { + assertThat(super.assertThatAlertListenerWorks(emptyWebDriver1, + alertListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second alert listener should have no messages", + alertListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother7() { + assertThat(super.assertThatConrextListenerWorks(emptyWebDriver1, + contextListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second context listener should have no messages", + contextListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother8() { + assertThat(super.assertThatRotationListenerWorks(emptyWebDriver1, + rotationListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second rotation listener should have no messages", + rotationListener2.messages.size(), is(0)); + } + + @Test + public void assertThatOneDriverDoNotListensToAnother9() { + assertThat(super.assertThatWindowListenerWorks(emptyWebDriver1, + windowListener1, StringUtils.EMPTY), + is(true)); + assertThat("The second window listener should have no messages", + windowListener2.messages.size(), is(0)); + } +} diff --git a/src/test/java/io/appium/java_client/events/StubAlert.java b/src/test/java/io/appium/java_client/events/StubAlert.java new file mode 100644 index 000000000..b3b20fa42 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/StubAlert.java @@ -0,0 +1,31 @@ +package io.appium.java_client.events; + +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.Alert; +import org.openqa.selenium.security.Credentials; + +public class StubAlert implements Alert { + @Override public void dismiss() { + //STUB it does nothing + } + + @Override public void accept() { + //STUB it does nothing + } + + @Override public String getText() { + return StringUtils.EMPTY; + } + + @Override public void sendKeys(String keysToSend) { + //STUB it does nothing + } + + @Override public void setCredentials(Credentials credentials) { + //STUB it does nothing + } + + @Override public void authenticateUsing(Credentials credentials) { + //STUB it does nothing + } +} diff --git a/src/test/java/io/appium/java_client/events/StubAndroidElement.java b/src/test/java/io/appium/java_client/events/StubAndroidElement.java new file mode 100644 index 000000000..b161f0a18 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/StubAndroidElement.java @@ -0,0 +1,28 @@ +package io.appium.java_client.events; + +import io.appium.java_client.MobileElement; +import io.appium.java_client.android.AndroidElement; + +import java.util.ArrayList; +import java.util.List; + +public class StubAndroidElement extends AndroidElement { + @Override public String toString() { + return this.getClass().getCanonicalName(); + } + + @Override public MobileElement findElement(String by, String using) { + return new StubAndroidElement(); + } + + @Override public List findElements(String by, String using) { + List result = new ArrayList<>(); + result.add(new StubAndroidElement()); + result.add(new StubAndroidElement()); + return result; + } + + public void replaceValue(String value) { + //STUB it does nothing + } +} diff --git a/src/test/java/io/appium/java_client/events/StubWebElement.java b/src/test/java/io/appium/java_client/events/StubWebElement.java new file mode 100644 index 000000000..affc116a9 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/StubWebElement.java @@ -0,0 +1,187 @@ +package io.appium.java_client.events; + +import com.google.common.collect.ImmutableList; + +import io.appium.java_client.FindsByAccessibilityId; +import io.appium.java_client.FindsByAndroidUIAutomator; +import io.appium.java_client.FindsByIosUIAutomation; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.Point; +import org.openqa.selenium.Rectangle; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.FindsByClassName; +import org.openqa.selenium.internal.FindsByCssSelector; +import org.openqa.selenium.internal.FindsById; +import org.openqa.selenium.internal.FindsByLinkText; +import org.openqa.selenium.internal.FindsByTagName; +import org.openqa.selenium.internal.FindsByXPath; + +import java.util.ArrayList; +import java.util.List; + +public class StubWebElement implements WebElement, FindsByClassName, FindsByCssSelector, FindsById, + FindsByLinkText, FindsByTagName, FindsByXPath, FindsByAccessibilityId, + FindsByAndroidUIAutomator, FindsByIosUIAutomation { + + private static List createStubSubElementList() { + List result = new ArrayList<>(); + result.addAll(ImmutableList.of(new StubWebElement(), new StubWebElement())); + return result; + } + + + @Override public WebElement findElementByAccessibilityId(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByAccessibilityId(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByAndroidUIAutomator(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByAndroidUIAutomator(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByClassName(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByClassName(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByCssSelector(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByCssSelector(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementById(String using) { + return new StubWebElement(); + } + + @Override public List findElementsById(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByIosUIAutomation(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByIosUIAutomation(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByLinkText(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByLinkText(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByPartialLinkText(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByPartialLinkText(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByTagName(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByTagName(String using) { + return createStubSubElementList(); + } + + @Override public WebElement findElementByXPath(String using) { + return new StubWebElement(); + } + + @Override public List findElementsByXPath(String using) { + return createStubSubElementList(); + } + + @Override public void click() { + //There is no clicking. It is STUB. + } + + @Override public void submit() { + //No submitting + } + + @Override public void sendKeys(CharSequence... keysToSend) { + //There is no the sending keys. + } + + @Override public void clear() { + //It doesn't clearing anything. + } + + @Override public String getTagName() { + return null; + } + + @Override public String getAttribute(String name) { + return null; + } + + @Override public boolean isSelected() { + return false; + } + + @Override public boolean isEnabled() { + return false; + } + + @Override public String getText() { + return null; + } + + @Override public List findElements(By by) { + return createStubSubElementList(); + } + + @Override public WebElement findElement(By by) { + return new StubWebElement(); + } + + @Override public boolean isDisplayed() { + return false; + } + + @Override public Point getLocation() { + return null; + } + + @Override public Dimension getSize() { + return null; + } + + @Override public Rectangle getRect() { + return null; + } + + @Override public String getCssValue(String propertyName) { + return null; + } + + @Override public X getScreenshotAs(OutputType target) throws WebDriverException { + throw new WebDriverException(); + } + + @Override public String toString() { + return this.getClass().getCanonicalName(); + } +} diff --git a/src/test/java/io/appium/java_client/events/StubWindow.java b/src/test/java/io/appium/java_client/events/StubWindow.java new file mode 100644 index 000000000..066f2d08f --- /dev/null +++ b/src/test/java/io/appium/java_client/events/StubWindow.java @@ -0,0 +1,31 @@ +package io.appium.java_client.events; + +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebDriver; + +public class StubWindow implements WebDriver.Window { + @Override public void setSize(Dimension targetSize) { + //STUB it does nothing + } + + @Override public void setPosition(Point targetPosition) { + //STUB it does nothing + } + + @Override public Dimension getSize() { + return null; + } + + @Override public Point getPosition() { + return null; + } + + @Override public void maximize() { + //STUB it does nothing + } + + @Override public void fullscreen() { + //STUB it does nothing + } +} diff --git a/src/test/java/io/appium/java_client/events/WebDriverEventListenerCompatibilityTest.java b/src/test/java/io/appium/java_client/events/WebDriverEventListenerCompatibilityTest.java new file mode 100644 index 000000000..4358566f2 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/WebDriverEventListenerCompatibilityTest.java @@ -0,0 +1,64 @@ +package io.appium.java_client.events; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import io.appium.java_client.events.listeners.AppiumListener; +import io.appium.java_client.events.listeners.SingleListeners; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class WebDriverEventListenerCompatibilityTest extends BaseListenerTest { + + private static EmptyWebDriver driver; + private static AppiumListener listener; + private static final String WEBDRIVER_EVENT_LISTENER = "WebDriverEventListener: "; + + @BeforeClass public static void beforeClass() throws Exception { + EmptyWebDriver emptyWebDriver = new EmptyWebDriver(); + driver = EventFiringWebDriverFactory.getEventFiringWebDriver(emptyWebDriver); + listener = (AppiumListener) SingleListeners.listeners.get(AppiumListener.class); + } + + @Test + public void searchContextEventTest() { + assertThat(super.assertThatSearchListenerWorks(driver, listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } + + @Test + public void searchContextEventTest2() { + assertThat(super.assertThatSearchListenerWorksAgainstElements(driver, + listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } + + @Test + public void navigationEventTest() throws Exception { + assertThat(super.assertThatNavigationListenerWorks(driver, + listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } + + @Test + public void elementEventTest() { + assertThat(super.assertThatElementListenerWorks(driver, listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } + + @Test + public void javaScriptEventTest() { + assertThat(super.assertThatJavaScriptListenerWorks(driver, + listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } + + @Test + public void exceptionEventTest() { + assertThat(super.assertThatExceptionListenerWorks(driver, listener, WEBDRIVER_EVENT_LISTENER), + is(true)); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/AlertListener.java b/src/test/java/io/appium/java_client/events/listeners/AlertListener.java new file mode 100644 index 000000000..3fc61782d --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/AlertListener.java @@ -0,0 +1,46 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.AlertEventListener; +import org.openqa.selenium.Alert; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.security.Credentials; + +public class AlertListener extends TestListener implements AlertEventListener { + @Override public void beforeAlertAccept(WebDriver driver, Alert alert) { + messages.add("Attempt to accept alert"); + } + + @Override public void afterAlertAccept(WebDriver driver, Alert alert) { + messages.add("The alert was accepted"); + } + + @Override public void afterAlertDismiss(WebDriver driver, Alert alert) { + messages.add("The alert was dismissed"); + } + + @Override public void beforeAlertDismiss(WebDriver driver, Alert alert) { + messages.add("Attempt to dismiss alert"); + } + + @Override public void beforeAlertSendKeys(WebDriver driver, Alert alert, String keys) { + messages.add("Attempt to send keys to alert"); + } + + @Override public void afterAlertSendKeys(WebDriver driver, Alert alert, String keys) { + messages.add("Keys were sent to alert"); + } + + @Override + public void beforeAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + messages.add("Attempt to send credentials " + credentials.toString() + " to alert"); + } + + @Override + public void afterAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + messages.add("Credentials " + credentials.toString() + " were sent to alert"); + } + + @Override protected void add() { + SingleListeners.listeners.put(AlertListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/AlertListener2.java b/src/test/java/io/appium/java_client/events/listeners/AlertListener2.java new file mode 100644 index 000000000..abeff2d80 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/AlertListener2.java @@ -0,0 +1,48 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.AlertEventListener; +import org.openqa.selenium.Alert; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.security.Credentials; + +public class AlertListener2 extends TestListener implements AlertEventListener { + @Override public void beforeAlertAccept(WebDriver driver, Alert alert) { + messages.add("Externally defined listener: Attempt to accept alert"); + } + + @Override public void afterAlertAccept(WebDriver driver, Alert alert) { + messages.add("Externally defined listener: The alert was accepted"); + } + + @Override public void afterAlertDismiss(WebDriver driver, Alert alert) { + messages.add("Externally defined listener: The alert was dismissed"); + } + + @Override public void beforeAlertDismiss(WebDriver driver, Alert alert) { + messages.add("Externally defined listener: Attempt to dismiss alert"); + } + + @Override public void beforeAlertSendKeys(WebDriver driver, Alert alert, String keys) { + messages.add("Externally defined listener: Attempt to send keys to alert"); + } + + @Override public void afterAlertSendKeys(WebDriver driver, Alert alert, String keys) { + messages.add("Externally defined listener: Keys were sent to alert"); + } + + @Override + public void beforeAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + messages.add("Externally defined listener: Attempt to send credentials " + + credentials.toString() + " to alert"); + } + + @Override + public void afterAuthentication(WebDriver driver, Alert alert, Credentials credentials) { + messages.add("Externally defined listener: Credentials " + credentials.toString() + + " were sent to alert"); + } + + @Override protected void add() { + SingleListeners.listeners.put(AlertListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/AppiumListener.java b/src/test/java/io/appium/java_client/events/listeners/AppiumListener.java new file mode 100644 index 000000000..cd3f06c77 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/AppiumListener.java @@ -0,0 +1,85 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.AppiumWebDriverEventListener; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class AppiumListener extends TestListener implements AppiumWebDriverEventListener { + @Override protected void add() { + SingleListeners.listeners.put(AppiumListener.class, this); + } + + @Override public void beforeNavigateTo(String url, WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to navigate to " + url); + } + + @Override public void afterNavigateTo(String url, WebDriver driver) { + messages.add("WebDriverEventListener: Navigation to " + url + " was successful"); + } + + @Override public void beforeNavigateBack(WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to navigate back"); + } + + @Override public void afterNavigateBack(WebDriver driver) { + messages.add("WebDriverEventListener: Navigation back was successful"); + } + + @Override public void beforeNavigateForward(WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to navigate forward"); + } + + @Override public void afterNavigateForward(WebDriver driver) { + messages.add("WebDriverEventListener: Navigation forward was successful"); + } + + @Override public void beforeNavigateRefresh(WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to refresh"); + } + + @Override public void afterNavigateRefresh(WebDriver driver) { + messages.add("WebDriverEventListener: The refreshing was successful"); + } + + @Override public void beforeFindBy(By by, WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to find something using " + by.toString() + + ". The root element is " + + String.valueOf(element)); + } + + @Override public void afterFindBy(By by, WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: The searching for something using " + + by.toString() + " has beed finished. " + + "The root element was " + + String.valueOf(element)); + } + + @Override public void beforeClickOn(WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to click on the element"); + } + + @Override public void afterClickOn(WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: Thee element was clicked"); + } + + @Override public void beforeChangeValueOf(WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to change value of the element"); + } + + @Override public void afterChangeValueOf(WebElement element, WebDriver driver) { + messages.add("WebDriverEventListener: The value of the element was changed"); + } + + @Override public void beforeScript(String script, WebDriver driver) { + messages.add("WebDriverEventListener: Attempt to perform java script: " + script); + } + + @Override public void afterScript(String script, WebDriver driver) { + messages.add("WebDriverEventListener: Java script " + script + " was performed"); + } + + @Override public void onException(Throwable throwable, WebDriver driver) { + messages.add("WebDriverEventListener: The exception was thrown: " + throwable.getClass()); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ContextListener.java b/src/test/java/io/appium/java_client/events/listeners/ContextListener.java new file mode 100644 index 000000000..5b97d1dd4 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ContextListener.java @@ -0,0 +1,18 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.mobile.ContextEventListener; +import org.openqa.selenium.WebDriver; + +public class ContextListener extends TestListener implements ContextEventListener { + @Override public void beforeSwitchingToContext(WebDriver driver, String context) { + messages.add("Attempt to change current context to " + context); + } + + @Override public void afterSwitchingToContext(WebDriver driver, String context) { + messages.add("The previous context has been changed to " + context); + } + + @Override protected void add() { + SingleListeners.listeners.put(ContextListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ContextListener2.java b/src/test/java/io/appium/java_client/events/listeners/ContextListener2.java new file mode 100644 index 000000000..979c19a1a --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ContextListener2.java @@ -0,0 +1,18 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.mobile.ContextEventListener; +import org.openqa.selenium.WebDriver; + +public class ContextListener2 extends TestListener implements ContextEventListener { + @Override public void beforeSwitchingToContext(WebDriver driver, String context) { + messages.add("Externally defined listener: Attempt to change current context to " + context); + } + + @Override public void afterSwitchingToContext(WebDriver driver, String context) { + messages.add("Externally defined listener: The previous context has been changed to " + context); + } + + @Override protected void add() { + SingleListeners.listeners.put(ContextListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ElementListener.java b/src/test/java/io/appium/java_client/events/listeners/ElementListener.java new file mode 100644 index 000000000..8b9d50ec3 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ElementListener.java @@ -0,0 +1,28 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.ElementEventListener; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class ElementListener extends TestListener implements ElementEventListener { + + @Override public void beforeClickOn(WebElement element, WebDriver driver) { + messages.add("Attempt to click on the element"); + } + + @Override public void afterClickOn(WebElement element, WebDriver driver) { + messages.add("Thee element was clicked"); + } + + @Override public void beforeChangeValueOf(WebElement element, WebDriver driver) { + messages.add("Attempt to change value of the element"); + } + + @Override public void afterChangeValueOf(WebElement element, WebDriver driver) { + messages.add("The value of the element was changed"); + } + + @Override protected void add() { + SingleListeners.listeners.put(ElementListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ElementListener2.java b/src/test/java/io/appium/java_client/events/listeners/ElementListener2.java new file mode 100644 index 000000000..8add3bf69 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ElementListener2.java @@ -0,0 +1,28 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.ElementEventListener; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class ElementListener2 extends TestListener implements ElementEventListener { + + @Override public void beforeClickOn(WebElement element, WebDriver driver) { + messages.add("Externally defined listener: Attempt to click on the element"); + } + + @Override public void afterClickOn(WebElement element, WebDriver driver) { + messages.add("Externally defined listener: Thee element was clicked"); + } + + @Override public void beforeChangeValueOf(WebElement element, WebDriver driver) { + messages.add("Externally defined listener: Attempt to change value of the element"); + } + + @Override public void afterChangeValueOf(WebElement element, WebDriver driver) { + messages.add("Externally defined listener: The value of the element was changed"); + } + + @Override protected void add() { + SingleListeners.listeners.put(ElementListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ExceptionListener.java b/src/test/java/io/appium/java_client/events/listeners/ExceptionListener.java new file mode 100644 index 000000000..837bcce1f --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ExceptionListener.java @@ -0,0 +1,14 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.ListensToException; +import org.openqa.selenium.WebDriver; + +public class ExceptionListener extends TestListener implements ListensToException { + @Override public void onException(Throwable throwable, WebDriver driver) { + messages.add("The exception was thrown: " + throwable.getClass()); + } + + @Override protected void add() { + SingleListeners.listeners.put(ExceptionListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/ExceptionListener2.java b/src/test/java/io/appium/java_client/events/listeners/ExceptionListener2.java new file mode 100644 index 000000000..194cad08c --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/ExceptionListener2.java @@ -0,0 +1,14 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.ListensToException; +import org.openqa.selenium.WebDriver; + +public class ExceptionListener2 extends TestListener implements ListensToException { + @Override public void onException(Throwable throwable, WebDriver driver) { + messages.add("Externally defined listener: The exception was thrown: " + throwable.getClass()); + } + + @Override protected void add() { + SingleListeners.listeners.put(ExceptionListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener.java b/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener.java new file mode 100644 index 000000000..09b7bad6e --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener.java @@ -0,0 +1,18 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.JavaScriptEventListener; +import org.openqa.selenium.WebDriver; + +public class JavaScriptListener extends TestListener implements JavaScriptEventListener { + @Override public void beforeScript(String script, WebDriver driver) { + messages.add("Attempt to perform java script: " + script); + } + + @Override public void afterScript(String script, WebDriver driver) { + messages.add("Java script " + script + " was performed"); + } + + @Override protected void add() { + SingleListeners.listeners.put(JavaScriptListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener2.java b/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener2.java new file mode 100644 index 000000000..2ec869751 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/JavaScriptListener2.java @@ -0,0 +1,18 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.JavaScriptEventListener; +import org.openqa.selenium.WebDriver; + +public class JavaScriptListener2 extends TestListener implements JavaScriptEventListener { + @Override public void beforeScript(String script, WebDriver driver) { + messages.add("Externally defined listener: Attempt to perform java script: " + script); + } + + @Override public void afterScript(String script, WebDriver driver) { + messages.add("Externally defined listener: Java script " + script + " was performed"); + } + + @Override protected void add() { + SingleListeners.listeners.put(JavaScriptListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/NavigationListener.java b/src/test/java/io/appium/java_client/events/listeners/NavigationListener.java new file mode 100644 index 000000000..ac9cd94f0 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/NavigationListener.java @@ -0,0 +1,43 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.NavigationEventListener; +import org.openqa.selenium.WebDriver; + +public class NavigationListener extends TestListener implements NavigationEventListener { + + @Override public void beforeNavigateTo(String url, WebDriver driver) { + messages.add("Attempt to navigate to " + url); + } + + @Override public void afterNavigateTo(String url, WebDriver driver) { + messages.add("Navigation to " + url + " was successful"); + } + + @Override public void beforeNavigateBack(WebDriver driver) { + messages.add("Attempt to navigate back"); + } + + @Override public void afterNavigateBack(WebDriver driver) { + messages.add("Navigation back was successful"); + } + + @Override public void beforeNavigateForward(WebDriver driver) { + messages.add("Attempt to navigate forward"); + } + + @Override public void afterNavigateForward(WebDriver driver) { + messages.add("Navigation forward was successful"); + } + + @Override public void beforeNavigateRefresh(WebDriver driver) { + messages.add("Attempt to refresh"); + } + + @Override public void afterNavigateRefresh(WebDriver driver) { + messages.add("The refreshing was successful"); + } + + @Override protected void add() { + SingleListeners.listeners.put(NavigationListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/NavigationListener2.java b/src/test/java/io/appium/java_client/events/listeners/NavigationListener2.java new file mode 100644 index 000000000..327f05bca --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/NavigationListener2.java @@ -0,0 +1,43 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.NavigationEventListener; +import org.openqa.selenium.WebDriver; + +public class NavigationListener2 extends TestListener implements NavigationEventListener { + + @Override public void beforeNavigateTo(String url, WebDriver driver) { + messages.add("Externally defined listener: Attempt to navigate to " + url); + } + + @Override public void afterNavigateTo(String url, WebDriver driver) { + messages.add("Externally defined listener: Navigation to " + url + " was successful"); + } + + @Override public void beforeNavigateBack(WebDriver driver) { + messages.add("Externally defined listener: Attempt to navigate back"); + } + + @Override public void afterNavigateBack(WebDriver driver) { + messages.add("Externally defined listener: Navigation back was successful"); + } + + @Override public void beforeNavigateForward(WebDriver driver) { + messages.add("Externally defined listener: Attempt to navigate forward"); + } + + @Override public void afterNavigateForward(WebDriver driver) { + messages.add("Externally defined listener: Navigation forward was successful"); + } + + @Override public void beforeNavigateRefresh(WebDriver driver) { + messages.add("Externally defined listener: Attempt to refresh"); + } + + @Override public void afterNavigateRefresh(WebDriver driver) { + messages.add("Externally defined listener: The refreshing was successful"); + } + + @Override protected void add() { + SingleListeners.listeners.put(NavigationListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/RotationListener.java b/src/test/java/io/appium/java_client/events/listeners/RotationListener.java new file mode 100644 index 000000000..e98d274cc --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/RotationListener.java @@ -0,0 +1,22 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.mobile.RotationEventListener; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; + +public class RotationListener extends TestListener implements RotationEventListener { + + @Override public void beforeRotation(WebDriver driver, ScreenOrientation orientation) { + messages.add("Attempt to change screen orientation. The new screen orientation is " + + orientation.toString()); + } + + @Override public void afterRotation(WebDriver driver, ScreenOrientation orientation) { + messages.add("The screen orientation has been changed to " + + orientation.toString()); + } + + @Override protected void add() { + SingleListeners.listeners.put(RotationListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/RotationListener2.java b/src/test/java/io/appium/java_client/events/listeners/RotationListener2.java new file mode 100644 index 000000000..9e7cd27ec --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/RotationListener2.java @@ -0,0 +1,23 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.mobile.RotationEventListener; +import org.openqa.selenium.ScreenOrientation; +import org.openqa.selenium.WebDriver; + +public class RotationListener2 extends TestListener implements RotationEventListener { + + @Override public void beforeRotation(WebDriver driver, ScreenOrientation orientation) { + messages.add("Externally defined listener: Attempt to change screen orientation. " + + "The new screen orientation is " + + orientation.toString()); + } + + @Override public void afterRotation(WebDriver driver, ScreenOrientation orientation) { + messages.add("Externally defined listener: The screen orientation has been changed to " + + orientation.toString()); + } + + @Override protected void add() { + SingleListeners.listeners.put(RotationListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/SearchingListener.java b/src/test/java/io/appium/java_client/events/listeners/SearchingListener.java new file mode 100644 index 000000000..22e46257a --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/SearchingListener.java @@ -0,0 +1,24 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.SearchingEventListener; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class SearchingListener extends TestListener implements SearchingEventListener { + + @Override public void beforeFindBy(By by, WebElement element, WebDriver driver) { + messages.add("Attempt to find something using " + by.toString() + ". The root element is " + + String.valueOf(element)); + } + + @Override public void afterFindBy(By by, WebElement element, WebDriver driver) { + messages.add("The searching for something using " + by.toString() + " has beed finished. " + + "The root element was " + + String.valueOf(element)); + } + + @Override protected void add() { + SingleListeners.listeners.put(SearchingListener.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/SearchingListener2.java b/src/test/java/io/appium/java_client/events/listeners/SearchingListener2.java new file mode 100644 index 000000000..bc3459a4d --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/SearchingListener2.java @@ -0,0 +1,26 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.SearchingEventListener; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class SearchingListener2 extends TestListener implements SearchingEventListener { + + @Override public void beforeFindBy(By by, WebElement element, WebDriver driver) { + messages.add("Externally defined listener: Attempt to find something using " + + by.toString() + ". The root element is " + + String.valueOf(element)); + } + + @Override public void afterFindBy(By by, WebElement element, WebDriver driver) { + messages.add("Externally defined listener: The searching for something using " + + by.toString() + " has beed finished. " + + "The root element was " + + String.valueOf(element)); + } + + @Override protected void add() { + SingleListeners.listeners.put(SearchingListener2.class, this); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/SingleListeners.java b/src/test/java/io/appium/java_client/events/listeners/SingleListeners.java new file mode 100644 index 000000000..a61f12e56 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/SingleListeners.java @@ -0,0 +1,12 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.Listener; + +import java.util.HashMap; +import java.util.Map; + +public class SingleListeners { + + public static final Map, TestListener> listeners = new HashMap<>(); + +} diff --git a/src/test/java/io/appium/java_client/events/listeners/TestListener.java b/src/test/java/io/appium/java_client/events/listeners/TestListener.java new file mode 100644 index 000000000..21c8e9419 --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/TestListener.java @@ -0,0 +1,17 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.Listener; + +import java.util.ArrayList; +import java.util.List; + +public abstract class TestListener implements Listener { + + public final List messages = new ArrayList<>(); + + public TestListener() { + add(); + } + + protected abstract void add(); +} diff --git a/src/test/java/io/appium/java_client/events/listeners/WindowListener.java b/src/test/java/io/appium/java_client/events/listeners/WindowListener.java new file mode 100644 index 000000000..193e5b5fd --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/WindowListener.java @@ -0,0 +1,45 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.WindowEventListener; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebDriver; + +public class WindowListener extends TestListener implements WindowEventListener { + + @Override protected void add() { + SingleListeners.listeners.put(WindowListener.class, this); + } + + @Override public void beforeWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + messages.add("Attempt to change size of the window. The height is " + targetSize.getHeight() + + " the width is " + targetSize.getWidth()); + } + + @Override public void afterWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + messages.add("Size of the window has been changed. The height is " + targetSize.getHeight() + + " the width is " + targetSize.getWidth()); + } + + @Override + public void beforeWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + messages.add("Attempt to change position of the window. The X is " + targetPoint.getX() + + " the Y is " + targetPoint.getY()); + } + + @Override + public void afterWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + messages.add("The position the window has been changed. The X is " + targetPoint.getX() + + " the Y is " + targetPoint.getY()); + } + + @Override public void beforeWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + messages.add("Attempt to maximize the window."); + } + + @Override public void afterWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + messages.add("The window has been maximized"); + } +} diff --git a/src/test/java/io/appium/java_client/events/listeners/WindowListener2.java b/src/test/java/io/appium/java_client/events/listeners/WindowListener2.java new file mode 100644 index 000000000..3f0c802fd --- /dev/null +++ b/src/test/java/io/appium/java_client/events/listeners/WindowListener2.java @@ -0,0 +1,49 @@ +package io.appium.java_client.events.listeners; + +import io.appium.java_client.events.api.general.WindowEventListener; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebDriver; + +public class WindowListener2 extends TestListener implements WindowEventListener { + + @Override protected void add() { + SingleListeners.listeners.put(WindowListener2.class, this); + } + + @Override public void beforeWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + messages.add("Externally defined listener: Attempt to change size of the window. " + + "The height is " + targetSize.getHeight() + + " the width is " + targetSize.getWidth()); + } + + @Override public void afterWindowChangeSize(WebDriver driver, WebDriver.Window window, + Dimension targetSize) { + messages.add("Externally defined listener: Size of the window has " + + "been changed. The height is " + targetSize.getHeight() + + " the width is " + targetSize.getWidth()); + } + + @Override + public void beforeWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + messages.add("Externally defined listener: Attempt to change position of the window. " + + "The X is " + targetPoint.getX() + + " the Y is " + targetPoint.getY()); + } + + @Override + public void afterWindowIsMoved(WebDriver driver, WebDriver.Window window, Point targetPoint) { + messages.add("Externally defined listener: The position the window has been changed. " + + "The X is " + targetPoint.getX() + + " the Y is " + targetPoint.getY()); + } + + @Override public void beforeWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + messages.add("Externally defined listener: Attempt to maximize the window."); + } + + @Override public void afterWindowIsMaximized(WebDriver driver, WebDriver.Window window) { + messages.add("Externally defined listener: The window has been maximized"); + } +} diff --git a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java index 5cac8c33a..dc790164b 100644 --- a/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java +++ b/src/test/java/io/appium/java_client/pagefactory_tests/widgets/ios/simple/IOSMovie.java @@ -1,10 +1,10 @@ package io.appium.java_client.pagefactory_tests.widgets.ios.simple; +import io.appium.java_client.TouchableElement; import io.appium.java_client.ios.IOSElement; import io.appium.java_client.pagefactory.iOSFindBy; import io.appium.java_client.pagefactory_tests.widgets.Movie; import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebElement; import java.util.List; @@ -25,10 +25,10 @@ protected IOSMovie(WebElement element) { } @Override public Object getPoster() { - return ((RemoteWebElement) getWrappedElement()).getSize(); + return getWrappedElement().getSize(); } @Override public void goToReview() { - ((IOSElement) getWrappedElement()).tap(1, 1500); + ((TouchableElement) getWrappedElement()).tap(1, 1500); } } diff --git a/src/test/java/io/appium/java_client/youiengine/SanityTest.java b/src/test/java/io/appium/java_client/youiengine/SanityTest.java new file mode 100644 index 000000000..830368aff --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/SanityTest.java @@ -0,0 +1,902 @@ +package io.appium.java_client.youiengine; + +import io.appium.java_client.CommandExecutionHelper; +import io.appium.java_client.android.Connection; +import io.appium.java_client.youiengine.util.BaseYouiEngineTest; +import io.appium.java_client.youiengine.util.TestUtility; +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.openqa.selenium.*; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.remote.Response; +import org.openqa.selenium.remote.SessionId; + +import java.io.File; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static io.appium.java_client.android.AndroidKeyCode.KEYCODE_H; +import static io.appium.java_client.android.AndroidKeyCode.KEYCODE_I; +import static io.appium.java_client.android.AndroidMobileCommandHelper.*; +import static io.appium.java_client.ios.IOSMobileCommandHelper.shakeCommand; +import static org.hamcrest.CoreMatchers.not; + +/** + * This test class performs a simple series of tests to confirm our implementation in the + * youiengine driver and in this java_client. This test class uses the included + * YouiEngineAppiumSample app as a target for these tests. The intent of each test can be found + * before each of the test methods. + * + *

This test uses the model provided in the Appium java_client test tutorial. + * + *

Uncompress the iOS YouiEngineAppiumSample.app.zip before using this with iOS as a target. + */ +public class SanityTest extends BaseYouiEngineTest { + + // Confirm we can get the page source and it is not empty. + @org.junit.Test + public void pageSourceTest() throws Exception { + String source; + source = driver.getPageSource(); + System.out.println("\nPageSource: " + source); + Assert.assertThat(source, not("")); + + } + + // Confirm we can take a screenshot. + @org.junit.Test + public void screenshotTest() throws Exception { + File screenShot = driver.getScreenshotAs(OutputType.FILE); + File output = new File("screenShot.PNG"); + FileUtils.copyFile(screenShot, output); + + System.out.println("\nOutput: " + output.getAbsolutePath()); + } + + // Confirm we can take a screenshot with a path supplied. + @org.junit.Test + public void screenshotWithPathTest() throws Exception { + String fileWithPath = Paths.get(System.getProperty("user.home")) + + "/Desktop/Screenshots/screenShot.PNG"; + File output = new File(fileWithPath); + + File screenShot = driver.getScreenshotAs(OutputType.FILE); + FileUtils.copyFile(screenShot, output); + + System.out.println("\nOutput: " + output.getAbsolutePath()); + } + + // Confirm we can find an element using the class name strategy. + @org.junit.Test + public void findElementByClassTest() throws Exception { + WebElement button = null; + try { + button = driver.findElement(By.className("CYIPushButtonView")); + } catch (NoSuchElementException exception) { + Assert.fail("Did not find the control."); + } + Assert.assertNotNull(button); + } + + // Confirm we can find an element using the name strategy. + @org.junit.Test + public void findElementByNameTest() throws Exception { + WebElement buttonsButton = null; + try { + buttonsButton = driver.findElement(By.name("Buttons")); + } catch (NoSuchElementException exception) { + Assert.fail("Did not find the control."); + } + + Assert.assertNotNull(buttonsButton); + } + + // Confirm we throw a NoSuchElementException when the element was not found. + @org.junit.Test + public void findElementNotFoundTest() throws Exception { + boolean exceptionThrown = false; + + try { + driver.findElement(By.className("DoesNotExistView")); + } catch (NoSuchElementException exception) { + exceptionThrown = true; + } + Assert.assertTrue(exceptionThrown); + } + + // Confirm we can find multiple elements using the class name strategy. + @org.junit.Test + public void findMultipleElementsTest() throws Exception { + app.goToTextEditScreen(); + + int expectedCount = 2; + List atlasTextSceneViewList = driver + .findElements(By.className("CYIAtlasTextSceneNode")); + TestUtility.outputListContents(atlasTextSceneViewList); + + Assert.assertEquals(atlasTextSceneViewList.size(), expectedCount); + } + + /* Confirm we can perform a relative find. The parent is found using the class name strategy + * and the child is found using the name strategy. */ + @org.junit.Test + public void findSingleElementFromElementTest() throws Exception { + app.goToTextEditScreen(); + + WebElement field = driver.findElement(By.className("CYITextEditView")); + WebElement childItem = field.findElement(By.name("Text")); + + String foundText = childItem.getText(); + String expectedText = "TextEdit"; + Assert.assertEquals(expectedText, foundText); + } + + /* Confirm we can perform a relative find resulting in multiple elements. The parent is found + * using the class name strategy and its children are also found using the class name strategy. + * */ + @org.junit.Test + public void findMultipleElementsFromElementTest() throws Exception { + app.goToTextEditScreen(); + + WebElement sceneView = driver.findElement(By.className("CYISceneView")); + List labelItems = sceneView.findElements( + By.className("CYIAtlasTextSceneNode")); + TestUtility.outputListContents(labelItems); + + int expectedCount = 2; + int actualCount = labelItems.size(); + Assert.assertEquals(expectedCount, actualCount); + } + + // Confirm we get the text of a button. + @org.junit.Test + public void getTextTest() throws Exception { + app.goToButtonsScreen(); + + String expectedText = "Pushed 0 Times"; + Assert.assertEquals(expectedText, app.buttonsScreen.getPushButtonCaption()); + } + + // Confirm we can retrieve the name attribute of an input field. + @org.junit.Test + public void getNameTest() throws Exception { + app.goToTextEditScreen(); + String expected = "TextEdit"; + + WebElement inputField = app.textEditScreen.getTextEdit(); + Assert.assertEquals(expected, inputField.getAttribute("name")); + } + + // Confirm we can set the text of an input field. + @org.junit.Test + public void valueSetTest() throws Exception { + app.goToTextEditScreen(); + String expected = "One Two 3"; + app.textEditScreen.setTextEditValue(expected); + TestUtility.delayInSeconds(2); + + Assert.assertEquals(expected, app.textEditScreen.getTextEditValue()); + } + + //TODO create one with special keys to validate we support special keys + + // Confirm we can stop and then start up the app. + @org.junit.Test + public void startStopAppTest() throws Exception { + driver.closeApp(); + // NOTE: utils.delayInSeconds allows time for the app to update between commands. It will + // be replaced in the future with app side signals or events to notify the Appium the + // app is ready. + TestUtility.delayInSeconds(2); + driver.launchApp(); + TestUtility.delayInSeconds(2); + } + + // Confirm we can send the app to the background for a short time. + @org.junit.Test + public void runInBackgroundTest() throws Exception { + driver.runAppInBackground(10); + try { + app.goToButtonsScreen(); + } catch (Exception ex) { + Assert.fail("Failed to find or interact with the button after backgrounding the app. " + + "May not have restored properly."); + } + } + + // Confirm we can toggle the Android device's location services. + @org.junit.Test + public void toggleLocationServicesTest() throws Exception { + boolean pass; + try { + CommandExecutionHelper.execute(driver, toggleLocationServicesCommand()); //off + TestUtility.delayInSeconds(2); + CommandExecutionHelper.execute(driver, toggleLocationServicesCommand()); //on + TestUtility.delayInSeconds(2); + pass = true; + } catch (WebDriverException wdEx) { + pass = isAndroid ? false : true; + } + Assert.assertTrue(pass); + } + + // Confirm we can get the context. + @org.junit.Test + public void getContextTest() throws Exception { + String contextValue = driver.getContext(); + + Assert.assertNotNull(contextValue); + System.out.println("\nContext value: " + contextValue); + } + + /* Confirm we can get all contexts. + * NOTE: youiengine currently only supports one context. */ + @org.junit.Test + public void getContextsTest() throws Exception { + Set contextValues = driver.getContextHandles(); + + Assert.assertNotNull(contextValues); + System.out.println("\nContext values: " + contextValues); + } + + // Confirm we can get the session id. + @org.junit.Test + public void getSessionIdTest() throws Exception { + SessionId sessionId = driver.getSessionId(); + Assert.assertNotNull(sessionId); + + System.out.println("\nSession Id: " + sessionId); + } + + /* Confirm we can remove the app. + * NOTE: Only supported on Android and this will also shut down the driver. */ + @org.junit.Test + public void removeAppTest() throws Exception { + boolean actual = false; + try { + driver.removeApp(bundleId); + actual = true; // did not throw exception + } catch (WebDriverException wdException) { + Assert.fail("WebDriverException was thrown."); + } + Assert.assertTrue(actual); + } + + /* Confirm we can remove the app. + * NOTE: Only supported on Android and this will also shut down the driver. */ + @org.junit.Test + public void installAppTest() throws Exception { + boolean actual = false; + try { + driver.removeApp(bundleId); + TestUtility.delayInSeconds(5); + driver.installApp(appPath); + TestUtility.delayInSeconds(5); + actual = driver.isAppInstalled(bundleId); + } catch (WebDriverException wdException) { + if (isAndroid) { + Assert.fail("WebDriverException was thrown while on Android."); + } else { + actual = true; + } + } + Assert.assertTrue(actual); + } + + // Confirm we can determine if an app is installed. + @org.junit.Test + public void isAppInstalledTest() throws Exception { + boolean actual = false; + try { + actual = driver.isAppInstalled(bundleId); + } catch (WebDriverException wdException) { + if (isAndroid) { + Assert.fail("WebDriverException was thrown when not on iOS."); + } else { + System.out.println("\nExpected exception was thrown."); + actual = true; // did not get a value, but we threw an exception for iOS + } + } + Assert.assertTrue(actual); + } + + // Confirm we can press a key code + @org.junit.Test + public void pressKeyCodeTest() throws Exception { + app.goToTextEditScreen(); + boolean actual = false; + app.textEditScreen.getTextEdit().click(); + TestUtility.delayInSeconds(1); + try { + CommandExecutionHelper.execute(driver, pressKeyCodeCommand(KEYCODE_H)); + CommandExecutionHelper.execute(driver, pressKeyCodeCommand(KEYCODE_I)); + + String myText = app.textEditScreen.getTextEditValue(); + actual = Objects.equals(myText, "hi"); + + } catch (WebDriverException wdException) { + if (isAndroid) { + Assert.fail("WebDriverException was thrown when not on iOS."); + } else { + System.out.println("\nExpected exception was thrown."); + actual = true; // did not get a value, but we threw an exception for iOS + } + } + Assert.assertTrue(actual); + } + + // Confirm we can long press a key code + @org.junit.Test + public void longPressKeyCodeTest() throws Exception { + app.goToTextEditScreen(); + + boolean actual = false; + + app.textEditScreen.getTextEdit().click(); + TestUtility.delayInSeconds(2); + + try { + CommandExecutionHelper.execute(driver, longPressKeyCodeCommand(KEYCODE_H)); + + TestUtility.delayInSeconds(5); + String myText = app.textEditScreen.getTextEditValue(); + char myChar0 = myText.charAt(0); + char myChar1 = myText.charAt(1); + + System.out.println("Resulting text: " + myText); + actual = (Objects.equals(myChar0, 'h')) && (Objects.equals(myChar1, 'h')); + + } catch (WebDriverException wdException) { + if (isAndroid) { + Assert.fail("WebDriverException was thrown when not on iOS."); + } else { + System.out.println("\nExpected exception was thrown."); + actual = true; // did not get a value, but we threw an exception for iOS + } + } + Assert.assertTrue(actual); + } + + // Confirm we can set Network to WIFI only + @org.junit.Test + public void networkConnectionAirplaneModeTest() throws Exception { + networkConnectionTest(Connection.AIRPLANE); + } + + // Confirm we can set Network to WIFI only + @org.junit.Test + public void networkConnectionWiFiTest() throws Exception { + networkConnectionTest(Connection.WIFI); + } + + // Confirm we can set Network to DATA only + @org.junit.Test + public void networkConnectionDataTest() throws Exception { + networkConnectionTest(Connection.DATA); + } + + // Confirm we can set Network to ALL + @org.junit.Test + public void networkConnectionAllTest() throws Exception { + networkConnectionTest(Connection.ALL); + } + + /* Performs a device level orientation change and confirms we can retrieve the expected + * orientation. */ + @org.junit.Test + public void verifyOrientationChangesTest() throws Exception { + ScreenOrientation currentOrientation = driver.getOrientation(); + + // Check that the default orientation for this app was Portrait. + if (currentOrientation == ScreenOrientation.PORTRAIT) { + System.out.println("\nOrientation was Portrait."); + } else { + Assert.fail("\nOrientation was not Portrait."); + } + + TestUtility.delayInSeconds(10); + + // Switch the orientation to Landscape + driver.rotate(ScreenOrientation.LANDSCAPE); + TestUtility.delayInSeconds(10); + + currentOrientation = driver.getOrientation(); + if (currentOrientation == ScreenOrientation.LANDSCAPE) { + System.out.println("\nOrientation was Landscape."); + } else { + Assert.fail("\nOrientation was not Landscape."); + } + + // Switch back to Portrait + driver.rotate(ScreenOrientation.PORTRAIT); + TestUtility.delayInSeconds(10); + + currentOrientation = driver.getOrientation(); + if (currentOrientation == ScreenOrientation.PORTRAIT) { + System.out.println("\nOrientation was Portrait."); + } else { + Assert.fail("\nOrientation was not Portrait."); + } + } + + // Call the get app string and ensure we do not throw any exception. + @org.junit.Test + public void getStringsTest() throws Exception { + Map appStringMap = driver.getAppStringMap(); + // we expect null for iOS because this does not have a localizable string file + // for Android it returns a value + if (isAndroid) { + Assert.assertNotNull(appStringMap); + System.out.println("\nMap: " + appStringMap.toString()); + } else { + Assert.assertEquals(null, appStringMap); + } + } + + // TODO These overloads do not currently apply to You.i Engine applications. + // getAppStringMap(language) + // getAppStringMap(language, stringFile) + + // Confirm the resetApp call works. + @org.junit.Test + public void resetTest() throws Exception { + try { + driver.resetApp(); + } catch (Exception ex ) { + Assert.fail("\nException was thrown when trying to reset the app."); + } + } + + @org.junit.Test + public void getLogTypesTest() throws Exception { + Set logTypes = driver.manage().logs().getAvailableLogTypes(); + Assert.assertNotNull(logTypes); + System.out.println("\nLog Types: " + logTypes); + } + + @org.junit.Test + public void getSysLogTest() throws Exception { + if (isAndroid) { + System.out.println("Not a supported Log Type for Android."); + return; + } + LogEntries logs = driver.manage().logs().get("syslog"); + Assert.assertNotNull(logs); + System.out.println("\nLogs: " + logs.toString()); + } + + @org.junit.Test + public void getCrashLogTest() throws Exception { + if (isAndroid) { + System.out.println("Not a supported Log Type for Android."); + return; + } + LogEntries logs = driver.manage().logs().get("crashlog"); + Assert.assertNotNull(logs); + System.out.println("\nLogs: " + logs.toString()); + } + + @org.junit.Test + public void getClientLogTest() throws Exception { + LogEntries logs = driver.manage().logs().get("client"); + Assert.assertNotNull(logs); + System.out.println("\nLogs: " + logs.toString()); + } + + @org.junit.Test + public void getLogCatLogTest() throws Exception { + if (!isAndroid) { + System.out.println("Not a supported Log Type for iOS."); + return; + } + LogEntries logs = driver.manage().logs().get("logcat"); + Assert.assertNotNull(logs); + System.out.println("\nLogs: " + logs.toString()); + } + + // Confirm the mobileShake call works. + @org.junit.Test + public void mobileShakeTest() throws Exception { + if (isAndroid) { + System.out.println("mobileShake() is not supported for Android."); + return; + } + CommandExecutionHelper.execute(driver, shakeCommand()); + } + + // Regression - ensure no exceptions occur when sending a click to a CYIAtlasTextSceneNode. + @org.junit.Test + public void clickOnCyiTextAtlasTest() throws Exception { + app.goToTextEditScreen(); + WebElement textLabel = driver.findElement(By.className("CYIAtlasTextSceneNode")); + try { + textLabel.click(); + } catch (Exception ex) { + // we don't want any exceptions to be thrown + Assert.fail("An exception occurred when clicking on a CYIAtlasTextSceneNode."); + } + } + + /* The following tests ensure that the server properly responds to these unsupported requests + with the appropriate error. */ + @org.junit.Test + public void findElementByXPathTest() throws Exception { + WebElement textEdit; + try { + textEdit = driver.findElement(By.xpath(".//CYISceneView//CYITextEditView")); + Assert.assertNotNull(textEdit); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementsByXPathTest() throws Exception { + List textEdits; + try { + textEdits = driver.findElements(By.xpath(".//CYISceneView//CYITextEditView")); + Assert.assertNotNull(textEdits); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementByCssSelectorTest() throws Exception { + WebElement textEdit; + try { + textEdit = driver.findElement(By.cssSelector(".something")); + Assert.assertNotNull(textEdit); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementsByCssSelectorTest() throws Exception { + List textEdits; + try { + textEdits = driver.findElements(By.cssSelector(".something")); + Assert.assertNotNull(textEdits); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementByLinkTextTest() throws Exception { + WebElement textEdit; + try { + textEdit = driver.findElement(By.linkText("Link")); + Assert.assertNotNull(textEdit); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementsByLinkTextTest() throws Exception { + List textEdits; + try { + textEdits = driver.findElements(By.linkText("Link")); + Assert.assertNotNull(textEdits); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementByPartialLinkTextTest() throws Exception { + WebElement textEdit; + try { + textEdit = driver.findElement(By.partialLinkText("Link")); + Assert.assertNotNull(textEdit); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void findElementsByPartialLinkTextTest() throws Exception { + List textEdits; + try { + textEdits = driver.findElements(By.partialLinkText("Link")); + Assert.assertNotNull(textEdits); + } catch (Exception ex) { + outputException(ex, "Expected exception was thrown."); + return; + } + //shouldn't get this far. + Assert.fail("Did not throw the expected exception."); + } + + @org.junit.Test + public void isSelectedTest() throws Exception { + app.goToButtonsScreen(); + WebElement toggleButton = app.buttonsScreen.getToggleButton(); + + // Initial state should be 'off'. + Assert.assertFalse(toggleButton.isSelected()); + + // Toggle this to on and check again. + toggleButton.click(); + TestUtility.delayInSeconds(1); + Assert.assertTrue(toggleButton.isSelected()); + + // Toggle this back off and confirm it is off. + toggleButton.click(); + TestUtility.delayInSeconds(1); + Assert.assertFalse(toggleButton.isSelected()); + } + + @org.junit.Test + public void isEnabledYes() throws Exception { + app.goToButtonsScreen(); + boolean result = app.buttonsScreen.getPushButton().isEnabled(); + Assert.assertTrue(result); + } + + @org.junit.Test + public void isEnabledNo() throws Exception { + app.goToButtonsScreen(); + + boolean result = app.buttonsScreen.getDisabledButton().isEnabled(); + Assert.assertEquals(false, result); + } + + @org.junit.Test + public void isEnabledNotSupported() throws Exception { + boolean result = false; + app.goToButtonsScreen(); + try { + app.buttonsScreen.getImage().isEnabled(); + } catch (UnsupportedCommandException ucEx) { + result = true; + } + Assert.assertTrue(result); + } + + @org.junit.Test + public void isDisplayedYes() { + app.goToButtonsScreen(); + boolean result = app.buttonsScreen.getPushButton().isDisplayed(); + Assert.assertTrue(result); + } + + @org.junit.Test + public void isDisplayedPartial() { + app.goToButtonsScreen(); + boolean result = app.buttonsScreen.getPartiallyObscuredButton().isDisplayed(); + // partially obscured views are still visible so this would return true + Assert.assertTrue(result); + } + + @org.junit.Test + public void isDisplayedNo() { + app.goToButtonsScreen(); + boolean result = app.buttonsScreen.getOpaqueButton().isDisplayed(); + Assert.assertEquals(false, result); + } + + @org.junit.Test + public void isDisplayedFullyObscured() { + app.goToButtonsScreen(); + // even though this is fully obscured, it is still rendered + boolean result = app.buttonsScreen.getFullyObscuredButton().isDisplayed(); + Assert.assertEquals(true, result); + } + + // Attempting to use a stale element reference should throw the appropriate exception. + @org.junit.Test + public void clickStaleElementTest() throws Exception { + WebElement textEditButton = app.landerScreen.getTextEditButton(); + + textEditButton.click(); + TestUtility.delayInSeconds(2); + + boolean staleElementReferenceExceptionThrown = false; + try { + // Click the button on the previous page + textEditButton.click(); + } catch (StaleElementReferenceException sere) { + staleElementReferenceExceptionThrown = true; + } + Assert.assertTrue(staleElementReferenceExceptionThrown); + } + + /* Set the text of an input field and delete a portion of it using BACK_SPACE. + * */ + @org.junit.Test + public void sendKeysBackspaceTest() throws Exception { + app.goToTextEditScreen(); + String expectedText = "You.i"; + String deletedText = " Engine"; + String sentText = expectedText + deletedText; + + app.textEditScreen.setTextEditValue(sentText); + TestUtility.delayInSeconds(2); + Assert.assertEquals(sentText, app.textEditScreen.getTextEditValue()); + + for (int i = 0; i < deletedText.length(); ++i) { + app.textEditScreen.getTextEdit().sendKeys(Keys.BACK_SPACE); + } + Assert.assertEquals(expectedText, app.textEditScreen.getTextEditValue()); + } + + /* Set the text of an input field and delete a portion of it using ARROW_LEFT and DELETE. + * */ + @org.junit.Test + public void sendKeysDeleteTest() throws Exception { + app.goToTextEditScreen(); + String expectedText = "You.i"; + String deletedText = " Engine"; + String sentText = expectedText + deletedText; + + app.textEditScreen.setTextEditValue(sentText); + TestUtility.delayInSeconds(2); + Assert.assertEquals(sentText, app.textEditScreen.getTextEditValue()); + + // move the cursor to the front of the text to be deleted + for (int i = 0; i < deletedText.length(); ++i) { + app.textEditScreen.getTextEdit().sendKeys(Keys.ARROW_LEFT); + } + TestUtility.delayInSeconds(2); + Assert.assertEquals(sentText, app.textEditScreen.getTextEditValue()); + + for (int i = 0; i < deletedText.length(); ++i) { + app.textEditScreen.getTextEdit().sendKeys(Keys.DELETE); + } + Assert.assertEquals(expectedText, app.textEditScreen.getTextEditValue()); + } + + @org.junit.Test + public void isLockedTest() throws Exception { + boolean result = false; + + try { + Assert.assertFalse(isDeviceLocked()); // should not be locked + CommandExecutionHelper.execute(driver, lockDeviceCommand()); + TestUtility.delayInSeconds(5); + Assert.assertTrue(isDeviceLocked()); // should now be locked + // at this point we don't need to unlockDevice + } catch (Exception ex) { + result = !isAndroid; // expected for iOS + } + Assert.assertTrue(result); + } + + /* This test utilizes the TouchAction methods and tests the Youi Engine implementation. + * */ + @org.junit.Test + public void textEditLocationTest() { + app.goToTextEditScreen(); + Point actual = app.textEditScreen.getTextEdit().getLocation(); + Point expected = new Point(220, 196); + + Assert.assertEquals(expected, actual); + } + + @org.junit.Test + public void passwordEditLocationTest() { + app.goToTextEditScreen(); + Point actual = app.textEditScreen.getPasswordEdit().getLocation(); + Point expected = new Point(218, 327); + + Assert.assertEquals(expected, actual); + } + + @org.junit.Test + public void toggleButtonLocationTest() { + app.goToButtonsScreen(); + Point actual = app.buttonsScreen.getToggleButton().getLocation(); + Point expected = new Point(208, 217); + + Assert.assertEquals(expected, actual); + } + + @org.junit.Test + public void textEditSizeTest() throws Exception { + app.goToTextEditScreen(); + Dimension actual = app.textEditScreen.getTextEdit().getSize(); + Dimension expected = new Dimension(525, 102); + + Assert.assertEquals(expected, actual); + } + + @org.junit.Test + public void passwordEditSizeTest() throws Exception { + app.goToTextEditScreen(); + Dimension actual = app.textEditScreen.getPasswordEdit().getSize(); + Dimension expected = new Dimension(525, 102); + + Assert.assertEquals(expected, actual); + } + + @org.junit.Test + public void partiallyHiddenButtonSizeTest() throws Exception { + app.goToButtonsScreen(); + Dimension actual = app.buttonsScreen.getPartiallyObscuredButton().getSize(); + Dimension expected = new Dimension(513, 113); + + Assert.assertEquals(expected, actual); + } + + // Private Helper methods for testing purposes + private void outputException(Exception ex, String message) { + System.out.println("\n" + message + "\nClass: " + ex.getClass() + "\nMessage: " + + ex.getMessage() + "\nCause: " + ex.getCause() + "\nStackTrace: " + + ex.getStackTrace()); + } + + private boolean isDeviceLocked() { + Response response = CommandExecutionHelper.execute(driver, isLockedCommand()); + System.out.println(response.toString()); + return response.toString().toLowerCase().contains("true"); + } + + private void setConnection(Connection connection) { + CommandExecutionHelper.execute(driver, setConnectionCommand(connection)); + } + + private Connection getConnection() throws WebDriverException { + long response = CommandExecutionHelper.execute(driver, getNetworkConnectionCommand()); + int bitMask = (int)response; + Connection[] types = Connection.values(); + + for (Connection connection: types) { + if (connection.getBitMask() == bitMask) { + return connection; + } + } + throw new WebDriverException("The unknown network connection " + + "type has been returned. The bitmask is " + bitMask); + } + + private void networkConnectionTest(Connection stateToTest) throws Exception { + boolean actual; + try { + // Get initial state + Connection statePrior = getConnection(); + Assert.assertNotEquals(Connection.NONE, statePrior); + + // Set state to test + setConnection(stateToTest); + Connection stateAfter = getConnection(); + Assert.assertEquals(stateToTest, stateAfter); + + // Set state to initial state + setConnection(statePrior); + stateAfter = getConnection(); + Assert.assertEquals(stateAfter, stateAfter); + actual = true; // Passed all tests + + } catch (WebDriverException wdException) { + actual = isAndroid ? false : true; + } + Assert.assertTrue(actual); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/frames/BaseChildFrame.java b/src/test/java/io/appium/java_client/youiengine/frames/BaseChildFrame.java new file mode 100644 index 000000000..dda3e8c7d --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/frames/BaseChildFrame.java @@ -0,0 +1,26 @@ +package io.appium.java_client.youiengine.frames; + +import io.appium.java_client.youiengine.YouiEngineDriver; +import org.openqa.selenium.WebElement; + +/** + * This base class is used for child screens in the YouiEngineAppiumSample app. All child screens + * must have a Back button allowing users to return to the initial page. + * + * Created by paulfoster on 2016-07-26. + */ +public abstract class BaseChildFrame { + protected YouiEngineDriver driver; + + public BaseChildFrame(YouiEngineDriver youiEngineDriver) { + driver = youiEngineDriver; + } + + public WebElement getBackButton() { + return driver.findElementByName("Btn-Back"); + } + + public void goBack() { + getBackButton().click(); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/frames/ButtonsFrame.java b/src/test/java/io/appium/java_client/youiengine/frames/ButtonsFrame.java new file mode 100644 index 000000000..ad881626b --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/frames/ButtonsFrame.java @@ -0,0 +1,61 @@ +package io.appium.java_client.youiengine.frames; + + +import io.appium.java_client.youiengine.YouiEngineDriver; +import org.openqa.selenium.WebElement; + +/** + * Created by paulfoster on 2016-07-21. + */ +public class ButtonsFrame extends BaseChildFrame { + + /* Constructor takes an instance of the YouiEngineDriver. This ensures there is an open and + running session to use. */ + public ButtonsFrame(YouiEngineDriver youiEngineDriver) { + super(youiEngineDriver); + } + + /* The following get methods perform the find as called to ensure the WebElement is present + and not a stale reference. */ + public WebElement getPushButton() { + return driver.findElementByName("PushButton"); + } + + public WebElement getToggleButton() { + return driver.findElementByName("ToggleButton"); + } + + public WebElement getPartiallyObscuredButton() { + return driver.findElementByName("PartHiddenButton"); + } + + public WebElement getFullyObscuredButton() { + return driver.findElementByName("FullyHiddenButton"); + } + + public WebElement getImageView() { + return driver.findElementByName("ImageView"); + } + + public WebElement getDisabledButton() { + return driver.findElementByName("DisabledButton"); + } + + public WebElement getOpaqueButton() { + return driver.findElementByName("OpaqueButton"); + } + + public WebElement getImage() { + return driver.findElementByName("Image-TestNotEnableable"); + } + + /* Helper methods to make authoring test scripts easier and increase readability in the + authored test scripts. */ + public String getPushButtonCaption() { + return getPushButton().getText(); + } + + public String getToggleButtonCaption() { + return getToggleButton().getText(); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/frames/LanderFrame.java b/src/test/java/io/appium/java_client/youiengine/frames/LanderFrame.java new file mode 100644 index 000000000..d7221b7ad --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/frames/LanderFrame.java @@ -0,0 +1,38 @@ +package io.appium.java_client.youiengine.frames; + +import io.appium.java_client.youiengine.YouiEngineDriver; +import org.openqa.selenium.WebElement; + +/** + * Created by paulfoster on 2016-07-21. + */ +public class LanderFrame { + + private YouiEngineDriver driver; + + /* Constructor takes an instance of the YouiEngineDriver. This ensures there is an open and + running session to use. */ + public LanderFrame(YouiEngineDriver youiEngineDriver) { + driver = youiEngineDriver; + } + + /* The following get methods perform the find as called to ensure the WebElement is present + and not a stale reference. */ + public WebElement getTextEditButton() { + return driver.findElementByName("Text Edit"); + } + + public WebElement getButtonsButton() { + return driver.findElementByName("Buttons"); + } + + /* Helper methods to make authoring test scripts easier and increase readability in the + authored test scripts. */ + public void clickTextEditButton() { + getTextEditButton().click(); + } + + public void clickButtonsButton() { + getButtonsButton().click(); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/frames/TextEditFrame.java b/src/test/java/io/appium/java_client/youiengine/frames/TextEditFrame.java new file mode 100644 index 000000000..186d45131 --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/frames/TextEditFrame.java @@ -0,0 +1,57 @@ +package io.appium.java_client.youiengine.frames; + +import io.appium.java_client.youiengine.YouiEngineDriver; +import org.openqa.selenium.WebElement; + +/** + * Created by paulfoster on 2016-07-21. + */ +public class TextEditFrame extends BaseChildFrame { + + /* Constructor takes an instance of the YouiEngineDriver. This ensures there is an open and + running session to use. */ + public TextEditFrame(YouiEngineDriver youiEngineDriver) { + super(youiEngineDriver); + } + + /* The following get methods perform the find as called to ensure the WebElement is present + and not a stale reference. */ + public WebElement getTextEdit() { + return driver.findElementByName("TextEdit"); + } + + public WebElement getPasswordEdit() { + return driver.findElementByName("PasswordEdit"); + } + + /* Helper methods to make authoring test scripts easier and increase readability in the + authored test scripts. */ + public void clearTextEdit() { + getTextEdit().clear(); + } + + public void setTextEditValue(String value) { + getTextEdit().sendKeys(value); + } + + public void clearPasswordEdit() { + getPasswordEdit().clear(); + } + + public void setPasswordEditValue(String value) { + getPasswordEdit().sendKeys(value); + } + + public void clearAll() { + clearTextEdit(); + clearPasswordEdit(); + } + + public String getTextEditValue() { + return getTextEdit().getText(); + } + + public String getPasswordEditValue() { + return getPasswordEdit().getText(); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/frames/YouiEngineAppiumSampleApp.java b/src/test/java/io/appium/java_client/youiengine/frames/YouiEngineAppiumSampleApp.java new file mode 100644 index 000000000..7bf2bfe55 --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/frames/YouiEngineAppiumSampleApp.java @@ -0,0 +1,90 @@ +package io.appium.java_client.youiengine.frames; + +import io.appium.java_client.youiengine.YouiEngineDriver; +import io.appium.java_client.youiengine.util.TestUtility; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.NoSuchFrameException; + +/** + * This class is used to encapsulate all common application behaviours and make them + * available to test authors from one object. Test scripts instance this object and call methods + * from this class or its member objects. + * + * By wrapping interactions in methods based off of UI components, a hierarchy is created that + * allows test authors to write simple code. The actual code behind the interactions can be + * updated in one place which detaches the test scripts from the implementation reducing + * maintenance should the application under test change. + * + * Created by paulfoster on 2016-07-21. + */ + +public class YouiEngineAppiumSampleApp { + private YouiEngineDriver driver; + private static TestUtility utility; + + public LanderFrame landerScreen; + public TextEditFrame textEditScreen; + public ButtonsFrame buttonsScreen; + + /* Constructor takes an instance of the YouiEngineDriver. This ensures there is an open and + running session to use and allows the test author to swap capabilities at the test script + level. + + This allows the YouiEngineSampleApp class to be platform independent. */ + public YouiEngineAppiumSampleApp(YouiEngineDriver youiEngineDriver) { + driver = youiEngineDriver; + landerScreen = new LanderFrame(driver); + textEditScreen = new TextEditFrame(driver); + buttonsScreen = new ButtonsFrame(driver); + } + + /* Helper methods to allow test authors to perform app level navigation. The leverage common + designs in the app such as the presence of a Back button on the second tier screens. */ + public void goHome() throws NoSuchFrameException { + if (!exists("Text Edit")) { + if (!exists("Btn-Back")) { + throw new NoSuchFrameException("Unable to determine what frame is currently " + + "active."); + } + driver.findElementByName("Btn-Back").click(); + } + } + + public void goToButtonsScreen() { + navigateTo("PushButton"); + } + + public void goToTextEditScreen() { + navigateTo("TextEdit"); + } + + // Private helper methods. + private boolean exists(String nameToSearchFor) { + try { + driver.findElementByName(nameToSearchFor); + } + catch (NoSuchElementException nseEx) { + return false; + } + return true; + } + + private void navigateTo(String target) { + // check to see if we can find a known View on the screen before we try navigating + // because we might already be there. + if (!exists(target)) { + // check if a back button exists, if so click it to ensure the app is at the lander + // screen + if (exists("Btn-Back")){ + goHome(); + } + if (target.equals("TextEdit")) { + landerScreen.clickTextEditButton(); + } else { + landerScreen.clickButtonsButton(); + } + } + // TODO for some reason the implicit wait is not working for this one scenario? + utility.delayInSeconds(2); + } +} diff --git a/src/test/java/io/appium/java_client/youiengine/util/BaseYouiEngineTest.java b/src/test/java/io/appium/java_client/youiengine/util/BaseYouiEngineTest.java new file mode 100644 index 000000000..717e6c8be --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/util/BaseYouiEngineTest.java @@ -0,0 +1,113 @@ +package io.appium.java_client.youiengine.util; + +import io.appium.java_client.remote.MobileCapabilityType; +import io.appium.java_client.remote.YouiEngineCapabilityType; +import io.appium.java_client.youiengine.YouiEngineDriver; +import io.appium.java_client.youiengine.frames.YouiEngineAppiumSampleApp; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.openqa.selenium.remote.DesiredCapabilities; + +import java.net.URL; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + + +@SuppressWarnings("ALL") +/** + * This is a utility class to contain settings for a test class and prepare the test session. + * Extend this to create a new test class. + * + * This follows the pattern found in the tutorial for writing Appium java_client tests. + */ +public class BaseYouiEngineTest { + + static { + // Disable annoying cookie warnings. + // WARNING: Invalid cookie header + LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", + "org.apache.commons.logging.impl.NoOpLog"); + } + + public static YouiEngineDriver driver; + public static URL serverAddress; + public static YouiEngineAppiumSampleApp app; + + public String appPath; + public boolean isAndroid; + public String bundleId; + + private DesiredCapabilities capabilities = new DesiredCapabilities(); + + @Rule + public TestRule printTests = new TestWatcher() { + protected void starting(Description description) { + System.out.print(" test: " + description.getMethodName()); + } + + protected void finished(Description description) { + System.out.println(); + } + }; + + private void setupCaps(String appPath) { + capabilities.setCapability(MobileCapabilityType.APP, appPath); + capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "youiengine"); + capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, "0"); + + if (isAndroid) { + // The lines below can be modified to target a device or an AVD. Update accordingly. + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "30044d90e35c6200"); + capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "android"); + capabilities.setCapability(YouiEngineCapabilityType.APP_ADDRESS, "192.168.1.181"); + //capabilities.setCapability(AndroidMobileCapabilityType.AVD, "AVD name goes here"); + + } else { + capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 6s Plus"); + capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS"); + capabilities.setCapability(YouiEngineCapabilityType.APP_ADDRESS, "localhost"); + //capabilities.setCapability(MobileCapabilityType.UDID, "some UDID goes here"); + } + } + + /** Run before each test. **/ + @Before + public void setUp() throws Exception { + + isAndroid = false; + + String currentPath = System.getProperty("user.dir"); + String javaClientPath = "src/test/java/io/appium/java_client/"; + + String appName = "YouiEngineAppiumSample"; + String fullAppName = isAndroid ? appName + "-debug.apk" : appName + ".app.zip"; + String iosAppPath = Paths.get(currentPath, javaClientPath + fullAppName).toAbsolutePath() + .toString(); + String androidAppPath = Paths.get(currentPath, javaClientPath + fullAppName) + .toAbsolutePath().toString(); + appPath = isAndroid ? androidAppPath : iosAppPath; + bundleId = isAndroid ? "tv.youi.youiengine.youiengineappiumsample" : + "tv.youi.YouiEngineAppiumSample"; + setupCaps(appPath); + + serverAddress = new URL("http://127.0.0.1:4723/wd/hub"); + + driver = new YouiEngineDriver(serverAddress, capabilities); + driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); + app = new YouiEngineAppiumSampleApp(driver); + } + + /** Run after each test. **/ + @After + public void tearDown() throws Exception { + if (driver != null) { + driver.quit(); + } + } + +} diff --git a/src/test/java/io/appium/java_client/youiengine/util/TestUtility.java b/src/test/java/io/appium/java_client/youiengine/util/TestUtility.java new file mode 100644 index 000000000..d78d2a032 --- /dev/null +++ b/src/test/java/io/appium/java_client/youiengine/util/TestUtility.java @@ -0,0 +1,36 @@ +package io.appium.java_client.youiengine.util; + +import org.openqa.selenium.WebElement; + +import java.util.Iterator; +import java.util.List; + +/** + * This TestUtility is available to help test authors by wrapping common helper methods into a + * single class which can be instantiated in their test classes. + */ +public class TestUtility { + /** Causes a delay in the provided number of seconds on the calling thread. + * This practice of a hard set delay is not a good practice. The specified delay may work for + * one device but not another, slower, device. This method is in place as a temporary work + * around until an additional timeout option is added into the automation layer. */ + public static void delayInSeconds(long delay) { + long delaySec = delay * 1000; + long currentTime = System.currentTimeMillis(); + long startTime = currentTime; + do { + try { + Thread.sleep(currentTime + delaySec - startTime); + } catch (InterruptedException iEx) { } + currentTime = System.currentTimeMillis(); + } while (currentTime < startTime + delaySec); + } + + /** This will print out the provided list to the console in a simple format. */ + public static void outputListContents(List listItems) { + System.out.println("\nList Contents:"); + for (Iterator item = listItems.iterator(); item.hasNext(); ) { + System.out.println("\tItem: " + item.next()); + } + } +} diff --git a/src/test/resources/META-INF/services/io.appium.java_client.events.api.Listener b/src/test/resources/META-INF/services/io.appium.java_client.events.api.Listener new file mode 100644 index 000000000..6faceb0d0 --- /dev/null +++ b/src/test/resources/META-INF/services/io.appium.java_client.events.api.Listener @@ -0,0 +1,10 @@ +io.appium.java_client.events.listeners.AlertListener +io.appium.java_client.events.listeners.RotationListener +io.appium.java_client.events.listeners.WindowListener +io.appium.java_client.events.listeners.ContextListener +io.appium.java_client.events.listeners.ElementListener +io.appium.java_client.events.listeners.ExceptionListener +io.appium.java_client.events.listeners.JavaScriptListener +io.appium.java_client.events.listeners.NavigationListener +io.appium.java_client.events.listeners.SearchingListener +io.appium.java_client.events.listeners.AppiumListener