From 002ff5dad5e9aa78feccfc38341d2272b542bf63 Mon Sep 17 00:00:00 2001 From: Toilal Date: Wed, 24 Aug 2016 23:26:03 +0200 Subject: [PATCH] Add hooks support --- .../org/fluentlenium/core/FluentDriver.java | 11 +- .../java/org/fluentlenium/core/axes/Axes.java | 16 +- .../core/components/ComponentsManager.java | 12 +- .../DefaultComponentInstantiator.java | 1 - .../fluentlenium/core/domain/FluentList.java | 3 +- .../core/domain/FluentListImpl.java | 66 ++- .../core/domain/FluentWebElement.java | 57 ++- .../core/hook/BaseFluentHook.java | 24 ++ .../org/fluentlenium/core/hook/BaseHook.java | 60 +++ .../core/hook/DefaultHookChainBuilder.java | 56 +++ .../fluentlenium/core/hook/FluentHook.java | 10 + .../java/org/fluentlenium/core/hook/Hook.java | 14 + .../core/hook/HookChainBuilder.java | 11 + .../fluentlenium/core/hook/HookControl.java | 29 ++ .../core/hook/HookDefinition.java | 17 + .../fluentlenium/core/hook/HookException.java | 18 + .../fluentlenium/core/hook/HookOptions.java | 14 + .../org/fluentlenium/core/hook/NoHook.java | 14 + .../org/fluentlenium/core/hook/wait/Wait.java | 24 ++ .../fluentlenium/core/hook/wait/WaitHook.java | 76 ++++ .../core/hook/wait/WaitHookOptions.java | 50 +++ .../core/inject/FluentInjector.java | 153 ++++++- .../core/proxy/AbstractListHandler.java | 216 ++++++++++ .../core/proxy/AbstractLocatorHandler.java | 31 ++ .../core/proxy/ComponentHandler.java | 97 +++-- .../core/proxy/FluentListHandler.java | 98 +---- .../core/proxy/InstanceElementLocator.java | 31 ++ .../fluentlenium/core/proxy/ListHandler.java | 42 +- .../core/proxy/LocatorHandler.java | 58 +++ .../core/proxy/LocatorProxies.java | 140 +++++++ .../org/fluentlenium/core/proxy/Proxies.java | 181 -------- .../core/proxy/ProxyElementListener.java | 4 +- .../org/fluentlenium/core/search/Search.java | 25 +- .../wait/FluentWaitElementListMatcher.java | 2 +- .../core/wait/FluentWaitElementMatcher.java | 2 +- .../core/wait/FluentWaitSupplierMatcher.java | 2 +- .../adapter/IsolatedTestTest.java | 6 +- .../org/fluentlenium/core/axes/AxesTest.java | 18 +- .../core/domain/FluentListImplTest.java | 6 +- .../core/hook/BaseFluentHookTest.java | 57 +++ .../fluentlenium/core/hook/BaseHookTest.java | 93 +++++ .../core/hook/HookChainBuilderTest.java | 95 +++++ .../org/fluentlenium/core/hook/NanoHook.java | 88 ++++ .../core/hook/NanoHookOptions.java | 14 + .../core/hook/SearchHookTest.java | 53 +++ .../inject/FluentInjectorElementTest.java | 121 +++--- .../core/inject/FluentInjectorHookTest.java | 391 ++++++++++++++++++ .../fluentlenium/core/proxy/ProxiesTest.java | 8 +- .../fluentlenium/core/search/SearchTest.java | 4 +- 49 files changed, 2123 insertions(+), 496 deletions(-) create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseFluentHook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseHook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/DefaultHookChainBuilder.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/FluentHook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/Hook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookChainBuilder.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookControl.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookDefinition.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookException.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookOptions.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/NoHook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/Wait.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHook.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHookOptions.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractListHandler.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractLocatorHandler.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/InstanceElementLocator.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorHandler.java create mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorProxies.java delete mode 100644 fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/Proxies.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseFluentHookTest.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseHookTest.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/HookChainBuilderTest.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHook.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHookOptions.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/hook/SearchHookTest.java create mode 100644 fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorHookTest.java diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/FluentDriver.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/FluentDriver.java index a07962a078..7c179bd10d 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/FluentDriver.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/FluentDriver.java @@ -14,9 +14,11 @@ import org.fluentlenium.core.events.EventsRegistry; import org.fluentlenium.core.events.AnnotationsComponentListener; import org.fluentlenium.core.filter.Filter; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; +import org.fluentlenium.core.hook.HookChainBuilder; import org.fluentlenium.core.inject.DefaultContainerInstanciator; import org.fluentlenium.core.inject.FluentInjector; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.proxy.LocatorProxies; import org.fluentlenium.core.script.FluentJavascript; import org.fluentlenium.core.search.Search; import org.fluentlenium.core.wait.FluentWait; @@ -64,6 +66,8 @@ public class FluentDriver implements FluentDriverControl { private KeyboardActions keyboardActions; + private HookChainBuilder hookChainBuilder; + private WindowAction windowAction; public FluentDriver(WebDriver driver, ConfigurationProperties configuration, ComponentsManager componentsManager) { @@ -111,7 +115,8 @@ private void configureDriver() { protected FluentDriver initFluent(WebDriver driver) { this.driver = driver; - this.search = new Search(driver, componentsManager); + this.hookChainBuilder = new DefaultHookChainBuilder(this.driver, this.componentsManager.getInstantiator()); + this.search = new Search(driver, componentsManager, hookChainBuilder); if (driver instanceof EventFiringWebDriver) { this.events = new EventsRegistry((EventFiringWebDriver) driver); this.eventsComponentsAnnotations = new AnnotationsComponentListener(componentsManager); @@ -414,7 +419,7 @@ public void switchTo(FluentWebElement element) { if (null == element || !"iframe".equals(element.getTagName())) { getDriver().switchTo().defaultContent(); } else { - getDriver().switchTo().frame(Proxies.getElement(element.getElement())); + getDriver().switchTo().frame(LocatorProxies.getLocatorResult(element.getElement())); } } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/axes/Axes.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/axes/Axes.java index 8956293138..573f2153e5 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/axes/Axes.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/axes/Axes.java @@ -2,15 +2,14 @@ import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.domain.FluentList; -import org.fluentlenium.core.domain.FluentListImpl; import org.fluentlenium.core.domain.FluentWebElement; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.proxy.LocatorProxies; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -20,11 +19,12 @@ public class Axes { private final WebElement webElement; private final ComponentInstantiator instantiator; + private final HookChainBuilder hookChainBuilder; - - public Axes(WebElement element, ComponentInstantiator instantiator) { + public Axes(WebElement element, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { this.webElement = element; this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; } @@ -34,7 +34,7 @@ public Axes(WebElement element, ComponentInstantiator instantiator) { * @return fluent web element */ public FluentWebElement parent() { - return Proxies.createComponent(new ElementLocator() { + return LocatorProxies.createComponent(new ElementLocator() { @Override public WebElement findElement() { return Axes.this.webElement.findElement(By.xpath("parent::*")); @@ -48,7 +48,7 @@ public List findElements() { } protected FluentList handleAxe(final String axe) { - return Proxies.createFluentList(new ElementLocator() { + return LocatorProxies.createFluentList(new ElementLocator() { @Override public WebElement findElement() { return Axes.this.webElement.findElement(By.xpath(axe + "::*")); @@ -58,7 +58,7 @@ public WebElement findElement() { public List findElements() { return Axes.this.webElement.findElements(By.xpath(axe + "::*")); } - }, FluentWebElement.class, instantiator); + }, FluentWebElement.class, instantiator, hookChainBuilder); } /** diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/components/ComponentsManager.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/components/ComponentsManager.java index d333e3d009..5a946a4aea 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/components/ComponentsManager.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/components/ComponentsManager.java @@ -2,7 +2,7 @@ import com.sun.jna.WeakIdentityHashMap; import org.fluentlenium.core.proxy.ProxyElementListener; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.proxy.LocatorProxies; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.internal.WrapsElement; @@ -81,13 +81,13 @@ public T newComponent(Class componentClass, WebElement element) { + " is not a valid component class. No valid constructor found (WebElement) or (WebElement, WebDriver)", e); } WebElement webElement = unwrapElement(element); - Proxies.addProxyListener(webElement, this); + LocatorProxies.addProxyListener(webElement, this); components.put(webElement, component); return component; } @Override - public void proxyElementSearch(WebElement proxy, ElementLocator locator) { + public void proxyElementSearch(Object proxy, ElementLocator locator) { } /** @@ -99,10 +99,10 @@ public void proxyElementSearch(WebElement proxy, ElementLocator locator) { * @param element found element. */ @Override - public synchronized void proxyElementFound(WebElement proxy, ElementLocator locator, WebElement element) { + public synchronized void proxyElementFound(Object proxy, ElementLocator locator, WebElement element) { Object component = components.remove(proxy); if (component != null) { - components.put(unwrapElement(proxy), component); + components.put(unwrapElement(element), component); } } @@ -121,7 +121,7 @@ private WebElement unwrapElement(WebElement element) { */ public void release() { for (WebElement element : components.keySet()) { - Proxies.removeProxyListener(element, this); + LocatorProxies.removeProxyListener(element, this); } components.clear(); } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/components/DefaultComponentInstantiator.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/components/DefaultComponentInstantiator.java index 9fc23a73bf..1cf32f2983 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/components/DefaultComponentInstantiator.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/components/DefaultComponentInstantiator.java @@ -1,6 +1,5 @@ package org.fluentlenium.core.components; -import org.fluentlenium.core.inject.FluentInjectException; import org.fluentlenium.utils.ReflectionUtils; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentList.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentList.java index 9c2baa0546..11e8ac5f07 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentList.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentList.java @@ -3,6 +3,7 @@ import org.fluentlenium.core.action.FluentActions; import org.fluentlenium.core.conditions.FluentListConditions; import org.fluentlenium.core.filter.Filter; +import org.fluentlenium.core.hook.HookControl; import org.fluentlenium.core.proxy.FluentProxyState; import org.fluentlenium.core.search.SearchControl; import org.openqa.selenium.WebElement; @@ -12,7 +13,7 @@ /** * */ -public interface FluentList extends List, FluentActions, E>, FluentProxyState>, SearchControl { +public interface FluentList extends List, FluentActions, E>, FluentProxyState>, SearchControl, HookControl> { /** * Return the first element of the list diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentListImpl.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentListImpl.java index ccd6cd2de8..9362e112ad 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentListImpl.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentListImpl.java @@ -9,17 +9,20 @@ import org.fluentlenium.core.conditions.EachElementConditions; import org.fluentlenium.core.conditions.FluentListConditions; import org.fluentlenium.core.filter.Filter; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; +import org.fluentlenium.core.hook.FluentHook; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; import org.fluentlenium.core.proxy.ListElementAccessor; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.proxy.LocatorProxies; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; import java.util.List; /** @@ -29,6 +32,9 @@ public class FluentListImpl extends ArrayList imp private Class componentClass; private ComponentInstantiator instantiator; + private final List> hookDefinitions = new ArrayList<>(); + private HookChainBuilder hookChainBuilder; + public FluentListImpl() { } @@ -40,22 +46,25 @@ public FluentListImpl(Collection listFiltered) { super(listFiltered); } - public FluentListImpl(Class componentClass, ComponentInstantiator instantiator) { + public FluentListImpl(Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { super(); this.componentClass = componentClass; this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; } - public FluentListImpl(Class componentClass, ComponentInstantiator instantiator, E... listFiltered) { + public FluentListImpl(Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, E... listFiltered) { super(new ArrayList(Arrays.asList(listFiltered))); this.componentClass = componentClass; this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; } - public FluentListImpl(Class componentClass, ComponentInstantiator instantiator, Collection listFiltered) { + public FluentListImpl(Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, Collection listFiltered) { super(listFiltered); this.componentClass = componentClass; this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; } /** @@ -64,8 +73,8 @@ public FluentListImpl(Class componentClass, ComponentInstantiator instantiato * @param elements array of Selenium elements * @return FluentList of FluentWebElement */ - public static FluentListImpl fromElements(ComponentInstantiator instantiator, WebElement... elements) { - return fromElements(instantiator, Arrays.asList(elements)); + public static FluentListImpl fromElements(ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, WebElement... elements) { + return fromElements(instantiator, hookChainBuilder, Arrays.asList(elements)); } /** @@ -74,8 +83,8 @@ public static FluentListImpl fromElements(ComponentInstantiato * @param elements iterable of Selenium elements * @return FluentList of FluentWebElement */ - public static FluentListImpl fromElements(ComponentInstantiator instantiator, Iterable elements) { - FluentListImpl fluentWebElements = new FluentListImpl<>(FluentWebElement.class, instantiator); + public static FluentListImpl fromElements(ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, Iterable elements) { + FluentListImpl fluentWebElements = new FluentListImpl<>(FluentWebElement.class, instantiator, hookChainBuilder); for (WebElement element : elements) { fluentWebElements.add(instantiator.newComponent(FluentWebElement.class, element)); } @@ -121,12 +130,12 @@ public E index(int index) { @Override public boolean isPresent() { - return Proxies.isPresent(this); + return LocatorProxies.isPresent(this); } @Override public FluentList now() { - Proxies.now(this); + LocatorProxies.now(this); if (this.size() == 0) { throw new NoSuchElementException("Element not found"); } @@ -135,13 +144,13 @@ public FluentList now() { @Override public FluentList reset() { - Proxies.reset(this); + LocatorProxies.reset(this); return this; } @Override public boolean isLoaded() { - return Proxies.isLoaded(this); + return LocatorProxies.isLoaded(this); } @Override @@ -384,7 +393,7 @@ public FluentList find(String selector, Filter... filters) { for (FluentWebElement e : this) { finds.addAll((Collection) e.find(selector, filters)); } - return new FluentListImpl(componentClass, instantiator, finds); + return new FluentListImpl(componentClass, instantiator, hookChainBuilder, finds); } @Override @@ -393,7 +402,7 @@ public FluentList find(By locator, Filter... filters) { for (FluentWebElement e : this) { finds.addAll((Collection) e.find(locator, filters)); } - return new FluentListImpl(componentClass, instantiator, finds); + return new FluentListImpl(componentClass, instantiator, hookChainBuilder, finds); } @Override @@ -402,7 +411,7 @@ public FluentList find(Filter... filters) { for (FluentWebElement e : this) { finds.addAll((Collection) e.find(filters)); } - return new FluentListImpl(componentClass, instantiator, finds); + return new FluentListImpl(componentClass, instantiator, hookChainBuilder, finds); } @Override @@ -491,7 +500,28 @@ public FluentList as(Class componentClass) { elements.add(e.as(componentClass)); } - return new FluentListImpl<>(componentClass, instantiator, elements); + return new FluentListImpl<>(componentClass, instantiator, hookChainBuilder, elements); + } + + @Override + public FluentList noHook() { + hookDefinitions.clear(); + LocatorProxies.setHooks(hookChainBuilder, this, hookDefinitions); + return this; + } + + @Override + public > FluentList withHook(Class hook) { + hookDefinitions.add(new HookDefinition<>(hook)); + LocatorProxies.setHooks(hookChainBuilder, this, hookDefinitions); + return this; + } + + @Override + public > FluentList withHook(Class hook, O options) { + hookDefinitions.add(new HookDefinition<>(hook, options)); + LocatorProxies.setHooks(hookChainBuilder, this, hookDefinitions); + return this; } } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentWebElement.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentWebElement.java index b885fde638..0039e5f2a3 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentWebElement.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/domain/FluentWebElement.java @@ -1,16 +1,21 @@ package org.fluentlenium.core.domain; -import org.fluentlenium.core.action.FluentActions; import org.fluentlenium.core.action.Fill; import org.fluentlenium.core.action.FillSelect; +import org.fluentlenium.core.action.FluentActions; import org.fluentlenium.core.action.KeyboardElementActions; import org.fluentlenium.core.action.MouseElementActions; import org.fluentlenium.core.axes.Axes; import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.conditions.WebElementConditions; import org.fluentlenium.core.filter.Filter; +import org.fluentlenium.core.hook.FluentHook; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookControl; +import org.fluentlenium.core.hook.HookDefinition; import org.fluentlenium.core.proxy.FluentProxyState; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.proxy.LocatorProxies; import org.fluentlenium.core.search.Search; import org.fluentlenium.core.search.SearchControl; import org.openqa.selenium.By; @@ -20,12 +25,14 @@ import org.openqa.selenium.internal.WrapsElement; import org.openqa.selenium.support.ui.ExpectedConditions; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * WebElementCustom include a Selenium WebElement. It provides a lot of shortcuts to make selenium more fluent */ -public class FluentWebElement implements WrapsElement, FluentActions, FluentProxyState, SearchControl { +public class FluentWebElement implements WrapsElement, FluentActions, FluentProxyState, SearchControl, HookControl { private WebElement webElement; private final WebDriver driver; private final ComponentInstantiator instantiator; @@ -36,16 +43,23 @@ public class FluentWebElement implements WrapsElement, FluentActions> hookDefinitions = new ArrayList<>(); + private final HookChainBuilder hookChainBuilder; + public FluentWebElement(WebElement webElement, WebDriver driver, ComponentInstantiator instantiator) { this.webElement = webElement; this.driver = driver; this.instantiator = instantiator; - this.search = new Search(webElement, this.instantiator); - this.axes = new Axes(webElement, this.instantiator); + this.hookChainBuilder = new DefaultHookChainBuilder(this.driver, this.instantiator); + + this.search = new Search(webElement, this.instantiator, this.hookChainBuilder); + this.axes = new Axes(webElement, this.instantiator, this.hookChainBuilder); this.mouseActions = new MouseElementActions(this.driver, webElement); this.keyboardActions = new KeyboardElementActions(this.driver, webElement); this.conditions = new WebElementConditions(this); + + } /** @@ -60,24 +74,24 @@ public FluentWebElement click() { @Override public boolean isPresent() { - return Proxies.isPresent(webElement); + return LocatorProxies.isPresent(webElement); } @Override public FluentWebElement now() { - Proxies.now(webElement); + LocatorProxies.now(webElement); return this; } @Override public FluentWebElement reset() { - Proxies.reset(webElement); + LocatorProxies.reset(webElement); return this; } @Override public boolean isLoaded() { - return Proxies.isLoaded(webElement); + return LocatorProxies.isLoaded(webElement); } /** @@ -277,7 +291,7 @@ public String getTagName() { */ public WebElement getElement() { if (webElement instanceof FailWebElement) { - ((FailWebElement)webElement).fail(); + ((FailWebElement) webElement).fail(); } return webElement; } @@ -297,7 +311,7 @@ public Dimension getSize() { } public FluentList asList() { - return new FluentListImpl<>(FluentWebElement.class, instantiator, Arrays.asList(this)); + return new FluentListImpl<>(FluentWebElement.class, instantiator, hookChainBuilder, Arrays.asList(this)); } @Override @@ -472,4 +486,25 @@ private boolean isInputOfTypeFile() { public String toString() { return this.getElement().toString(); } + + @Override + public FluentWebElement noHook() { + hookDefinitions.clear(); + LocatorProxies.setHooks(hookChainBuilder, getElement(), hookDefinitions); + return this; + } + + @Override + public > FluentWebElement withHook(Class hook) { + hookDefinitions.add(new HookDefinition<>(hook)); + LocatorProxies.setHooks(hookChainBuilder, getElement(), hookDefinitions); + return this; + } + + @Override + public > FluentWebElement withHook(Class hook, O options) { + hookDefinitions.add(new HookDefinition<>(hook, options)); + LocatorProxies.setHooks(hookChainBuilder, getElement(), hookDefinitions); + return this; + } } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseFluentHook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseFluentHook.java new file mode 100644 index 0000000000..20c0d3c5c0 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseFluentHook.java @@ -0,0 +1,24 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.domain.FluentWebElement; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +public class BaseFluentHook extends BaseHook { + private FluentWebElement fluentWebElement; + + public BaseFluentHook(WebDriver webDriver, ComponentInstantiator instantiator, Supplier elementSupplier, Supplier locatorSupplier, T options) { + super(webDriver, instantiator, elementSupplier, locatorSupplier, options); + } + + public FluentWebElement getFluentWebElement() { + WebElement element = getElement(); + if (fluentWebElement == null || element != fluentWebElement.getElement()) { + fluentWebElement = getInstantiator().newComponent(FluentWebElement.class, element); + } + return fluentWebElement; + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseHook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseHook.java new file mode 100644 index 0000000000..eec20ecd38 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/BaseHook.java @@ -0,0 +1,60 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import lombok.experimental.Delegate; +import org.fluentlenium.core.DefaultFluentContainer; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +public class BaseHook extends DefaultFluentContainer implements FluentHook { + private final ComponentInstantiator instantiator; + + private final WebDriver webDriver; + + private final Supplier locatorSupplier; + + private T options; + + private final Supplier elementSupplier; + + @Delegate + public final WebElement getElement() { + return elementSupplier.get(); + } + + @Delegate + public final ElementLocator getElementLocator() { + return locatorSupplier.get(); + } + + public BaseHook(WebDriver webDriver, ComponentInstantiator instantiator, Supplier elementSupplier, Supplier locatorSupplier, T options) { + this.webDriver = webDriver; + this.instantiator = instantiator; + this.elementSupplier = elementSupplier; + this.locatorSupplier = locatorSupplier; + this.options = options; + + if (this.options == null) { + this.options = newOptions(); + } + } + + protected T newOptions() { + return null; + } + + public WebDriver getWebDriver() { + return webDriver; + } + + public ComponentInstantiator getInstantiator() { + return instantiator; + } + + @Override + public T getOptions() { + return options; + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/DefaultHookChainBuilder.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/DefaultHookChainBuilder.java new file mode 100644 index 0000000000..701d30c085 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/DefaultHookChainBuilder.java @@ -0,0 +1,56 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.utils.ReflectionUtils; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +public class DefaultHookChainBuilder implements HookChainBuilder { + private final WebDriver webDriver; + private final ComponentInstantiator instantiator; + + public DefaultHookChainBuilder(WebDriver webDriver, ComponentInstantiator instantiator) { + this.webDriver = webDriver; + this.instantiator = instantiator; + } + + @Override + public List build(Supplier elementSupplier, Supplier locator, List> hooks) { + List chain = new ArrayList<>(); + + Supplier currentSupplier = elementSupplier; + + for (HookDefinition hook : hooks) { + FluentHook newObject; + try { + newObject = newInstance(hook.getHookClass(), webDriver, instantiator, currentSupplier, locator, hook.getOptions()); + } catch (NoSuchMethodException e) { + throw new HookException(e); + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new HookException(e); + } + final FluentHook hookInstance = newObject; + + currentSupplier = new Supplier() { + @Override + public WebElement get() { + return hookInstance; + } + }; + + chain.add(hookInstance); + } + + return chain; + } + + protected FluentHook newInstance(Class> hookClass, WebDriver webDriver, ComponentInstantiator instantiator, Supplier currentSupplier, Supplier locator, Object options) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + return ReflectionUtils.newInstance(hookClass, webDriver, instantiator, currentSupplier, locator, options); + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/FluentHook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/FluentHook.java new file mode 100644 index 0000000000..b2dcfbd41b --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/FluentHook.java @@ -0,0 +1,10 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import org.fluentlenium.core.domain.FluentWebElement; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +public interface FluentHook extends WebElement, ElementLocator { + T getOptions(); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/Hook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/Hook.java new file mode 100644 index 0000000000..64403fd3c8 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/Hook.java @@ -0,0 +1,14 @@ +package org.fluentlenium.core.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.FIELD, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Hook { + Class> value(); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookChainBuilder.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookChainBuilder.java new file mode 100644 index 0000000000..8c69e7611a --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookChainBuilder.java @@ -0,0 +1,11 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.List; + +public interface HookChainBuilder { + List build(Supplier elementSupplier, Supplier locator, List> hooks); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookControl.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookControl.java new file mode 100644 index 0000000000..e99871d416 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookControl.java @@ -0,0 +1,29 @@ +package org.fluentlenium.core.hook; + +public interface HookControl { + /** + * Disable all hooks. + * + * @return + */ + T noHook(); + + /** + * Enable a hook with default options. + * + * @param hook class to enable. + * @return + */ + > T withHook(Class hook); + + /** + * Enable a hook with given options. + * + * @param hook + * @param options + * @param + * @param + * @return + */ + > T withHook(Class hook, O options); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookDefinition.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookDefinition.java new file mode 100644 index 0000000000..22c0eaf0cb --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookDefinition.java @@ -0,0 +1,17 @@ +package org.fluentlenium.core.hook; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class HookDefinition { + @NonNull + private Class> hookClass; + private T options; +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookException.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookException.java new file mode 100644 index 0000000000..defb91b002 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookException.java @@ -0,0 +1,18 @@ +package org.fluentlenium.core.hook; + +public class HookException extends RuntimeException { + public HookException() { + } + + public HookException(String message) { + super(message); + } + + public HookException(String message, Throwable cause) { + super(message, cause); + } + + public HookException(Throwable cause) { + super(cause); + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookOptions.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookOptions.java new file mode 100644 index 0000000000..fdfb7efafd --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/HookOptions.java @@ -0,0 +1,14 @@ +package org.fluentlenium.core.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.FIELD, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface HookOptions { + Class value(); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/NoHook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/NoHook.java new file mode 100644 index 0000000000..3fd91ee4e7 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/NoHook.java @@ -0,0 +1,14 @@ +package org.fluentlenium.core.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface NoHook { + +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/Wait.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/Wait.java new file mode 100644 index 0000000000..20d4295eb9 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/Wait.java @@ -0,0 +1,24 @@ +package org.fluentlenium.core.hook.wait; + +import org.fluentlenium.core.hook.Hook; +import org.fluentlenium.core.hook.HookOptions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Inherited +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Hook(WaitHook.class) +@HookOptions(WaitHookOptions.class) +public @interface Wait { + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + long atMost() default -1L; + long pollingEvery() default -1L; + boolean withNoDefaultsException(); + java.lang.Class[] ignoreAll(); +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHook.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHook.java new file mode 100644 index 0000000000..432df46a93 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHook.java @@ -0,0 +1,76 @@ +package org.fluentlenium.core.hook.wait; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import org.fluentlenium.core.FluentDriver; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.components.ComponentsManager; +import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.fluentlenium.core.domain.FluentWebElement; +import org.fluentlenium.core.hook.BaseFluentHook; +import org.fluentlenium.core.hook.BaseHook; +import org.fluentlenium.core.wait.FluentWait; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.List; + +public class WaitHook extends BaseFluentHook { + public WaitHook(WebDriver webDriver, ComponentInstantiator instantiator, Supplier elementSupplier, Supplier locatorSupplier, WaitHookOptions options) { + super(webDriver, instantiator, elementSupplier, locatorSupplier, options); + } + + @Override + protected WaitHookOptions newOptions() { + return new WaitHookOptions(); + } + + public FluentWait buildAwait() { + return getOptions().configureAwait(await()); + } + + @Override + public void click() { + buildAwait().until(getFluentWebElement()).isClickable(); + super.click(); + } + + @Override + public void sendKeys(CharSequence... keysToSend) { + buildAwait().until(getFluentWebElement()).isEnabled(); + super.sendKeys(keysToSend); + } + + @Override + public void submit() { + buildAwait().until(getFluentWebElement()).isEnabled(); + super.submit(); + } + + @Override + public void clear() { + buildAwait().until(getFluentWebElement()).isEnabled(); + super.clear(); + } + + @Override + public List findElements() { + return buildAwait().until(new Function>() { + @Override + public List apply(FluentDriver input) { + return WaitHook.super.findElements(); + } + }); + } + + @Override + public WebElement findElement() { + return buildAwait().until(new Function() { + @Override + public WebElement apply(FluentDriver input) { + return WaitHook.super.findElement(); + } + }); + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHookOptions.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHookOptions.java new file mode 100644 index 0000000000..5e301a8698 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/hook/wait/WaitHookOptions.java @@ -0,0 +1,50 @@ +package org.fluentlenium.core.hook.wait; + +import lombok.Getter; +import lombok.Setter; +import org.fluentlenium.core.wait.FluentWait; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +@Getter +@Setter +public class WaitHookOptions { + + private TimeUnit timeUnit = TimeUnit.MILLISECONDS; + private Long atMost; + private Long pollingEvery; + private java.util.Collection> ignoreAll; + private boolean withNoDefaultsException; + + public WaitHookOptions() {} + + public WaitHookOptions(Wait annotation) { + timeUnit = annotation.timeUnit(); + atMost = annotation.atMost() == -1L ? null : annotation.atMost(); + pollingEvery = annotation.pollingEvery() == -1L ? null : annotation.pollingEvery(); + ignoreAll = new ArrayList>(Arrays.asList(annotation.ignoreAll())); + withNoDefaultsException = annotation.withNoDefaultsException(); + } + + FluentWait configureAwait(FluentWait await) { + if (atMost != null) { + await.atMost(atMost, timeUnit); + } + + if (pollingEvery != null) { + await.pollingEvery(pollingEvery, timeUnit); + } + + if (withNoDefaultsException) { + await.withNoDefaultsException(); + } + + if (ignoreAll != null) { + await.ignoreAll(ignoreAll); + } + + return await; + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/inject/FluentInjector.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/inject/FluentInjector.java index 5086a580ba..876022af21 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/inject/FluentInjector.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/inject/FluentInjector.java @@ -8,7 +8,14 @@ import org.fluentlenium.core.domain.FluentList; import org.fluentlenium.core.domain.FluentWebElement; import org.fluentlenium.core.events.ContainerAnnotationsEventsRegistry; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.hook.NoHook; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; +import org.fluentlenium.core.hook.FluentHook; +import org.fluentlenium.core.hook.Hook; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; +import org.fluentlenium.core.hook.HookOptions; +import org.fluentlenium.core.proxy.LocatorProxies; import org.fluentlenium.utils.ReflectionUtils; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.events.EventFiringWebDriver; @@ -16,10 +23,13 @@ import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -30,16 +40,19 @@ public class FluentInjector implements FluentInjectControl { private final ConcurrentMap pageInstances = new ConcurrentHashMap<>(); + private final ConcurrentMap>> pageHookDefinitions = new ConcurrentHashMap<>(); private final ConcurrentMap eventsContainerSupport = new ConcurrentHashMap<>(); private final FluentControl fluentControl; private final ComponentsManager componentsManager; private final ContainerInstanciator containerInstanciator; + private final DefaultHookChainBuilder hookChainBuilder; public FluentInjector(FluentControl fluentControl, ComponentsManager componentsManager, ContainerInstanciator instanciator) { this.fluentControl = fluentControl; this.componentsManager = componentsManager; this.containerInstanciator = instanciator; + this.hookChainBuilder = new DefaultHookChainBuilder(fluentControl.getDriver(), componentsManager.getInstantiator()); } /** @@ -69,9 +82,13 @@ public void inject(Object... containers) { @Override public void inject(Object container) { + inject(container, new ArrayList>()); + } + + private void inject(Object container, List> hookDefinitions) { initContainer(container); - initChildrenContainers(container); - initFluentElements(container); + initFluentElements(container, hookDefinitions); + initChildrenContainers(container, hookDefinitions); // Default Selenium WebElement injection. PageFactory.initElements(fluentControl.getDriver(), container); @@ -98,7 +115,7 @@ private static boolean isClassSupported(Class cls) { return cls != Object.class && cls != null; } - private void initChildrenContainers(Object container) { + private void initChildrenContainers(Object container, List> hookDefinitions) { for (Class cls = container.getClass(); isClassSupported(cls); cls = cls.getSuperclass()) { for (Field field : cls.getDeclaredFields()) { if (isContainer(field)) { @@ -119,30 +136,114 @@ private void initChildrenContainers(Object container) { throw new FluentInjectException("Can't set field " + field + " with value " + childContainer, e); } pageInstances.putIfAbsent(fieldClass, childContainer); - inject(childContainer); + inject(childContainer, hookDefinitions); } } } } } - private void initFluentElements(Object container) { + private void initFluentElements(Object container, List> hookDefinitions) { for (Class cls = container.getClass(); isClassSupported(cls); cls = cls.getSuperclass()) { - for (Field fieldFromPage : cls.getDeclaredFields()) { - if (isSupported(container, fieldFromPage)) { - AjaxElement annotation = fieldFromPage.getAnnotation(AjaxElement.class); + addHookDefinitions(cls.getDeclaredAnnotations(), hookDefinitions); + + for (Field field : cls.getDeclaredFields()) { + if (isSupported(container, field)) { + ArrayList> fieldHookDefinitions = new ArrayList<>(hookDefinitions); + addHookDefinitions(field.getAnnotations(), fieldHookDefinitions); + + AjaxElement annotation = field.getAnnotation(AjaxElement.class); ElementLocatorFactory locatorFactory; if (annotation == null) { locatorFactory = new DefaultElementLocatorFactory(this.fluentControl.getDriver()); } else { locatorFactory = new ConfigurableAjaxElementLocatorFactory(this.fluentControl.getDriver(), annotation); } - initFieldElements(locatorFactory, container, fieldFromPage); + + initFieldElements(locatorFactory, fieldHookDefinitions, container, field); + } + } + } + + pageHookDefinitions.put(container, new ArrayList<>(hookDefinitions)); + } + + private Hook getHookAnnotation(Annotation annotation) { + if (annotation instanceof Hook) { + return (Hook) annotation; + } else if (annotation.getClass().isAnnotationPresent(Hook.class)) { + return annotation.getClass().getAnnotation(Hook.class); + } + return null; + } + + private HookOptions getHookOptionsAnnotation(Annotation annotation) { + if (annotation instanceof HookOptions) { + return (HookOptions) annotation; + } else if (annotation.getClass().isAnnotationPresent(HookOptions.class)) { + return annotation.getClass().getAnnotation(HookOptions.class); + } + return null; + } + + private void addHookDefinitions(Annotation[] annotations, List> hookDefinitions) { + Hook currentHookAnnotation = null; + HookOptions currentHookOptionAnnotation = null; + + for (Annotation annotation : annotations) { + if (annotation instanceof NoHook) { + hookDefinitions.clear(); + break; + } + } + + for (Annotation annotation : annotations) { + Hook hookAnnotation = getHookAnnotation(annotation); + if (hookAnnotation != null && currentHookAnnotation != null) { + hookDefinitions.add(buildHookDefinition(currentHookAnnotation, currentHookOptionAnnotation)); + currentHookAnnotation = null; + currentHookOptionAnnotation = null; + + } + if (hookAnnotation != null) { + currentHookAnnotation = hookAnnotation; + } else { + HookOptions hookOptionsAnnotation = getHookOptionsAnnotation(annotation); + if (hookOptionsAnnotation != null) { + if (currentHookOptionAnnotation != null) { + throw new FluentInjectException("Unexpected @HookOptions annotation. @Hook is missing."); + } + currentHookOptionAnnotation = hookOptionsAnnotation; } } } + + if (currentHookAnnotation != null) { + hookDefinitions.add(buildHookDefinition(currentHookAnnotation, currentHookOptionAnnotation)); + } } + private HookDefinition buildHookDefinition(Hook hookAnnotation, HookOptions hookOptionsAnnotation) { + Class> hookClass = (Class>) hookAnnotation.value(); + Class hookOptionsClass = hookOptionsAnnotation == null ? null : (Class) hookOptionsAnnotation.value(); + T fluentHookOptions = null; + if (hookOptionsClass != null) { + try { + fluentHookOptions = ReflectionUtils.newInstanceOptionalArgs(hookOptionsClass, hookOptionsAnnotation); + } catch (NoSuchMethodException e) { + throw new FluentInjectException("@HookOption class has no valid constructor", e); + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new FluentInjectException("Can't create @HookOption class instance", e); + } + } + if (fluentHookOptions != null) { + return new HookDefinition<>(hookClass, fluentHookOptions); + } else { + return new HookDefinition<>(hookClass); + } + } + + private boolean isSupported(Object container, Field field) { return isValueNull(container, field) && !field.isAnnotationPresent(NoInject.class) && !Modifier.isFinal(field.getModifiers()) && (isListOfFluentWebElement(field) || isList(field) || isComponent(field)); } @@ -184,7 +285,7 @@ private static boolean isList(Field field) { return List.class.isAssignableFrom(field.getType()); } - private Object initFieldElements(ElementLocatorFactory factory, Object container, Field field) { + private Object initFieldElements(ElementLocatorFactory factory, List> hookDefinitions, Object container, Field field) { ElementLocator locator = factory.createLocator(field); if (locator == null) { return null; @@ -192,11 +293,11 @@ private Object initFieldElements(ElementLocatorFactory factory, Object container try { if (isListOfFluentWebElement(field)) { - return initFieldAsListOfFluentWebElement(locator, container, field); + return initFieldAsListOfFluentWebElement(locator, container, field, hookDefinitions); } else if (isList(field)) { - return initFieldAsList(locator, container, field); + return initFieldAsList(locator, container, field, hookDefinitions); } else if (isComponent(field)) { - return initFieldAsElement(locator, container, field); + return initFieldAsElement(locator, container, field, hookDefinitions); } } catch (IllegalAccessException e) { throw new FluentInjectException("Unable to find an accessible constructor with an argument of type WebElement in " + field.getType(), e); @@ -204,20 +305,32 @@ private Object initFieldElements(ElementLocatorFactory factory, Object container return null; } - private Object initFieldAsElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException { - Object proxy = Proxies.createComponent(locator, field.getType(), componentsManager); + private Object initFieldAsElement(ElementLocator locator, Object container, Field field, List> hookDefinitions) throws IllegalAccessException { + Object proxy; + if (hookDefinitions.size() > 0) { + proxy = LocatorProxies.createComponent(locator, field.getType(), componentsManager, hookChainBuilder, hookDefinitions); + } else { + proxy = LocatorProxies.createComponent(locator, field.getType(), componentsManager); + } ReflectionUtils.set(field, container, proxy); return proxy; } - private List initFieldAsList(ElementLocator locator, Object container, Field field) throws IllegalAccessException { - List proxy = Proxies.createComponentList(locator, getFirstGenericType(field), componentsManager); + private List initFieldAsList(ElementLocator locator, Object container, Field field, List> hookDefinitions) throws IllegalAccessException { + List proxy = LocatorProxies.createComponentList(locator, getFirstGenericType(field), componentsManager, hookChainBuilder); + if (hookDefinitions.size() > 0) { + LocatorProxies.setHooks(hookChainBuilder, proxy, hookDefinitions); + } ReflectionUtils.set(field, container, proxy); return proxy; } - private FluentList initFieldAsListOfFluentWebElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException { - FluentList proxy = Proxies.createFluentList(locator, (Class) getFirstGenericType(field), componentsManager); + private FluentList initFieldAsListOfFluentWebElement(ElementLocator locator, Object container, Field field, List> hookDefinitions) throws IllegalAccessException { + FluentList proxy = LocatorProxies.createFluentList(locator, (Class) getFirstGenericType(field), componentsManager, hookChainBuilder); + if (hookDefinitions.size() > 0) { + HookChainBuilder hookChainBuilder = new DefaultHookChainBuilder(fluentControl.getDriver(), componentsManager.getInstantiator()); + LocatorProxies.setHooks(hookChainBuilder, proxy, hookDefinitions); + } ReflectionUtils.set(field, container, proxy); return proxy; } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractListHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractListHandler.java new file mode 100644 index 0000000000..bf98c3f5ba --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractListHandler.java @@ -0,0 +1,216 @@ +package org.fluentlenium.core.proxy; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.domain.FluentListImpl; +import org.fluentlenium.core.hook.FluentHook; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +abstract class AbstractListHandler> extends AbstractLocatorHandler implements InvocationHandler, LocatorHandler { + private static final Method EQUALS = getMethod(Object.class, "equals", Object.class); + private static final Method HASH_CODE = getMethod(Object.class, "hashCode"); + private static final Method TO_STRING = getMethod(Object.class, "toString"); + + private List hooks; + private L proxy; + + private static Method getMethod(Class declaringClass, String name, Class... types) { + try { + return declaringClass.getMethod(name, types); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } + } + + private L list; + + private final ElementLocator locator; + + private final Class componentClass; + + private final ComponentInstantiator instantiator; + + private HookChainBuilder hookChainBuilder; + private List> hookDefinitions; + + public AbstractListHandler(ElementLocator locator, + Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { + this.locator = locator; + this.componentClass = componentClass;; + this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; + } + + protected abstract L buildList(List elements); + + public Class getComponentClass() { + return componentClass; + } + + public ComponentInstantiator getInstantiator() { + return instantiator; + } + + public HookChainBuilder getHookChainBuilder() { + return hookChainBuilder; + } + + @Override + public ElementLocator getLocator() { + return locator; + } + + @Override + public ElementLocator getHookLocator() { + if (hooks != null && hooks.size() > 0) { + return hooks.get(hooks.size()-1); + } + return locator; + } + + @Override + public L getHookLocatorResult() { + return list; + } + + @Override + public boolean isLoaded() { + return list != null; + } + + @Override + public void reset() { + list = null; + } + + @Override + public void now() { + getLocatorResult(); + } + + @Override + public void setHooks(HookChainBuilder hookChainBuilder, List> hookDefinitions) { + this.hookChainBuilder = hookChainBuilder; + this.hookDefinitions = hookDefinitions; + + if (hookDefinitions == null || hookDefinitions.size() == 0) { + hooks = null; + } else { + hooks = hookChainBuilder.build(new Supplier() { + @Override + public WebElement get() { + return null; + } + }, new Supplier() { + @Override + public ElementLocator get() { + return locator; + } + }, hookDefinitions); + } + + if (list != null) { + for (T component : list) { + LocatorHandler handler = LocatorProxies.getLocatorHandler(component); + handler.setHooks(hookChainBuilder, hookDefinitions); + } + } + } + + protected Function getTransformer() { + if (hookDefinitions != null && hookDefinitions.size() > 0) { + return new Function() { + @Override + public T apply(WebElement input) { + return LocatorProxies.createComponent(input, componentClass, instantiator, hookChainBuilder, hookDefinitions); + } + }; + } else { + return new Function() { + @Override + public T apply(WebElement input) { + return LocatorProxies.createComponent(input, componentClass, instantiator); + } + }; + } + } + + @Override + public synchronized L getLocatorResult() { + if (list == null) { + fireProxyElementSearch(proxy, locator); + List elements = getHookLocator().findElements(); + for (WebElement element : elements) { + fireProxyElementFound(proxy, locator, element); + } + list = buildList(elements); + } + return list; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (list == null) { + if (TO_STRING.equals(method)) { + return "Proxy List for: " + locator; + } + if (EQUALS.equals(method)) { + return this.equals(LocatorProxies.getLocatorHandler(args[0])); + } + if (HASH_CODE.equals(method)) { + return FluentListHandler.class.hashCode() + locator.hashCode(); + } + + ListElementAccessor annotation = FluentListImpl.class.getMethod(method.getName(), method.getParameterTypes()).getAnnotation(ListElementAccessor.class); + if (annotation != null) { + if (annotation.first()) { + return LocatorProxies.createComponent(new FirstElementLocator(locator), componentClass, instantiator); + } else if (annotation.last()) { + return LocatorProxies.createComponent(new LastElementLocator(locator), componentClass, instantiator); + } else if (annotation.index()) { + return LocatorProxies.createComponent(new AtIndexElementLocator(locator, (int) args[0]), componentClass, instantiator); + } else { + throw new IllegalArgumentException("@ListElementAccessor should have first(), last() or index() defined"); + } + } + } + + getLocatorResult(); + try { + return method.invoke(list, args); + } catch (InvocationTargetException e) { + // Unwrap the underlying exception + throw e.getCause(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractListHandler that = (AbstractListHandler) o; + + return locator != null ? locator.equals(that.locator) : that.locator == null; + } + + @Override + public int hashCode() { + return locator != null ? locator.hashCode() : 0; + } + + + public void setProxy(L proxy) { + this.proxy = proxy; + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractLocatorHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractLocatorHandler.java new file mode 100644 index 0000000000..2f0a50303c --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/AbstractLocatorHandler.java @@ -0,0 +1,31 @@ +package org.fluentlenium.core.proxy; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractLocatorHandler { + private List listeners = new ArrayList<>(); + + public synchronized boolean addListener(ProxyElementListener listener) { + return listeners.add(listener); + } + + public synchronized boolean removeListener(ProxyElementListener listener) { + return listeners.remove(listener); + } + + protected void fireProxyElementFound(Object proxy, ElementLocator locator, WebElement element) { + for (ProxyElementListener listener : listeners) { + listener.proxyElementFound(proxy, locator, element); + } + } + + protected void fireProxyElementSearch(Object proxy, ElementLocator locator) { + for (ProxyElementListener listener : listeners) { + listener.proxyElementSearch(proxy, locator); + } + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ComponentHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ComponentHandler.java index a576e3d616..dccdab9c73 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ComponentHandler.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ComponentHandler.java @@ -1,5 +1,9 @@ package org.fluentlenium.core.proxy; +import com.google.common.base.Supplier; +import org.fluentlenium.core.hook.FluentHook; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; import org.openqa.selenium.WebElement; import org.openqa.selenium.internal.WrapsElement; import org.openqa.selenium.support.pagefactory.ElementLocator; @@ -11,17 +15,19 @@ import java.util.ArrayList; import java.util.List; -public class ComponentHandler implements InvocationHandler { +public class ComponentHandler extends AbstractLocatorHandler implements InvocationHandler, LocatorHandler { private static final Method EQUALS = getMethod(Object.class, "equals", Object.class); private static final Method HASH_CODE = getMethod(Object.class, "hashCode"); private static final Method TO_STRING = getMethod(Object.class, "toString"); private static final Method GET_WRAPPED_ELEMENT = getMethod(WrapsElement.class, "getWrappedElement"); + private List hooks; + private WebElement proxy; + private static Method getMethod(Class declaringClass, String name, Class... types) { try { return declaringClass.getMethod(name, types); - } - catch (NoSuchMethodException e) { + } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } } @@ -34,47 +40,62 @@ private static Method getMethod(Class declaringClass, String name, Class... t public ComponentHandler(ElementLocator locator) { this.locator = locator; + if (this.locator instanceof WrapsElement) { + this.element = ((WrapsElement) this.locator).getWrappedElement(); + } + } + + public void setProxy(WebElement proxy) { + this.proxy = proxy; } + @Override public ElementLocator getLocator() { return locator; } - public synchronized boolean addListener(ProxyElementListener listener) { - return listeners.add(listener); - } - public synchronized boolean removeListener(ProxyElementListener listener) { - return listeners.remove(listener); + @Override + public WebElement getLocatorResult() { + if (element == null) { + fireProxyElementSearch(proxy, locator); + element = getHookLocator().findElement(); + fireProxyElementFound(proxy, locator, element); + } + return element; } - protected void fireProxyElementFound(WebElement proxy, ElementLocator locator, WebElement element) { - for (ProxyElementListener listener : listeners) { - listener.proxyElementFound(proxy, locator, element); + @Override + public WebElement getHookLocatorResult() { + if (hooks != null && hooks.size() > 0) { + return hooks.get(hooks.size()-1); } + return element; } - protected void fireProxyElementSearch(WebElement proxy, ElementLocator locator) { - for (ProxyElementListener listener : listeners) { - listener.proxyElementSearch(proxy, locator); + @Override + public ElementLocator getHookLocator() { + if (hooks != null && hooks.size() > 0) { + return hooks.get(hooks.size()-1); } + return locator; } + @Override public synchronized boolean isLoaded() { return element != null; } - public synchronized void reset() { - this.element = null; + @Override + public void now() { + getLocatorResult(); } - public synchronized WebElement getOrFindElement(WebElement proxy) { - if (element == null) { - fireProxyElementSearch(proxy, locator); - element = locator.findElement(); - fireProxyElementFound(proxy, locator, element); + @Override + public synchronized void reset() { + if (this.locator != null) { + this.element = null; } - return element; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -86,9 +107,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return "Proxy Component for: " + locator; } if (EQUALS.equals(method)) { - ComponentHandler componentHandler = Proxies.getComponentHandler(args[0]); - if (componentHandler != null) { - return this.equals(componentHandler); + LocatorHandler locatorHandler = LocatorProxies.getLocatorHandler(args[0]); + if (locatorHandler != null) { + return this.equals(locatorHandler); } // Loading element if equals is called with a non ComponentHandler proxy parameter } @@ -97,9 +118,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } } - getOrFindElement((WebElement)proxy); + getLocatorResult(); - if (EQUALS.equals(method) && Proxies.getComponentHandler(args[0]) != null) { + if (EQUALS.equals(method) && LocatorProxies.getLocatorHandler(args[0]) != null) { return equalsInternal(proxy, args[0]); } @@ -109,7 +130,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } try { - return method.invoke(element, args); + return method.invoke(getHookLocatorResult(), args); } catch (InvocationTargetException e) { // Unwrap the underlying exception throw e.getCause(); @@ -129,7 +150,7 @@ private boolean equalsInternal(Object me, Object other) { // the proxies behave differently. return false; } - return ((ComponentHandler) handler).getOrFindElement((WebElement)me).equals(getOrFindElement((WebElement)other)); + return ((ComponentHandler) handler).getLocatorResult().equals(getLocatorResult()); } @Override @@ -140,7 +161,6 @@ public boolean equals(Object o) { ComponentHandler that = (ComponentHandler) o; return locator != null ? locator.equals(that.locator) : that.locator == null; - } @Override @@ -148,4 +168,21 @@ public int hashCode() { return locator != null ? locator.hashCode() : 0; } + public void setHooks(HookChainBuilder hookChainBuilder, List> hookDefinitions) { + if (hookDefinitions == null || hookDefinitions.size() == 0) { + hooks = null; + } else { + hooks = hookChainBuilder.build(new Supplier() { + @Override + public WebElement get() { + return element; + } + }, new Supplier() { + @Override + public ElementLocator get() { + return locator; + } + }, hookDefinitions); + } + } } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/FluentListHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/FluentListHandler.java index 2995301ef2..41a60fe8df 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/FluentListHandler.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/FluentListHandler.java @@ -1,109 +1,25 @@ package org.fluentlenium.core.proxy; -import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.domain.FluentList; import org.fluentlenium.core.domain.FluentListImpl; import org.fluentlenium.core.domain.FluentWebElement; -import org.openqa.selenium.NoSuchElementException; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.List; -class FluentListHandler implements InvocationHandler { - private static final Method EQUALS = getMethod(Object.class, "equals", Object.class); - private static final Method HASH_CODE = getMethod(Object.class, "hashCode"); - private static final Method TO_STRING = getMethod(Object.class, "toString"); - - private static Method getMethod(Class declaringClass, String name, Class... types) { - try { - return declaringClass.getMethod(name, types); - } - catch (NoSuchMethodException e) { - throw new IllegalArgumentException(e); - } - } - - private final ElementLocator locator; - - private FluentList list; - - private final Class componentClass; - - private final ComponentInstantiator instantiator; - - public FluentListHandler(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator) { - this.locator = locator; - this.componentClass = componentClass; - this.instantiator = instantiator; - } - - public synchronized FluentList getOrFindList(FluentList proxy) { - if (list == null) { - List elements = locator.findElements(); - list = new FluentListImpl(this.componentClass, instantiator, FluentIterable.from(elements).transform(new Function() { - - @Override - public T apply(WebElement input) { - return instantiator.newComponent(componentClass, input); - } - }).toList()); - } - return (FluentList) list; - } - - public ElementLocator getLocator() { - return locator; - } - - public void reset() { - list = null; - } - - public boolean isLoaded() { - return list != null; +class FluentListHandler extends AbstractListHandler> { + public FluentListHandler(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { + super(locator, componentClass, instantiator, hookChainBuilder); } @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (list == null) { - if (TO_STRING.equals(method)) { - return "Proxy FluentList for: " + locator; - } - if (EQUALS.equals(method)) { - return this.equals(Proxies.getComponentHandler(args[0])); - } - if (HASH_CODE.equals(method)) { - return FluentListHandler.class.hashCode() + locator.hashCode(); - } - - ListElementAccessor annotation = FluentListImpl.class.getMethod(method.getName(), method.getParameterTypes()).getAnnotation(ListElementAccessor.class); - if (annotation != null) { - if (annotation.first()) { - return Proxies.createComponent(new FirstElementLocator(locator), componentClass, instantiator); - } else if (annotation.last()) { - return Proxies.createComponent(new LastElementLocator(locator), componentClass, instantiator); - } else if (annotation.index()) { - return Proxies.createComponent(new AtIndexElementLocator(locator, (int)args[0]), componentClass, instantiator); - } else { - throw new IllegalArgumentException("@ListElementAccessor should have first(), last() or index() defined"); - } - } - } - - getOrFindList((FluentList) proxy); - try { - return method.invoke(list, args); - } catch (InvocationTargetException e) { - // Unwrap the underlying exception - throw e.getCause(); - } + protected FluentList buildList(List elements) { + return new FluentListImpl(getComponentClass(), getInstantiator(), getHookChainBuilder(), FluentIterable.from(elements).transform(getTransformer()).toList()); } - } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/InstanceElementLocator.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/InstanceElementLocator.java new file mode 100644 index 0000000000..8f37534b9f --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/InstanceElementLocator.java @@ -0,0 +1,31 @@ +package org.fluentlenium.core.proxy; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.WrapsElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.Arrays; +import java.util.List; + +public class InstanceElementLocator implements ElementLocator, WrapsElement { + private final WebElement element; + + public InstanceElementLocator(WebElement element) { + this.element = element; + } + + @Override + public WebElement findElement() { + return this.element; + } + + @Override + public List findElements() { + return Arrays.asList(findElement()); + } + + @Override + public WebElement getWrappedElement() { + return this.element; + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ListHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ListHandler.java index 53ee19750c..61cf90f156 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ListHandler.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ListHandler.java @@ -1,51 +1,21 @@ package org.fluentlenium.core.proxy; -import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.hook.HookChainBuilder; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.pagefactory.ElementLocator; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -class ListHandler implements InvocationHandler { - private final ElementLocator elementLocator; - - private List list; - - private final Class componentClass; - - private final ComponentInstantiator instantiator; - - public ListHandler(ElementLocator elementLocator, - Class componentClass, ComponentInstantiator instantiator) { - this.elementLocator = elementLocator; - this.componentClass = componentClass; - this.instantiator = instantiator; +public class ListHandler extends AbstractListHandler> { + public ListHandler(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { + super(locator, componentClass, instantiator, hookChainBuilder); } @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - synchronized (this) { - if (list == null) { - List elements = elementLocator.findElements(); - list = new ArrayList(FluentIterable.from(elements).transform(new Function() { - @Override - public T apply(WebElement input) { - return instantiator.newComponent(componentClass, input); - } - }).toList()); - } - } - try { - return method.invoke(list, args); - } catch (InvocationTargetException e) { - // Unwrap the underlying exception - throw e.getCause(); - } + protected List buildList(List elements) { + return new ArrayList(FluentIterable.from(elements).transform(getTransformer()).toList()); } } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorHandler.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorHandler.java new file mode 100644 index 0000000000..9d34d6e14f --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorHandler.java @@ -0,0 +1,58 @@ +package org.fluentlenium.core.proxy; + +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.List; + +public interface LocatorHandler { + /** + * Locator used by this proxy, without any hook applied. + * + * @return + */ + ElementLocator getLocator(); + + /** + * Result retrieved by the locator, without any hook applied. + * @return + */ + T getLocatorResult(); + + /** + * Locator used by this proxy, with all hooks applied. + * @return + */ + ElementLocator getHookLocator(); + + /** + * Result retrieved by the locator, with all hooks applied. + * @return + */ + T getHookLocatorResult(); + + /** + * Set hooks of this locator handler. + * @param hookChainBuilder + * @param hookDefinitions + */ + void setHooks(HookChainBuilder hookChainBuilder, List> hookDefinitions); + + /** + * Check if this handler has loaded it's result. + * @return + */ + boolean isLoaded(); + + /** + * Reset the loaded data. + */ + void reset(); + + /** + * Force loading of the results. + */ + void now(); + +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorProxies.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorProxies.java new file mode 100644 index 0000000000..0a9f759757 --- /dev/null +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/LocatorProxies.java @@ -0,0 +1,140 @@ +package org.fluentlenium.core.proxy; + +import lombok.experimental.UtilityClass; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.domain.FluentList; +import org.fluentlenium.core.domain.FluentWebElement; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.hook.HookDefinition; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.internal.Locatable; +import org.openqa.selenium.internal.WrapsElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.List; + +/** + * Utility class to create proxies of WebElement, Component, FluentList and List of Components based on their locators. + */ +@UtilityClass +public class LocatorProxies { + public LocatorHandler getLocatorHandler(Object proxy) { + if (Proxy.isProxyClass(proxy.getClass())) { + InvocationHandler proxyHandler = Proxy.getInvocationHandler(proxy); + if (proxyHandler instanceof LocatorHandler) { + return ((LocatorHandler) proxyHandler); + } + } + return null; + } + + public T getLocatorResult(T proxy) { + LocatorHandler componentHandler = getLocatorHandler(proxy); + if (componentHandler != null) { + return (T) componentHandler.getLocatorResult(); + } + return proxy; + } + + public void reset(Object proxy) { + LocatorHandler handler = getLocatorHandler(proxy); + if (handler != null) { + handler.reset(); + } + } + + public boolean isPresent(Object proxy) { + try { + now(proxy); + return true; + } catch (NoSuchElementException | StaleElementReferenceException e) { + return false; + } + } + + public static void now(Object proxy) { + LocatorHandler handler = getLocatorHandler(proxy); + if (handler != null) { + handler.now(); + } + } + + public boolean isLoaded(Object proxy) { + LocatorHandler handler = getLocatorHandler(proxy); + if (handler != null) { + return handler.isLoaded(); + } + return true; + } + + public boolean addProxyListener(WebElement proxy, ProxyElementListener listener) { + if (Proxy.isProxyClass(proxy.getClass())) { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); + if (invocationHandler instanceof ComponentHandler) { + return ((ComponentHandler) invocationHandler).addListener(listener); + } + } + return false; + } + + public boolean removeProxyListener(WebElement proxy, ProxyElementListener listener) { + if (Proxy.isProxyClass(proxy.getClass())) { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); + if (invocationHandler instanceof ComponentHandler) { + return ((ComponentHandler) invocationHandler).removeListener(listener); + } + } + return false; + } + + public WebElement createWebElement(ElementLocator locator) { + final ComponentHandler handler = new ComponentHandler(locator); + WebElement proxy = (WebElement) Proxy.newProxyInstance(locator.getClass().getClassLoader(), new Class[]{WebElement.class, Locatable.class, WrapsElement.class}, handler); + handler.setProxy(proxy); + return proxy; + } + + public T createComponent(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator) { + return instantiator.newComponent(componentClass, createWebElement(locator)); + } + + public T createComponent(WebElement element, Class componentClass, ComponentInstantiator instantiator) { + return createComponent(new InstanceElementLocator(element), componentClass, instantiator); + } + + public static T createComponent(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, List> hookDefinitions) { + WebElement proxy = createWebElement(locator); + setHooks(hookChainBuilder, proxy, hookDefinitions); + return instantiator.newComponent(componentClass, proxy); + } + + public static T createComponent(WebElement element, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder, List> hookDefinitions) { + return createComponent(new InstanceElementLocator(element), componentClass, instantiator, hookChainBuilder, hookDefinitions); + } + + public FluentList createFluentList(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { + final FluentListHandler handler = new FluentListHandler(locator, componentClass, instantiator, hookChainBuilder); + FluentList proxy = (FluentList) Proxy.newProxyInstance( + locator.getClass().getClassLoader(), new Class[]{FluentList.class}, handler); + handler.setProxy(proxy); + return proxy; + } + + public List createComponentList(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { + final ListHandler handler = new ListHandler(locator, componentClass, instantiator, hookChainBuilder); + List proxy = (List) Proxy.newProxyInstance( + locator.getClass().getClassLoader(), new Class[]{List.class}, handler); + handler.setProxy(proxy); + return proxy; + } + + public void setHooks(HookChainBuilder hookChainBuilder, Object element, List> hookDefinitions) { + LocatorHandler componentHandler = getLocatorHandler(element); + componentHandler.setHooks(hookChainBuilder, hookDefinitions); + } +} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/Proxies.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/Proxies.java deleted file mode 100644 index 94e8c43039..0000000000 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/Proxies.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.fluentlenium.core.proxy; - -import lombok.experimental.UtilityClass; -import org.fluentlenium.core.components.ComponentInstantiator; -import org.fluentlenium.core.domain.FluentList; -import org.fluentlenium.core.domain.FluentWebElement; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.StaleElementReferenceException; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.internal.Locatable; -import org.openqa.selenium.internal.WrapsElement; -import org.openqa.selenium.support.pagefactory.ElementLocator; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.List; - -/** - * Utility class to create proxies of WebElement, Component, FluentList and List of Components. - */ -@UtilityClass -public class Proxies { - public ComponentHandler getComponentHandler(Object proxy) { - if (Proxy.isProxyClass(proxy.getClass())) { - InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); - if (invocationHandler instanceof ComponentHandler) { - return ((ComponentHandler)invocationHandler); - } - } - return null; - } - - - private FluentListHandler getFluentListHandler(FluentList proxy) { - if (Proxy.isProxyClass(proxy.getClass())) { - InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); - if (invocationHandler instanceof FluentListHandler) { - return ((FluentListHandler)invocationHandler); - } - } - return null; - } - - public WebElement getElement(WebElement proxy) { - ComponentHandler componentHandler = getComponentHandler(proxy); - if (componentHandler != null) { - return componentHandler.getOrFindElement(proxy); - } - return proxy; - } - - public FluentList getList(FluentList proxy) { - FluentListHandler fluentListHandler = getFluentListHandler(proxy); - if (fluentListHandler != null) { - return fluentListHandler.getOrFindList(proxy); - } - return proxy; - } - - public ElementLocator getLocator(WebElement proxy) { - ComponentHandler componentHandler = getComponentHandler(proxy); - if (componentHandler != null) { - return componentHandler.getLocator(); - } - return null; - } - - public ElementLocator getLocator(FluentList proxy) { - FluentListHandler fluentListHandler = getFluentListHandler(proxy); - if (fluentListHandler != null) { - return fluentListHandler.getLocator(); - } - return null; - } - - public void reset(WebElement proxy) { - ComponentHandler componentHandler = getComponentHandler(proxy); - if (componentHandler != null) { - componentHandler.reset(); - } - } - - public void reset(FluentList proxy) { - FluentListHandler componentHandler = getFluentListHandler(proxy); - if (componentHandler != null) { - componentHandler.reset(); - } - } - - public boolean isPresent(WebElement proxy) { - try { - now(proxy); - return true; - } catch (NoSuchElementException | StaleElementReferenceException e) { - return false; - } - } - - public boolean isPresent(FluentList proxy) { - try { - now(proxy); - return true; - } catch (NoSuchElementException | StaleElementReferenceException e) { - return false; - } - } - - public static void now(WebElement proxy) { - ComponentHandler componentHandler = getComponentHandler(proxy); - if (componentHandler != null) { - componentHandler.getOrFindElement(proxy); - } - } - - - public void now(FluentList listProxy) { - FluentListHandler fluentListHandler = getFluentListHandler(listProxy); - if (fluentListHandler != null) { - fluentListHandler.getOrFindList(listProxy); - } - } - - public boolean isLoaded(WebElement proxy) { - ComponentHandler componentHandler = getComponentHandler(proxy); - if (componentHandler != null) { - return componentHandler.isLoaded(); - } - return true; - } - - public boolean isLoaded(FluentList proxy) { - FluentListHandler fluentListHandler = getFluentListHandler(proxy); - if (fluentListHandler != null) { - return fluentListHandler.isLoaded(); - } - return true; - } - - public boolean addProxyListener(WebElement proxy, ProxyElementListener listener) { - if (Proxy.isProxyClass(proxy.getClass())) { - InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); - if (invocationHandler instanceof ComponentHandler) { - return ((ComponentHandler)invocationHandler).addListener(listener); - } - } - return false; - } - - public boolean removeProxyListener(WebElement proxy, ProxyElementListener listener) { - if (Proxy.isProxyClass(proxy.getClass())) { - InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); - if (invocationHandler instanceof ComponentHandler) { - return ((ComponentHandler)invocationHandler).removeListener(listener); - } - } - return false; - } - - public WebElement createWebElement(ElementLocator locator) { - final InvocationHandler handler = new ComponentHandler(locator); - WebElement proxy = (WebElement) Proxy.newProxyInstance(locator.getClass().getClassLoader(), new Class[]{WebElement.class, Locatable.class, WrapsElement.class}, handler); - return proxy; - } - - public T createComponent(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator) { - return instantiator.newComponent(componentClass, createWebElement(locator)); - } - - public FluentList createFluentList(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator) { - final InvocationHandler handler = new FluentListHandler(locator, componentClass, instantiator); - return (FluentList) Proxy.newProxyInstance( - locator.getClass().getClassLoader(), new Class[]{FluentList.class}, handler); - } - - public List createComponentList(ElementLocator locator, Class componentClass, ComponentInstantiator instantiator) { - final InvocationHandler handler = new ListHandler(locator, componentClass, instantiator); - return (List) Proxy.newProxyInstance( - locator.getClass().getClassLoader(), new Class[]{List.class}, handler); - } - -} diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ProxyElementListener.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ProxyElementListener.java index d786ca6bfa..bc91f7a81e 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ProxyElementListener.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/proxy/ProxyElementListener.java @@ -4,6 +4,6 @@ import org.openqa.selenium.support.pagefactory.ElementLocator; public interface ProxyElementListener { - void proxyElementSearch(WebElement proxy, ElementLocator locator); - void proxyElementFound(WebElement proxy, ElementLocator locator, WebElement element); + void proxyElementSearch(Object proxy, ElementLocator locator); + void proxyElementFound(Object proxy, ElementLocator locator, WebElement element); } diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/search/Search.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/search/Search.java index d2e2a0ba78..0c91b49f03 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/search/Search.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/search/Search.java @@ -7,7 +7,8 @@ import org.fluentlenium.core.domain.FluentWebElement; import org.fluentlenium.core.filter.Filter; import org.fluentlenium.core.filter.FilterPredicate; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.hook.HookChainBuilder; +import org.fluentlenium.core.proxy.LocatorProxies; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.SearchContext; @@ -21,17 +22,27 @@ public class Search implements SearchControl { private final SearchContext searchContext; private final ComponentInstantiator instantiator; + private final HookChainBuilder hookChainBuilder; - public Search(SearchContext context, ComponentInstantiator instantiator) { + public Search(SearchContext context, ComponentInstantiator instantiator, HookChainBuilder hookChainBuilder) { this.searchContext = context; this.instantiator = instantiator; + this.hookChainBuilder = hookChainBuilder; + } + + public HookChainBuilder getHookChainBuilder() { + return hookChainBuilder; + } + + public ComponentInstantiator getInstantiator() { + return instantiator; } /** * Central methods to find elements on the page. Can provide some filters. Able to use css1, css2, css3, see WebDriver restrictions * - * @param selector elements name to find - * @param filters filters set + * @param selector elements name to find + * @param filters filters set * @return fluent list of fluent web elements */ @Override @@ -55,7 +66,7 @@ public FluentList find(String selector, final Filter... filter postFiltered = Collections2.filter(select, new FilterPredicate(filter)); } - return new FluentListImpl<>(FluentWebElement.class, instantiator, postFiltered); + return new FluentListImpl<>(FluentWebElement.class, instantiator, hookChainBuilder, postFiltered); } @@ -78,7 +89,7 @@ private FluentList selectList(final String cssSelector) { } private FluentList selectList(By locator) { - return Proxies.createFluentList(locator(locator), FluentWebElement.class, instantiator); + return LocatorProxies.createFluentList(locator(locator), FluentWebElement.class, instantiator, hookChainBuilder); } /** @@ -138,7 +149,7 @@ public FluentList find(By locator, final Filter... filters) { postFiltered = Collections2.filter(postFiltered, new FilterPredicate(selector)); } - return new FluentListImpl<>(FluentWebElement.class, instantiator, postFiltered); + return new FluentListImpl<>(FluentWebElement.class, instantiator, hookChainBuilder, postFiltered); } @Override diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementListMatcher.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementListMatcher.java index dee8f4cc11..953f277e68 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementListMatcher.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementListMatcher.java @@ -32,7 +32,7 @@ public FluentWaitElementListMatcher not() { @Override protected FluentList find() { - FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, null); + FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, search.getInstantiator(), search.getHookChainBuilder()); if (untilElements != null) { try { for (FluentWebElement untilElement : untilElements) { diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementMatcher.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementMatcher.java index 9b7b27a547..74685e622c 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementMatcher.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitElementMatcher.java @@ -30,7 +30,7 @@ public FluentWaitElementMatcher not() { @Override protected FluentList find() { - FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, null); + FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, search.getInstantiator(), search.getHookChainBuilder()); if (untilElement != null) { try { elements.add(untilElement.now()); diff --git a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitSupplierMatcher.java b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitSupplierMatcher.java index fc96849caf..476df4a628 100644 --- a/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitSupplierMatcher.java +++ b/fluentlenium-core/src/main/java/org/fluentlenium/core/wait/FluentWaitSupplierMatcher.java @@ -32,7 +32,7 @@ public FluentWaitSupplierMatcher not() { protected FluentList find() { try { FluentWebElement fluentWebElement = selector.get().now(); - FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, null); + FluentListImpl elements = new FluentListImpl<>(FluentWebElement.class, search.getInstantiator(), search.getHookChainBuilder()); if (fluentWebElement != null) { elements.add(fluentWebElement); } diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/adapter/IsolatedTestTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/adapter/IsolatedTestTest.java index 6864ac0aea..d3a40c3915 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/adapter/IsolatedTestTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/adapter/IsolatedTestTest.java @@ -5,7 +5,7 @@ import org.fluentlenium.core.FluentPage; import org.fluentlenium.core.annotation.Page; import org.fluentlenium.core.domain.FluentWebElement; -import org.fluentlenium.core.proxy.Proxies; +import org.fluentlenium.core.proxy.LocatorProxies; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -62,8 +62,8 @@ public WebDriver newWebDriver() { } public void testSomething() { - Assertions.assertThat(Proxies.getElement(element.now().getElement())).isSameAs(IsolatedTestTest.this.element); - Assertions.assertThat(Proxies.getElement(page.pageElement.now().getElement())).isSameAs(IsolatedTestTest.this.pageElement); + Assertions.assertThat(LocatorProxies.getLocatorResult(element.now().getElement())).isSameAs(IsolatedTestTest.this.element); + Assertions.assertThat(LocatorProxies.getLocatorResult(page.pageElement.now().getElement())).isSameAs(IsolatedTestTest.this.pageElement); } } diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/axes/AxesTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/axes/AxesTest.java index 120bf20258..3be2deedae 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/core/axes/AxesTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/axes/AxesTest.java @@ -4,6 +4,7 @@ import org.fluentlenium.core.FluentDriver; import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -27,16 +28,19 @@ public class AxesTest { private WebDriver driver; private ComponentInstantiator instantiator; + + private DefaultHookChainBuilder hookChainBuilder; @Before public void before() { MockitoAnnotations.initMocks(this); this.instantiator = new DefaultComponentInstantiator(driver); + this.hookChainBuilder = new DefaultHookChainBuilder(driver, instantiator); } @Test public void testAncestors() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("ancestor::*"))).thenReturn(elements); @@ -46,7 +50,7 @@ public void testAncestors() { @Test public void testDescendants() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("descendant::*"))).thenReturn(elements); @@ -56,7 +60,7 @@ public void testDescendants() { @Test public void testFollowings() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("following::*"))).thenReturn(elements); @@ -66,7 +70,7 @@ public void testFollowings() { @Test public void testFollowingSiblings() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("following-sibling::*"))).thenReturn(elements); @@ -76,7 +80,7 @@ public void testFollowingSiblings() { @Test public void testPrecedings() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("preceding::*"))).thenReturn(elements); @@ -86,7 +90,7 @@ public void testPrecedings() { @Test public void testPrecedingSiblings() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); List elements = Arrays.asList(mock(WebElement.class), mock(WebElement.class), mock(WebElement.class)); when(element.findElements(By.xpath("preceding-sibling::*"))).thenReturn(elements); @@ -96,7 +100,7 @@ public void testPrecedingSiblings() { @Test public void testParent() { - Axes axes = new Axes(element, instantiator); + Axes axes = new Axes(element, instantiator, hookChainBuilder); WebElement parent = mock(WebElement.class); when(element.findElement(By.xpath("parent::*"))).thenReturn(parent); diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/domain/FluentListImplTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/domain/FluentListImplTest.java index 393e6d95d9..72b0a16755 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/core/domain/FluentListImplTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/domain/FluentListImplTest.java @@ -4,6 +4,7 @@ import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.components.DefaultComponentInstantiator; import org.fluentlenium.core.conditions.WebElementConditions; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -88,7 +89,10 @@ public void testFromToElements() { WebElement webElement2 = mock(WebElement.class); WebElement webElement3 = mock(WebElement.class); - FluentListImpl list = FluentListImpl.fromElements(new DefaultComponentInstantiator(driver), webElement1, webElement2, webElement3); + DefaultComponentInstantiator instantiator = new DefaultComponentInstantiator(driver); + DefaultHookChainBuilder hookChainBuilder = new DefaultHookChainBuilder(driver, instantiator); + + FluentListImpl list = FluentListImpl.fromElements(instantiator, hookChainBuilder, webElement1, webElement2, webElement3); assertThat(list.toElements()).containsExactly(webElement1, webElement2, webElement3); } diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseFluentHookTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseFluentHookTest.java new file mode 100644 index 0000000000..70feda6876 --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseFluentHookTest.java @@ -0,0 +1,57 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Suppliers; +import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.fluentlenium.core.domain.FluentWebElement; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.annotation.Annotation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class BaseFluentHookTest { + @Mock + private WebDriver webDriver; + + @Mock + private WebElement element; + + @Mock + private ElementLocator locator; + + @Mock + private Object options; + + private DefaultComponentInstantiator instantiator; + + private BaseFluentHook hook; + + @Before + public void before() { + instantiator = spy(new DefaultComponentInstantiator(webDriver)); + hook = new BaseFluentHook<>(webDriver, instantiator, Suppliers.ofInstance(element), Suppliers.ofInstance(locator), options); + } + + @Test + public void testFluentWebElement() { + FluentWebElement fluentWebElement = hook.getFluentWebElement(); + verify(instantiator).newComponent(FluentWebElement.class, element); + + assertThat(fluentWebElement).isInstanceOf(FluentWebElement.class); + assertThat(fluentWebElement.getElement()).isSameAs(element); + } + +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseHookTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseHookTest.java new file mode 100644 index 0000000000..17d56e6a33 --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/BaseHookTest.java @@ -0,0 +1,93 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Suppliers; +import org.assertj.core.api.Assertions; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.annotation.Annotation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class BaseHookTest { + @Mock + private WebDriver webDriver; + + @Mock + private WebElement element; + + @Mock + private ElementLocator locator; + + @Mock + private Object options; + + private DefaultComponentInstantiator instantiator; + + private BaseHook hook; + + @Before + public void before() { + instantiator = new DefaultComponentInstantiator(webDriver); + hook = new BaseHook<>(webDriver, instantiator, Suppliers.ofInstance(element), Suppliers.ofInstance(locator), options); + } + + @Test + public void testDelegatesElement() { + hook.click(); + verify(element).click(); + } + + @Test + public void testDelegatesLocator() { + hook.findElement(); + verify(element, never()).findElement(any(By.class)); + verify(locator).findElement(); + } + + @Test + public void testGetters() { + assertThat(hook.getWebDriver()).isSameAs(webDriver); + assertThat(hook.getInstantiator()).isSameAs(instantiator); + assertThat(hook.getElement()).isSameAs(element); + assertThat(hook.getElementLocator()).isSameAs(locator); + assertThat(hook.getOptions()).isSameAs(options); + } + + @Test + public void testNoOptionHook() { + final Object defaultOptions = new Object(); + + BaseHook noOptionHook = new BaseHook(webDriver, instantiator, Suppliers.ofInstance(element), Suppliers.ofInstance(locator), null) { + @Override + protected Object newOptions() { + return defaultOptions; + } + }; + + assertThat(noOptionHook.getOptions()).isSameAs(defaultOptions); + } + + + @Test + public void testNoOptionHookWithoutDefault() { + + BaseHook noOptionHook = new BaseHook<>(webDriver, instantiator, Suppliers.ofInstance(element), Suppliers.ofInstance(locator), null); + + assertThat(noOptionHook.getOptions()).isNull(); + } +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/HookChainBuilderTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/HookChainBuilderTest.java new file mode 100644 index 0000000000..3043d3ef5e --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/HookChainBuilderTest.java @@ -0,0 +1,95 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import org.assertj.core.api.Assertions; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class HookChainBuilderTest { + @Mock + private WebElement element; + + @Mock + private ElementLocator locator; + + @Mock + private WebDriver webDriver; + + + private ComponentInstantiator instantiator; + + private DefaultHookChainBuilder hookChainBuilder; + + @Before + public void before() { + instantiator = new DefaultComponentInstantiator(webDriver); + hookChainBuilder = new DefaultHookChainBuilder(webDriver, instantiator) { + @Override + protected FluentHook newInstance(Class> hookClass, WebDriver webDriver, ComponentInstantiator instantiator, Supplier currentSupplier, Supplier locator, Object options) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + return spy(super.newInstance(hookClass, webDriver, instantiator, currentSupplier, locator, options)); + } + }; + } + + @Test + public void testBuildHook() { + List> hookDefinitions = new ArrayList<>(); + + hookDefinitions.add(new HookDefinition<>(NanoHook.class)); + hookDefinitions.add(new HookDefinition<>(NanoHook.class, new NanoHookOptions("option"))); + hookDefinitions.add(new HookDefinition<>(NanoHook.class)); + + List fluentHooks = hookChainBuilder.build(Suppliers.ofInstance(element), Suppliers.ofInstance(locator), hookDefinitions); + + Assertions.assertThat(fluentHooks).hasSize(hookDefinitions.size()); + + Assertions.assertThat(fluentHooks.get(0)).isInstanceOf(NanoHook.class); + Assertions.assertThat(fluentHooks.get(1)).isInstanceOf(NanoHook.class); + Assertions.assertThat(fluentHooks.get(2)).isInstanceOf(NanoHook.class); + + fluentHooks.get(0).click(); + + verify(element).click(); + verify(fluentHooks.get(0)).click(); + verify(fluentHooks.get(1), never()).click(); + verify(fluentHooks.get(2), never()).click(); + + reset(element); + reset(fluentHooks.toArray()); + + fluentHooks.get(2).click(); + + verify(element).click(); + verify(fluentHooks.get(0)).click(); + verify(fluentHooks.get(1)).click(); + verify(fluentHooks.get(2)).click(); + + Assertions.assertThat(((NanoHook) fluentHooks.get(2)).getBeforeClickNano()).isLessThanOrEqualTo(((NanoHook) fluentHooks.get(1)).getBeforeClickNano()); + Assertions.assertThat(((NanoHook) fluentHooks.get(1)).getBeforeClickNano()).isLessThanOrEqualTo(((NanoHook) fluentHooks.get(0)).getBeforeClickNano()); + Assertions.assertThat(((NanoHook) fluentHooks.get(2)).getAfterClickNano()).isGreaterThanOrEqualTo(((NanoHook) fluentHooks.get(1)).getAfterClickNano()); + Assertions.assertThat(((NanoHook) fluentHooks.get(1)).getAfterClickNano()).isGreaterThanOrEqualTo(((NanoHook) fluentHooks.get(0)).getAfterClickNano()); + + Assertions.assertThat(((NanoHook) fluentHooks.get(0)).getOptionValue()).isNull(); + Assertions.assertThat(((NanoHook) fluentHooks.get(1)).getOptionValue()).isEqualTo("option"); + Assertions.assertThat(((NanoHook) fluentHooks.get(2)).getOptionValue()).isNull(); + } +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHook.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHook.java new file mode 100644 index 0000000000..4deebd4bb2 --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHook.java @@ -0,0 +1,88 @@ +package org.fluentlenium.core.hook; + +import com.google.common.base.Supplier; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.List; + +public class NanoHook extends BaseHook { + private long beforeClickNano; + private long afterClickNano; + private String optionValue; + + private long beforeFindElementsNano; + private long afterFindElementsNano; + + private long beforeFindElementNano; + private long afterFindElementNano; + + public NanoHook(WebDriver webDriver, ComponentInstantiator instantiator, Supplier elementSupplier, Supplier locatorSupplier, NanoHookOptions options) { + super(webDriver, instantiator, elementSupplier, locatorSupplier, options); + } + + @Override + protected NanoHookOptions newOptions() { + return new NanoHookOptions(); + } + + public long getBeforeClickNano() { + return beforeClickNano; + } + + public long getAfterClickNano() { + return afterClickNano; + } + + public long getBeforeFindElementNano() { + return beforeFindElementNano; + } + + public long getAfterFindElementNano() { + return afterFindElementNano; + } + + public long getBeforeFindElementsNano() { + return beforeFindElementsNano; + } + + public long getAfterFindElementsNano() { + return afterFindElementsNano; + } + + public String getOptionValue() { + return optionValue; + } + + @Override + public void click() { + beforeClickNano = System.nanoTime(); + if (getOptions().getValue() != null) { + optionValue = getOptions().getValue(); + } + super.click(); + afterClickNano = System.nanoTime(); + } + + @Override + public List findElements() { + beforeFindElementsNano = System.nanoTime(); + try { + return super.findElements(); + } finally { + afterFindElementsNano = System.nanoTime(); + } + } + + @Override + public WebElement findElement() { + beforeFindElementNano = System.nanoTime(); + try { + return super.findElement(); + } finally { + afterFindElementNano = System.nanoTime(); + } + } +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHookOptions.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHookOptions.java new file mode 100644 index 0000000000..555d0a297a --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/NanoHookOptions.java @@ -0,0 +1,14 @@ +package org.fluentlenium.core.hook; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class NanoHookOptions { + private String value; +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/SearchHookTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/SearchHookTest.java new file mode 100644 index 0000000000..8b9e7419b2 --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/hook/SearchHookTest.java @@ -0,0 +1,53 @@ +package org.fluentlenium.core.hook; + +import org.assertj.core.api.Assertions; +import org.fluentlenium.core.components.DefaultComponentInstantiator; +import org.fluentlenium.core.domain.FluentWebElement; +import org.fluentlenium.core.proxy.LocatorHandler; +import org.fluentlenium.core.proxy.LocatorProxies; +import org.fluentlenium.core.search.Search; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.Arrays; + +@RunWith(MockitoJUnitRunner.class) +public class SearchHookTest { + @Mock + WebElement element; + + @Mock + WebDriver driver; + + DefaultComponentInstantiator instantiator; + + private Search search; + + @Before + public void before() { + Mockito.when(driver.findElements(By.cssSelector(".selector"))).thenReturn(Arrays.asList(element)); + + instantiator = new DefaultComponentInstantiator(driver); + search = new Search(driver, instantiator, new DefaultHookChainBuilder(driver, instantiator)); + } + + @Test + public void testHookedSearch() { + FluentWebElement hookedElement = search.findFirst(".selector").withHook(NanoHook.class).click(); + + Mockito.verify(element).click(); + + LocatorHandler componentHandler = LocatorProxies.getLocatorHandler(hookedElement.getElement()); + NanoHook hookElement = (NanoHook) componentHandler.getHookLocatorResult(); + + Assertions.assertThat(hookElement.getBeforeClickNano()).isNotEqualTo(0L); + Assertions.assertThat(hookElement.getAfterClickNano()).isNotEqualTo(0L); + } +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorElementTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorElementTest.java index 52c7cb708c..37f4dd8596 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorElementTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorElementTest.java @@ -1,21 +1,18 @@ package org.fluentlenium.core.inject; -import org.assertj.core.api.Assertions; import org.fluentlenium.adapter.FluentAdapter; -import org.fluentlenium.core.FluentControl; import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.components.ComponentsManager; -import org.fluentlenium.core.components.DefaultComponentInstantiator; import org.fluentlenium.core.domain.FluentList; -import org.fluentlenium.core.domain.FluentListImpl; import org.fluentlenium.core.domain.FluentWebElement; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; @@ -88,7 +85,7 @@ public static class FluentWebElementContainer { FluentWebElement element; } - private static FluentWebElement existingElement = Mockito.mock(FluentWebElement.class); + private static FluentWebElement existingElement = mock(FluentWebElement.class); public static class ExistingFluentWebElementContainer { FluentWebElement element = existingElement; } @@ -132,9 +129,9 @@ public void testFluentWebElement() { when(webDriver.findElement(any(By.class))).thenReturn(webElement); - Assertions.assertThat(container.element.getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element).isExactlyInstanceOf(FluentWebElement.class); - Assertions.assertThat(container.element.getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.getTagName()).isEqualTo("h1"); + assertThat(container.element).isExactlyInstanceOf(FluentWebElement.class); + assertThat(container.element.getElement()).isInstanceOf(WebElement.class); } /** @@ -151,7 +148,7 @@ public void testExistingFluentWebElement() { when(webDriver.findElement(any(By.class))).thenReturn(webElement); - Assertions.assertThat(container.element).isSameAs(existingElement); + assertThat(container.element).isSameAs(existingElement); } @Test @@ -165,9 +162,9 @@ public void testFluentWebElementExtends() { when(webDriver.findElement(any(By.class))).thenReturn(webElement); - Assertions.assertThat(container.element.getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element).isExactlyInstanceOf(FluentWebElementSubClass.class); - Assertions.assertThat(container.element.getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.getTagName()).isEqualTo("h1"); + assertThat(container.element).isExactlyInstanceOf(FluentWebElementSubClass.class); + assertThat(container.element.getElement()).isInstanceOf(WebElement.class); } @Test @@ -181,8 +178,8 @@ public void testWebElementWrapper() { injector.inject(container); - Assertions.assertThat(container.element).isExactlyInstanceOf(WebElementWrapper.class); - Assertions.assertThat(container.element.getElement()).isInstanceOf(WebElement.class); + assertThat(container.element).isExactlyInstanceOf(WebElementWrapper.class); + assertThat(container.element.getElement()).isInstanceOf(WebElement.class); } @Test @@ -196,9 +193,9 @@ public void testWebElementDriverWrapper() { injector.inject(container); - Assertions.assertThat(container.element).isExactlyInstanceOf(WebElementDriverWrapper.class); - Assertions.assertThat(container.element.getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.getWebDriver()).isSameAs(webDriver); + assertThat(container.element).isExactlyInstanceOf(WebElementDriverWrapper.class); + assertThat(container.element.getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.getWebDriver()).isSameAs(webDriver); } @Test @@ -219,16 +216,16 @@ public void testFluentWebElementList() { when(webDriver.findElements(any(By.class))).thenReturn(webElements); - Assertions.assertThat(container.element).hasSize(2); - Assertions.assertThat(container.element).isInstanceOf(FluentList.class); + assertThat(container.element).hasSize(2); + assertThat(container.element).isInstanceOf(FluentList.class); - Assertions.assertThat(container.element.get(0).getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element.get(0)).isExactlyInstanceOf(FluentWebElement.class); - Assertions.assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(0).getTagName()).isEqualTo("h1"); + assertThat(container.element.get(0)).isExactlyInstanceOf(FluentWebElement.class); + assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(1).getTagName()).isEqualTo("h2"); - Assertions.assertThat(container.element.get(1)).isExactlyInstanceOf(FluentWebElement.class); - Assertions.assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(1).getTagName()).isEqualTo("h2"); + assertThat(container.element.get(1)).isExactlyInstanceOf(FluentWebElement.class); + assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); } @Test @@ -249,16 +246,16 @@ public void testFluentWebElementExtendsList() { when(webDriver.findElements(any(By.class))).thenReturn(webElements); - Assertions.assertThat(container.element).hasSize(2); - Assertions.assertThat(container.element).isInstanceOf(FluentList.class); + assertThat(container.element).hasSize(2); + assertThat(container.element).isInstanceOf(FluentList.class); - Assertions.assertThat(container.element.get(0).getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element.get(0)).isExactlyInstanceOf(FluentWebElementSubClass.class); - Assertions.assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(0).getTagName()).isEqualTo("h1"); + assertThat(container.element.get(0)).isExactlyInstanceOf(FluentWebElementSubClass.class); + assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(1).getTagName()).isEqualTo("h2"); - Assertions.assertThat(container.element.get(1)).isExactlyInstanceOf(FluentWebElementSubClass.class); - Assertions.assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(1).getTagName()).isEqualTo("h2"); + assertThat(container.element.get(1)).isExactlyInstanceOf(FluentWebElementSubClass.class); + assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); } @Test @@ -279,17 +276,17 @@ public void testWebElementWrapperList() { when(webDriver.findElements(any(By.class))).thenReturn(webElements); - Assertions.assertThat(container.element).hasSize(2); - Assertions.assertThat(container.element).isNotInstanceOf(FluentList.class); + assertThat(container.element).hasSize(2); + assertThat(container.element).isNotInstanceOf(FluentList.class); - Assertions.assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementWrapper.class); - Assertions.assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); + assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementWrapper.class); + assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementWrapper.class); - Assertions.assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); + assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementWrapper.class); + assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); } @Test @@ -310,20 +307,20 @@ public void testWebElementDriverWrapperList() { when(webDriver.findElements(any(By.class))).thenReturn(webElements); - Assertions.assertThat(container.element).hasSize(2); - Assertions.assertThat(container.element).isNotInstanceOf(FluentList.class); + assertThat(container.element).hasSize(2); + assertThat(container.element).isNotInstanceOf(FluentList.class); - Assertions.assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementDriverWrapper.class); - Assertions.assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element.get(0).getWebDriver()).isSameAs(webDriver); + assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementDriverWrapper.class); + assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); + assertThat(container.element.get(0).getWebDriver()).isSameAs(webDriver); - Assertions.assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementDriverWrapper.class); - Assertions.assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); - Assertions.assertThat(container.element.get(1).getWebDriver()).isSameAs(webDriver); + assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementDriverWrapper.class); + assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); + assertThat(container.element.get(1).getWebDriver()).isSameAs(webDriver); } @Test @@ -342,18 +339,18 @@ public void testNewInstance() { WebElementDriverWrapperListContainer container = injector.newInstance(WebElementDriverWrapperListContainer.class); - Assertions.assertThat(container.element).hasSize(2); - Assertions.assertThat(container.element).isNotInstanceOf(FluentList.class); + assertThat(container.element).hasSize(2); + assertThat(container.element).isNotInstanceOf(FluentList.class); - Assertions.assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementDriverWrapper.class); - Assertions.assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); - Assertions.assertThat(container.element.get(0).getWebDriver()).isSameAs(webDriver); + assertThat(container.element.get(0)).isExactlyInstanceOf(WebElementDriverWrapper.class); + assertThat(container.element.get(0).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(0).getElement().getTagName()).isEqualTo("h1"); + assertThat(container.element.get(0).getWebDriver()).isSameAs(webDriver); - Assertions.assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementDriverWrapper.class); - Assertions.assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); - Assertions.assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); - Assertions.assertThat(container.element.get(1).getWebDriver()).isSameAs(webDriver); + assertThat(container.element.get(1)).isExactlyInstanceOf(WebElementDriverWrapper.class); + assertThat(container.element.get(1).getElement()).isInstanceOf(WebElement.class); + assertThat(container.element.get(1).getElement().getTagName()).isEqualTo("h2"); + assertThat(container.element.get(1).getWebDriver()).isSameAs(webDriver); } @Test diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorHookTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorHookTest.java new file mode 100644 index 0000000000..8574d1a011 --- /dev/null +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/inject/FluentInjectorHookTest.java @@ -0,0 +1,391 @@ +package org.fluentlenium.core.inject; + +import com.google.common.base.Supplier; +import org.fluentlenium.adapter.FluentAdapter; +import org.fluentlenium.core.annotation.Page; +import org.fluentlenium.core.components.ComponentInstantiator; +import org.fluentlenium.core.components.ComponentsManager; +import org.fluentlenium.core.domain.FluentList; +import org.fluentlenium.core.domain.FluentWebElement; +import org.fluentlenium.core.hook.NoHook; +import org.fluentlenium.core.hook.Hook; +import org.fluentlenium.core.hook.HookOptions; +import org.fluentlenium.core.hook.NanoHook; +import org.fluentlenium.core.hook.NanoHookOptions; +import org.fluentlenium.core.proxy.LocatorHandler; +import org.fluentlenium.core.proxy.LocatorProxies; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ByIdOrName; +import org.openqa.selenium.support.pagefactory.ElementLocator; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class FluentInjectorHookTest { + + @Mock + private WebDriver webDriver; + + private FluentAdapter fluentAdapter; + + private FluentInjector injector; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + + fluentAdapter = new FluentAdapter(); + fluentAdapter.initFluent(webDriver); + + injector = new FluentInjector(fluentAdapter, new ComponentsManager(webDriver), new DefaultContainerInstanciator(fluentAdapter)); + } + + @After + public void after() { + reset(webDriver); + } + + public static class FluentWebElementContainer { + @Hook(NanoHook.class) + private FluentWebElement injected; + } + + @Test + public void testFluentWebElement() { + FluentWebElementContainer container = new FluentWebElementContainer(); + + WebElement element = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("injected"))).thenReturn(element); + + injector.inject(container); + + assertThat(container.injected).isNotNull(); + + container.injected.getElement().click(); + verify(element).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(container.injected.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getOptionValue()).isEqualTo(null); + + assertThat(elementWrapperHook.getBeforeFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + } + + public static class Options extends NanoHookOptions { + public Options() { + super("options"); + } + } + + public static class FluentWebElementOptionContainer { + @Hook(NanoHook.class) + @HookOptions(Options.class) + private FluentWebElement injected; + } + + @Test + public void testFluentWebElementOption() { + FluentWebElementOptionContainer container = new FluentWebElementOptionContainer(); + + WebElement element = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("injected"))).thenReturn(element); + + injector.inject(container); + + assertThat(container.injected).isNotNull(); + + container.injected.getElement().click(); + verify(element).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(container.injected.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getOptionValue()).isEqualTo("options"); + + assertThat(elementWrapperHook.getBeforeFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + } + + public static class WebElementWrapper { + private final WebElement element; + + public WebElementWrapper(WebElement element) { + this.element = element; + } + + public WebElement getElement() { + return element; + } + } + + public static class WebElementWrapperContainer { + @Hook(NanoHook.class) + private WebElementWrapper injected; + } + + @Test + public void testWebElementWrapper() { + WebElementWrapperContainer container = new WebElementWrapperContainer(); + + WebElement element = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("injected"))).thenReturn(element); + + injector.inject(container); + + assertThat(container.injected).isNotNull(); + + container.injected.getElement().click(); + verify(element).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(container.injected.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + + assertThat(elementWrapperHook.getBeforeFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + } + + public static class FluentListContainer { + @Hook(NanoHook.class) + private FluentList injected; + } + + @Test + public void testFluentList() { + FluentListContainer container = new FluentListContainer(); + + WebElement element1 = mock(WebElement.class); + WebElement element2 = mock(WebElement.class); + WebElement element3 = mock(WebElement.class); + + when(webDriver.findElements(new ByIdOrName("injected"))).thenReturn(Arrays.asList(element1, element2, element3)); + + injector.inject(container); + + LocatorHandler listLocatorHandler = LocatorProxies.getLocatorHandler(container.injected); + NanoHook listLocatorHook = (NanoHook)listLocatorHandler.getHookLocator(); + + assertThat(listLocatorHook.getBeforeFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementsNano()).isEqualTo(0L); + + assertThat(container.injected).hasSize(3); + + assertThat(listLocatorHook.getBeforeFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getBeforeFindElementsNano()).isNotEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementsNano()).isNotEqualTo(0L); + + for (FluentWebElement webElement : container.injected) { + assertThat(webElement).isNotNull(); + + webElement.click(); + verify(LocatorProxies.getLocatorResult(webElement.getElement())).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(webElement.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + + assertThat(elementWrapperHook.getBeforeFindElementNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + } + } + + public static class WebElementWrapperListContainer { + @Hook(NanoHook.class) + private List injected; + } + + @Test + public void testWebElementWrapperList() { + WebElementWrapperListContainer container = new WebElementWrapperListContainer(); + + WebElement element1 = mock(WebElement.class); + WebElement element2 = mock(WebElement.class); + WebElement element3 = mock(WebElement.class); + + when(webDriver.findElements(new ByIdOrName("injected"))).thenReturn(Arrays.asList(element1, element2, element3)); + + injector.inject(container); + + LocatorHandler listLocatorHandler = LocatorProxies.getLocatorHandler(container.injected); + NanoHook listLocatorHook = (NanoHook)listLocatorHandler.getHookLocator(); + + assertThat(listLocatorHook.getBeforeFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementsNano()).isEqualTo(0L); + + assertThat(container.injected).hasSize(3); + + assertThat(listLocatorHook.getBeforeFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementNano()).isEqualTo(0L); + assertThat(listLocatorHook.getBeforeFindElementsNano()).isNotEqualTo(0L); + assertThat(listLocatorHook.getAfterFindElementsNano()).isNotEqualTo(0L); + + for (WebElementWrapper webElement : container.injected) { + assertThat(webElement).isNotNull(); + + webElement.getElement().click(); + verify(LocatorProxies.getLocatorResult(webElement.getElement())).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(webElement.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + } + } + + public static class NanoHook2 extends NanoHook { + public NanoHook2(WebDriver webDriver, ComponentInstantiator instantiator, Supplier elementSupplier, Supplier locatorSupplier, NanoHookOptions options) { + super(webDriver, instantiator, elementSupplier, locatorSupplier, options); + } + } + + @NoHook + public static class SubContainer3 { + private WebElementWrapper subInjected3; + } + + @Hook(NanoHook2.class) + public static class SubContainer2 { + private WebElementWrapper subInjected2; + } + + public static class SubContainer { + private FluentWebElement subInjected; + + @NoHook + private FluentWebElement subNoHookInjected; + + @Page + private SubContainer2 subContainer2; + + @Page + private SubContainer3 subContainer3; + } + + @Hook(NanoHook.class) + public static class FluentWebElementClassContainer { + private FluentWebElement injected; + + @Page + private SubContainer subContainer; + } + + @Test + public void testFluentWebElementClass() { + FluentWebElementClassContainer container = new FluentWebElementClassContainer(); + + WebElement element = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("injected"))).thenReturn(element); + + WebElement subElement = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("subInjected"))).thenReturn(subElement); + + WebElement subNoHookElement = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("subNoHookInjected"))).thenReturn(subNoHookElement); + + WebElement subElement2 = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("subInjected2"))).thenReturn(subElement2); + + WebElement subElement3 = mock(WebElement.class); + when(webDriver.findElement(new ByIdOrName("subInjected3"))).thenReturn(subElement3); + + injector.inject(container); + + assertThat(container.injected).isNotNull(); + + container.injected.getElement().click(); + verify(element).click(); + + LocatorHandler elementWrapperHandler = LocatorProxies.getLocatorHandler(container.injected.getElement()); + NanoHook elementWrapperHook = (NanoHook)elementWrapperHandler.getHookLocatorResult(); + + assertThat(elementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getOptionValue()).isEqualTo(null); + + assertThat(elementWrapperHook.getBeforeFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementNano()).isNotEqualTo(0L); + assertThat(elementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(elementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + + assertThat(container.subContainer.subInjected).isNotNull(); + + container.subContainer.subInjected.getElement().click(); + verify(subElement).click(); + + LocatorHandler subElementWrapperHandler = LocatorProxies.getLocatorHandler(container.subContainer.subInjected.getElement()); + NanoHook subElementWrapperHook = (NanoHook)subElementWrapperHandler.getHookLocatorResult(); + + assertThat(subElementWrapperHook.getBeforeClickNano()).isNotEqualTo(0L); + assertThat(subElementWrapperHook.getAfterClickNano()).isNotEqualTo(0L); + assertThat(subElementWrapperHook.getOptionValue()).isEqualTo(null); + + assertThat(subElementWrapperHook.getBeforeFindElementNano()).isNotEqualTo(0L); + assertThat(subElementWrapperHook.getAfterFindElementNano()).isNotEqualTo(0L); + assertThat(subElementWrapperHook.getBeforeFindElementsNano()).isEqualTo(0L); + assertThat(subElementWrapperHook.getAfterFindElementsNano()).isEqualTo(0L); + + container.subContainer.subNoHookInjected.getElement().click(); + verify(subNoHookElement).click(); + + LocatorHandler subNoHookElementWrapperHandler = LocatorProxies.getLocatorHandler(container.subContainer.subNoHookInjected.getElement()); + assertThat(subNoHookElementWrapperHandler.getHookLocatorResult()).isSameAs(subNoHookElementWrapperHandler.getLocatorResult()); + + container.subContainer.subContainer2.subInjected2.getElement().click(); + verify(subElement2).click(); + + LocatorHandler subElement2WrapperHandler = LocatorProxies.getLocatorHandler(container.subContainer.subContainer2.subInjected2.getElement()); + assertThat(subElement2WrapperHandler.getHookLocatorResult()).isExactlyInstanceOf(NanoHook2.class); + + NanoHook2 nanoHook2 = ((NanoHook2) subElement2WrapperHandler.getHookLocatorResult()); + + assertThat(nanoHook2.getElement()).isExactlyInstanceOf(NanoHook.class); + assertThat(((NanoHook)nanoHook2.getElement()).getElement()).isSameAs(subElement2); + + container.subContainer.subContainer3.subInjected3.getElement().click(); + verify(subElement3).click(); + + LocatorHandler subNoHook3ElementWrapperHandler = LocatorProxies.getLocatorHandler(container.subContainer.subContainer3.subInjected3.getElement()); + assertThat(subNoHook3ElementWrapperHandler.getHookLocatorResult()).isSameAs(subNoHook3ElementWrapperHandler.getLocatorResult()); + + } + + +} diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/proxy/ProxiesTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/proxy/ProxiesTest.java index e36188185d..1969b61bb3 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/core/proxy/ProxiesTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/proxy/ProxiesTest.java @@ -46,7 +46,7 @@ public void before() { @Test public void testElementIsLazy() { - WebElement elementProxy1 = Proxies.createWebElement(new ElementLocator() { + WebElement elementProxy1 = LocatorProxies.createWebElement(new ElementLocator() { @Override public WebElement findElement() { return driver.findElement(By.cssSelector("#element1")); @@ -67,7 +67,7 @@ public List findElements() { @Test public void testElementChainIsLazy() { - final WebElement element1Proxy = Proxies.createWebElement(new ElementLocator() { + final WebElement element1Proxy = LocatorProxies.createWebElement(new ElementLocator() { @Override public WebElement findElement() { return driver.findElement(By.cssSelector("#element1")); @@ -79,7 +79,7 @@ public List findElements() { } }); - final WebElement element2Proxy = Proxies.createWebElement(new ElementLocator() { + final WebElement element2Proxy = LocatorProxies.createWebElement(new ElementLocator() { @Override public WebElement findElement() { return element1Proxy.findElement(By.cssSelector("#element2")); @@ -91,7 +91,7 @@ public List findElements() { } }); - WebElement element3Proxy = Proxies.createWebElement(new ElementLocator() { + WebElement element3Proxy = LocatorProxies.createWebElement(new ElementLocator() { @Override public WebElement findElement() { return element2Proxy.findElement(By.cssSelector("#element3")); diff --git a/fluentlenium-core/src/test/java/org/fluentlenium/core/search/SearchTest.java b/fluentlenium-core/src/test/java/org/fluentlenium/core/search/SearchTest.java index f0bd938781..08ff07d203 100644 --- a/fluentlenium-core/src/test/java/org/fluentlenium/core/search/SearchTest.java +++ b/fluentlenium-core/src/test/java/org/fluentlenium/core/search/SearchTest.java @@ -6,6 +6,7 @@ import org.fluentlenium.core.domain.FluentWebElement; import org.fluentlenium.core.filter.Filter; import org.fluentlenium.core.filter.matcher.Matcher; +import org.fluentlenium.core.hook.DefaultHookChainBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,8 @@ public class SearchTest { @Before public void before() { MockitoAnnotations.initMocks(this); - search = new Search(searchContext, new DefaultComponentInstantiator(driver)); + DefaultComponentInstantiator instantiator = new DefaultComponentInstantiator(driver); + search = new Search(searchContext, instantiator, new DefaultHookChainBuilder(driver, instantiator)); } @After