package org.fluentlenium.core.proxy; import org.fluentlenium.core.domain.WrapsElements; import org.fluentlenium.core.hook.HookChainBuilder; import org.fluentlenium.core.hook.HookDefinition; import org.openqa.selenium.NoSuchElementException; 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; import java.util.function.Supplier; /** * Utility class to create proxies of WebElement, Component, FluentList and List of Components based on their locators. */ public final class LocatorProxies { private LocatorProxies() { // Utility class } /** * Build a NoSuchElementException using message provided by proxy. * * @param proxy proxy * @return NoSuchElementException No such element exception */ public static NoSuchElementException noSuchElement(Object proxy) { LocatorHandler locatorHandler = getLocatorHandler(proxy); if (locatorHandler == null) { return new NoSuchElementException("No such element"); } return locatorHandler.noSuchElement(); } /** * Get the message context of given proxy. * * @param proxy proxy * @return message context */ public static String getMessageContext(Object proxy) { LocatorHandler locatorHandler = getLocatorHandler(proxy); if (locatorHandler == null) { return ""; } return locatorHandler.getMessageContext(); } /** * Get the proxy locator handler. * * @param proxy proxy object * @return locator handler, or null if not found */ public static LocatorHandler getLocatorHandler(Object proxy) { while (proxy instanceof WrapsElements && !Proxy.isProxyClass(proxy.getClass())) { proxy = ((WrapsElements) proxy).getWrappedElements(); } if (proxy != null && Proxy.isProxyClass(proxy.getClass())) { InvocationHandler proxyHandler = Proxy.getInvocationHandler(proxy); if (proxyHandler instanceof LocatorHandler) { return (LocatorHandler) proxyHandler; } } return null; } /** * Get the underlying result of a proxy, through locator handler. * * @param proxy proxy object * @param <T> type of the result * @return result * @see LocatorHandler#getLocatorResult() */ public static <T> T getLocatorResult(T proxy) { LocatorHandler<?> componentHandler = getLocatorHandler(proxy); if (componentHandler != null) { return (T) componentHandler.getLocatorResult(); } return proxy; } /** * Reset the proxy locator handler. * * @param proxy proxy object * @see LocatorHandler#reset() */ public static void reset(Object proxy) { LocatorHandler handler = getLocatorHandler(proxy); if (handler != null) { handler.reset(); } } /** * Check if the proxy element is present. * * @param proxy proxy object * @return true if result is present, false otherwise * @see LocatorHandler#present() */ public static boolean present(Object proxy) { LocatorHandler locatorHandler = getLocatorHandler(proxy); if (locatorHandler == null) { return true; } return locatorHandler.present(); } /** * If result is not loaded, load result immediatly. If it's already loaded, it has no effect. * * @param proxy proxy object * @see LocatorHandler#now() */ public static void now(Object proxy) { LocatorHandler<?> handler = getLocatorHandler(proxy); if (handler != null) { handler.now(); } } /** * Check if this proxy has loaded it's result. * * @param proxy proxy object * @return true if the result is loaded, false otherwise */ public static boolean loaded(Object proxy) { LocatorHandler handler = getLocatorHandler(proxy); if (handler != null) { return handler.loaded(); } return true; } /** * Add a proxy listener for this proxy. * * @param proxy proxy object * @param listener listener to add, which will be notified when result is searched and found * @return true if the listener was added, false otherwise */ public static boolean addProxyListener(Object proxy, ProxyElementListener listener) { if (Proxy.isProxyClass(proxy.getClass())) { InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); if (invocationHandler instanceof LocatorHandler) { return ((LocatorHandler) invocationHandler).addListener(listener); } } return false; } /** * Removes a proxy element listener. * * @param proxy proxy object * @param listener listener to remove * @return true if the listener was removed, false otherwise */ public static boolean removeProxyListener(Object proxy, ProxyElementListener listener) { if (Proxy.isProxyClass(proxy.getClass())) { InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); if (invocationHandler instanceof LocatorHandler) { return ((LocatorHandler) invocationHandler).removeListener(listener); } } return false; } /** * Create a WebElement proxy from an existing element. * * @param element existing element * @return proxy */ public static WebElement createWebElement(WebElement element) { return createWebElement(new ElementInstanceLocator(element)); } /** * Create a WebElement proxy from an element supplier. * * @param elementSupplier element supplier * @return proxy */ public static WebElement createWebElement(Supplier<WebElement> elementSupplier) { return createWebElement(new ElementSupplierLocator(elementSupplier)); } /** * Create a WebElement proxy from an element locator. * * @param locator element locator * @return proxy */ public static WebElement createWebElement(ElementLocator locator) { 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; } /** * Create a list of WebElement proxies from an existing element list. * * @param elements existing element list * @return proxy */ public static List<WebElement> createWebElementList(List<WebElement> elements) { return createWebElementList(new ElementListInstanceLocator(elements)); } /** * Create a list of WebElement proxies from a supplier of element list. * * @param elementsSupplier supplier of element list * @return proxy */ public static List<WebElement> createWebElementList(Supplier<List<WebElement>> elementsSupplier) { return createWebElementList(new ElementListSupplierLocator(elementsSupplier)); } /** * Create a list of WebElement proxies from a locator of element list. * * @param locator locator of element list * @return proxy */ public static List<WebElement> createWebElementList(ElementLocator locator) { ListHandler handler = new ListHandler(locator); List<WebElement> proxy = (List<WebElement>) Proxy .newProxyInstance(locator.getClass().getClassLoader(), new Class[] {List.class, WrapsElements.class}, handler); handler.setProxy(proxy); return proxy; } /** * Apply this hook list. * * @param proxy proxy object * @param hookChainBuilder hook chain builder * @param hookDefinitions hook definitions */ public static void setHooks(Object proxy, HookChainBuilder hookChainBuilder, List<HookDefinition<?>> hookDefinitions) { LocatorHandler<?> componentHandler = getLocatorHandler(proxy); componentHandler.setHooks(hookChainBuilder, hookDefinitions); } /** * Creates a proxy element matching the first element of the list. * * @param proxy list of element * @return proxy element matching the first element */ public static WebElement first(List<WebElement> proxy) { LocatorHandler locatorHandler = getLocatorHandler(proxy); return createWebElement(new FirstElementLocator(locatorHandler.getLocator())); } /** * Creates a proxy element matching the last element of the list. * * @param proxy list of element * @return proxy element matching the last element */ public static WebElement last(List<WebElement> proxy) { LocatorHandler locatorHandler = getLocatorHandler(proxy); return createWebElement(new LastElementLocator(locatorHandler.getLocator())); } /** * Creates a proxy element matching the n-th element of the list. * * @param proxy list of element * @param index index to match * @return proxy element matching the n-th element */ public static WebElement index(List<WebElement> proxy, int index) { LocatorHandler locatorHandler = getLocatorHandler(proxy); return createWebElement(new AtIndexElementLocator(locatorHandler.getLocator(), index)); } }