A light Java library designed to tackle the shortcomings of Selenium Page Object Model and Page Factory features.
It provides the following enhancements:
- WebContext, used for locating web elements relative to the parent search context
- Retry on error mechanism, activated when a specific error or exception occurs while locating and interacting with a web element, such as StaleElementReferenceException.
<dependency>
<groupId>io.github.fslev</groupId>
<artifactId>selenium-jutils</artifactId>
<version>${latest.version}</version>
</dependency>
Gradle: compile("io.github.fslev:selenium-jutils:${latest.version}")
Selenium-JUtils uses the selenium-java library. Set it inside your project, in addition to selenium-jutils dependency:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.java.version}</version>
</dependency>
Decorate your Page Object class instance using PageFactory and FieldContextDecorator:
public class BasePage {
protected WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(new FieldContextDecorator(new ElementContextLocatorFactory(driver)), this);
}
}
You can group WebElements or other context classes under the same class which extends WebContext and each element will be located relative to the search context of its wrapper class.
Example:
public class GroceryPage extends BasePage {
@FindBy(xpath = "//app-list")
private GroceryListTab groceryListTab;
}
public class GroceryListTab extends WebContext {
@FindBy(xpath = ".//li//app-item")
private List<Item> items;
}
public class Item extends WebContext {
@FindBy(css = "span")
private WebElement name;
@FindBy(xpath = ".//button[text()='Remove']")
private WebElement removeButton;
}
See how easy is to manage a grocery list:
GroceryPage groceryPage = new GroceryPage(driver);
List<Item> itemList = groceryPage.getGroceryListTab().getItems();
assertEquals(6, itemList.size());
assertEquals("Baking item1", itemList.get(0).getName().getText());
// Remove 4th item
list.get(3).getRemoveButton().click();
assertEquals(5, itemList.size());
Behind the scenes
new GroceryPage(driver).getGroceryListTab().getItems().get(2).getName()
==>
driver.findElement(By.xpath("//app-list"))
.findElements(By.xpath(".//li//app-item"))
.get(2)
.findElement(By.cssSelector("span"))
Behind the scenes, the web element(s) you get, are nothing but Java proxies which are first locating the corresponding element every time you interact with it. It preserves the default behaviour and features of Selenium Page Factory: https://github.com/SeleniumHQ/selenium/wiki/PageFactory
There is a possibility that after locating a web element(s) but before interacting with it, the DOM gets refreshed, in which case you will receive a StaleElementReferenceException.
new GroceryPage(driver).getGroceryListTab().getItems().get(2)
// <-- DOM is refreshed
.getName()
or
driver.findElement(By.xpath("//app-list"))
.findElements(By.xpath(".//li//app-item"))
.get(2)
// <-- DOM is refreshed
.findElement(By.cssSelector("span"))
=> StaleElementReferenceException
Or maybe the element cannot be selected for a very short period of time, in which case you get ElementNotSelectableException.
The retry on error mechanism tackles these problems by invoking again the chained localisation and interaction of the web element, if any specific error occurs. In order to activate it, instantiate the ElementContextLocatorFactory with a specific List of errors or exceptions, upon which web element localisation and interaction should be retried, and a duration timeout.
PageFactory.initElements(new FieldContextDecorator(new ElementContextLocatorFactory(
driver, Duration.ofSeconds(20), Arrays.asList(StaleElementReferenceException.class,
ElementNotSelectableException.class ))), this);
Please take a look over the project tests in order to get a clearer picture on how the WebContext and Retry on error mechanism work.
You need to start Selenium Grid and the Grocery application via docker compose, from src/test/resources:
docker-compose -f selenium-grid.yml up
This tutorial shows you how to test a website user interface with Selenium and Cucumber for Java