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..c735adb80 100644 --- a/pom.xml +++ b/pom.xml @@ -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/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/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/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