Selenium WebDriver support utilities providing Page Object Model, waiting mechanisms, event handling, and UI utilities for robust test automation
—
The Page Factory provides a comprehensive implementation of the Page Object Model pattern, offering annotations for element location, automatic element initialization, and flexible locator strategies. It simplifies page object creation by automatically initializing WebElement fields with proxies that locate elements dynamically.
public class PageFactory {
/**
* Create and initialize a new page object instance
*/
public static <T> T initElements(WebDriver driver, Class<T> pageClassToProxy);
/**
* Initialize an existing page object instance with element proxies
*/
public static void initElements(WebDriver driver, Object page);
/**
* Initialize page object with custom element locator factory
*/
public static void initElements(ElementLocatorFactory factory, Object page);
/**
* Initialize page object with custom field decorator
*/
public static void initElements(FieldDecorator decorator, Object page);
}// Create and initialize new page object
LoginPage loginPage = PageFactory.initElements(driver, LoginPage.class);
// Initialize existing page object
LoginPage loginPage = new LoginPage();
PageFactory.initElements(driver, loginPage);
// Initialize with custom timeout for AJAX elements
AjaxElementLocatorFactory factory = new AjaxElementLocatorFactory(driver, 15);
PageFactory.initElements(factory, loginPage);@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface FindBy {
/**
* Location strategy to use
*/
How how() default How.UNSET;
/**
* Value to use with the location strategy
*/
String using() default "";
/**
* Locate by ID attribute
*/
String id() default "";
/**
* Locate by name attribute
*/
String name() default "";
/**
* Locate by CSS class name
*/
String className() default "";
/**
* Locate by CSS selector
*/
String css() default "";
/**
* Locate by HTML tag name
*/
String tagName() default "";
/**
* Locate by exact link text
*/
String linkText() default "";
/**
* Locate by partial link text
*/
String partialLinkText() default "";
/**
* Locate by XPath expression
*/
String xpath() default "";
}@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface FindBys {
/**
* Array of @FindBy annotations to chain together (all must match)
*/
FindBy[] value();
}@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface FindAll {
/**
* Array of @FindBy annotations (any can match)
*/
FindBy[] value();
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CacheLookup {
// Marker annotation - element lookups will be cached after first access
}public enum How {
CLASS_NAME, CSS, ID, ID_OR_NAME, LINK_TEXT, NAME,
PARTIAL_LINK_TEXT, TAG_NAME, XPATH, UNSET;
/**
* Build a By locator for this strategy with the given value
*/
public abstract By buildBy(String value);
}import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
@FindBy(id = "username")
private WebElement usernameField;
@FindBy(name = "password")
private WebElement passwordField;
@FindBy(css = "button[type='submit']")
private WebElement submitButton;
@FindBy(xpath = "//div[@class='error-message']")
private WebElement errorMessage;
public LoginPage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void login(String username, String password) {
usernameField.clear();
usernameField.sendKeys(username);
passwordField.clear();
passwordField.sendKeys(password);
submitButton.click();
}
public String getErrorMessage() {
return errorMessage.getText();
}
}public class AdvancedPage {
// Using How enum with using attribute
@FindBy(how = How.CSS, using = ".submit-btn")
private WebElement submitButton;
// Chained locators (AND logic) - both conditions must match
@FindBys({
@FindBy(className = "form-group"),
@FindBy(tagName = "input")
})
private WebElement nestedInput;
// Multiple possible locators (OR logic) - any condition can match
@FindAll({
@FindBy(id = "submit"),
@FindBy(name = "submit"),
@FindBy(xpath = "//input[@type='submit']")
})
private WebElement submitByAny;
// Cached lookup - element located once and reused
@CacheLookup
@FindBy(id = "static-header")
private WebElement header;
// List of elements
@FindBy(className = "menu-item")
private List<WebElement> menuItems;
}public interface ElementLocator {
/**
* Find a single element
*/
WebElement findElement();
/**
* Find multiple elements
*/
List<WebElement> findElements();
}public interface ElementLocatorFactory {
/**
* Create an ElementLocator for the given field
*/
ElementLocator createLocator(Field field);
}public class DefaultElementLocator implements ElementLocator {
public DefaultElementLocator(SearchContext searchContext, Field field);
public DefaultElementLocator(SearchContext searchContext, AbstractAnnotations annotations);
public WebElement findElement();
public List<WebElement> findElements();
protected boolean shouldCache();
}public class AjaxElementLocator extends DefaultElementLocator {
/**
* Element locator that waits for elements to appear (for AJAX content)
*/
public AjaxElementLocator(SearchContext context, int timeOutInSeconds, AbstractAnnotations annotations);
public AjaxElementLocator(SearchContext searchContext, Field field, int timeOutInSeconds);
public WebElement findElement();
public List<WebElement> findElements();
protected long sleepFor();
protected boolean isElementUsable(WebElement element);
}public class AjaxElementLocatorFactory implements ElementLocatorFactory {
/**
* Factory for creating AjaxElementLocator instances with timeout
*/
public AjaxElementLocatorFactory(SearchContext searchContext, int timeOutInSeconds);
public ElementLocator createLocator(Field field);
}public interface FieldDecorator {
/**
* Decorate a field with a proxy or return null if field should not be decorated
*/
Object decorate(ClassLoader loader, Field field);
}public class DefaultFieldDecorator implements FieldDecorator {
public DefaultFieldDecorator(ElementLocatorFactory factory);
public Object decorate(ClassLoader loader, Field field);
protected boolean isDecoratableList(Field field);
protected WebElement proxyForLocator(ClassLoader loader, ElementLocator locator);
protected List<WebElement> proxyForListLocator(ClassLoader loader, ElementLocator locator);
}public class ByAll extends By {
/**
* Locator that finds elements matching any of multiple By instances
*/
public ByAll(By... bys);
public WebElement findElement(SearchContext context);
public List<WebElement> findElements(SearchContext context);
}public class ByChained extends By {
/**
* Locator that finds elements using chained lookups
*/
public ByChained(By... bys);
public WebElement findElement(SearchContext context);
public List<WebElement> findElements(SearchContext context);
}public abstract class AbstractAnnotations {
/**
* Build a By locator from field annotations
*/
public abstract By buildBy();
/**
* Check if element lookup should be cached
*/
public abstract boolean isLookupCached();
}public class Annotations extends AbstractAnnotations {
/**
* Standard implementation for processing Page Object annotations
*/
public Annotations(Field field);
public boolean isLookupCached();
public By buildBy();
protected Field getField();
protected By buildByFromDefault();
protected void assertValidAnnotations();
}public abstract class AbstractFindByBuilder {
/**
* Abstract base class for building By instances from annotations
*/
public abstract By buildIt(Object annotation, Field field);
protected By buildByFromFindBy(FindBy findBy);
protected By buildByFromShortFindBy(FindBy findBy);
protected By buildByFromLongFindBy(FindBy findBy);
protected void assertValidFindBy(FindBy findBy);
protected void assertValidFindBys(FindBys findBys);
protected void assertValidFindAll(FindAll findAll);
}public class DynamicPage {
private WebDriver driver;
public DynamicPage(WebDriver driver) {
this.driver = driver;
// Use AjaxElementLocatorFactory for dynamic content
AjaxElementLocatorFactory factory = new AjaxElementLocatorFactory(driver, 15);
PageFactory.initElements(factory, this);
}
@FindBy(id = "dynamic-content")
private WebElement dynamicContent;
@FindBy(className = "async-loaded")
private List<WebElement> asyncElements;
}public abstract class BasePage {
@FindBy(id = "header")
protected WebElement header;
@FindBy(id = "footer")
protected WebElement footer;
public BasePage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
}
public class HomePage extends BasePage {
@FindBy(id = "welcome-message")
private WebElement welcomeMessage;
public HomePage(WebDriver driver) {
super(driver);
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-support