Class WebDriverDecorator<T extends WebDriver>
java.lang.Object
org.openqa.selenium.support.decorators.WebDriverDecorator<T>
- Direct Known Subclasses:
EventFiringDecorator
This class helps to create decorators for instances of
WebDriver
and derived objects,
such as WebElement
s and Alert
, 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:
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);
}
}
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 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());
}
}
};
}
}
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.
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":
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);
}
}
};
}
}
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:
WebDriver original = new FirefoxDriver();
WebDriver decorated =
new ImplicitlyWaitingDecorator().decorate(
new LoggingDecorator().decorate(original));
-
Nested Class Summary
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionvoid
void
beforeCall
(Decorated<?> target, Method method, Object[] args) createDecorated
(Alert original) createDecorated
(VirtualAuthenticator original) createDecorated
(WebDriver.Navigation original) createDecorated
(WebDriver.Options original) createDecorated
(WebDriver.TargetLocator original) createDecorated
(WebDriver.Timeouts original) createDecorated
(WebDriver.Window original) createDecorated
(WebElement original) createDecorated
(T driver) protected final <Z> Z
createProxy
(Decorated<Z> decorated, Class<Z> clazz) final T
onError
(Decorated<?> target, Method method, Object[] args, InvocationTargetException e)
-
Constructor Details
-
WebDriverDecorator
public WebDriverDecorator() -
WebDriverDecorator
-
-
Method Details
-
decorate
-
getDecoratedDriver
-
createDecorated
-
createDecorated
-
createDecorated
-
createDecorated
-
createDecorated
-
createDecorated
-
createDecorated
-
createDecorated
-
beforeCall
-
call
- Throws:
Throwable
-
afterCall
-
onError
public Object onError(Decorated<?> target, Method method, Object[] args, InvocationTargetException e) throws Throwable - Throws:
Throwable
-
createProxy
-