Class WebDriverDecorator<T extends WebDriver>
- java.lang.Object
-
- org.openqa.selenium.support.decorators.WebDriverDecorator<T>
-
- Direct Known Subclasses:
EventFiringDecorator
@Beta public class WebDriverDecorator<T extends WebDriver> extends java.lang.Object
This class helps to create decorators for instances ofWebDriver
and derived objects, such asWebElement
s andAlert
, that can extend or modify their "regular" behavior. It provides a flexible alternative to subclassing WebDriver.Here is a general usage pattern:
- implement a subclass of WebDriverDecorator that adds something to WebDriver behavior:
(see below for details)public class MyWebDriverDecorator extends WebDriverDecorator { ... }
- use a decorator instance to decorate a WebDriver object:
WebDriver original = new FirefoxDriver(); WebDriver decorated = new MyWebDriverDecorator().decorate(original);
- use the decorated WebDriver instead of the original one:
decorated.get("http://example.com/"); ... decorated.quit();
- before executing a method of the underlying object,
- after executing a method of the underlying object,
- instead of executing a method of the underlying object,
- when an exception is thrown by a method of the underlying object.
decorated.findElement(someLocator)
automatically decorates the returned WebElement.Instances created by the decorator implement all the same interfaces as the original objects.
When you implement a decorator there are two main options (that can be used both separately and together):
- if you want to apply the same behavior modification to all methods of
a WebDriver instance and its derived objects you can subclass
WebDriverDecorator and override some of the following methods:
beforeCall(Decorated, Method, Object[])
,afterCall(Decorated, Method, Object[], Object)
,call(Decorated, Method, Object[])
andonError(Decorated, Method, Object[], InvocationTargetException)
- if you want to modify behavior of a specific class instances only
(e.g. behaviour of WebElement instances) you can override one of the
overloaded
createDecorated
methods to create a non-trivial decorator for the specific class only.
One of the most widely used decorator examples is a logging decorator. In this case we want to add the same piece of logging code before and after each invoked method:
For the second example let's implement a decorator that implicitly waits for an element to be visible before any click or sendKeys method call.public class LoggingDecorator extends WebDriverDecorator { final Logger logger = LoggerFactory.getLogger(Thread.currentThread().getName()); @Override public void beforeCall(Decorated<?> target, Method method, Object[] args) { logger.debug("before {}.{}({})", target, method, args); } @Override public void afterCall(Decorated<?> target, Method method, Object[] args, Object res) { logger.debug("after {}.{}({}) => {}", target, method, args, res); } }
This class is not a pure decorator, it allows to not only add new behavior but also replace "normal" behavior of a WebDriver or derived objects.public class ImplicitlyWaitingDecorator extends WebDriverDecorator { private WebDriverWait wait; @Override public Decorated<WebDriver> createDecorated(WebDriver driver) { wait = new WebDriverWait(driver, Duration.ofSeconds(10)); return super.createDecorated(driver); } @Override public Decorated<WebElement> createDecorated(WebElement original) { return new DefaultDecorated<>(original, this) { @Override public void beforeCall(Method method, Object[] args) { String methodName = method.getName(); if ("click".equals(methodName) || "sendKeys".equals(methodName)) { wait.until(d -> getOriginal().isDisplayed()); } } }; } }
Let's suppose you want to use JavaScript-powered clicks instead of normal ones (yes, this allows to interact with invisible elements, it's a bad practice in general but sometimes it's inevitable). This behavior change can be achieved with the following "decorator":
It is possible to apply multiple decorators to compose behaviors added by each of them. For example, if you want to log method calls and implicitly wait for elements visibility you can use two decorators:public class JavaScriptPoweredDecorator extends WebDriverDecorator { @Override public Decorated<WebElement> createDecorated(WebElement original) { return new DefaultDecorated<>(original, this) { @Override public Object call(Method method, Object[] args) throws Throwable { String methodName = method.getName(); if ("click".equals(methodName)) { JavascriptExecutor executor = (JavascriptExecutor) getDecoratedDriver().getOriginal(); executor.executeScript("arguments[0].click()", getOriginal()); return null; } else { return super.call(method, args); } } }; } }
WebDriver original = new FirefoxDriver(); WebDriver decorated = new ImplicitlyWaitingDecorator().decorate( new LoggingDecorator().decorate(original));
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description protected static interface
WebDriverDecorator.JsonSerializer
-
Constructor Summary
Constructors Constructor Description WebDriverDecorator()
WebDriverDecorator(java.lang.Class<T> targetClass)
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description void
afterCall(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.Object res)
void
beforeCall(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args)
java.lang.Object
call(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args)
Decorated<Alert>
createDecorated(Alert original)
Decorated<VirtualAuthenticator>
createDecorated(VirtualAuthenticator original)
Decorated<WebDriver.Navigation>
createDecorated(WebDriver.Navigation original)
Decorated<WebDriver.Options>
createDecorated(WebDriver.Options original)
Decorated<WebDriver.TargetLocator>
createDecorated(WebDriver.TargetLocator original)
Decorated<WebDriver.Timeouts>
createDecorated(WebDriver.Timeouts original)
Decorated<WebDriver.Window>
createDecorated(WebDriver.Window original)
Decorated<WebElement>
createDecorated(WebElement original)
Decorated<T>
createDecorated(T driver)
protected <Z> Z
createProxy(Decorated<Z> decorated, java.lang.Class<Z> clazz)
T
decorate(T original)
Decorated<T>
getDecoratedDriver()
java.lang.Object
onError(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.reflect.InvocationTargetException e)
-
-
-
Constructor Detail
-
WebDriverDecorator
public WebDriverDecorator()
-
WebDriverDecorator
public WebDriverDecorator(java.lang.Class<T> targetClass)
-
-
Method Detail
-
createDecorated
public Decorated<WebElement> createDecorated(WebElement original)
-
createDecorated
public Decorated<WebDriver.TargetLocator> createDecorated(WebDriver.TargetLocator original)
-
createDecorated
public Decorated<WebDriver.Navigation> createDecorated(WebDriver.Navigation original)
-
createDecorated
public Decorated<WebDriver.Options> createDecorated(WebDriver.Options original)
-
createDecorated
public Decorated<WebDriver.Timeouts> createDecorated(WebDriver.Timeouts original)
-
createDecorated
public Decorated<WebDriver.Window> createDecorated(WebDriver.Window original)
-
createDecorated
public Decorated<VirtualAuthenticator> createDecorated(VirtualAuthenticator original)
-
beforeCall
public void beforeCall(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args)
-
call
public java.lang.Object call(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args) throws java.lang.Throwable
- Throws:
java.lang.Throwable
-
afterCall
public void afterCall(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.Object res)
-
onError
public java.lang.Object onError(Decorated<?> target, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.reflect.InvocationTargetException e) throws java.lang.Throwable
- Throws:
java.lang.Throwable
-
createProxy
protected final <Z> Z createProxy(Decorated<Z> decorated, java.lang.Class<Z> clazz)
-
-