CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-seleniumhq-selenium--selenium-api

Selenium WebDriver core API for automating web browsers across different platforms and programming languages.

Pending
Overview
Eval results
Files

page-objects.mddocs/

Page Object Model Support

Utilities for implementing the Page Object Model pattern including element initialization, locator annotations, and event handling for maintainable test automation.

Capabilities

PageFactory Class

Utility class for initializing Page Object Model classes with automatic element location and caching.

/**
 * PageFactory utility for initializing Page Object Model classes
 * Automatically initializes WebElement fields marked with @FindBy annotations
 */
class PageFactory {
    /**
     * Initialize page object elements using default element locator factory
     * @param driver - WebDriver instance for element location
     * @param page - Page object instance to initialize
     */
    static void initElements(WebDriver driver, Object page);
    
    /**
     * Initialize page object elements using custom element locator factory
     * @param factory - Custom ElementLocatorFactory for element location strategy
     * @param page - Page object instance to initialize
     */
    static void initElements(ElementLocatorFactory factory, Object page);
    
    /**
     * Initialize page object elements with custom field decorator
     * @param decorator - FieldDecorator for customizing element initialization
     * @param page - Page object instance to initialize
     */
    static void initElements(FieldDecorator decorator, Object page);
}

FindBy Annotation

Annotation for marking WebElement fields with their location strategy.

/**
 * FindBy annotation for specifying element location strategy
 * Used on WebElement fields in Page Object classes
 */
@interface FindBy {
    /**
     * Locate by ID attribute
     * @return Element ID value
     */
    String id() default "";
    
    /**
     * Locate by name attribute
     * @return Element name value
     */
    String name() default "";
    
    /**
     * Locate by CSS class name
     * @return Element class name
     */
    String className() default "";
    
    /**
     * Locate by HTML tag name
     * @return Element tag name
     */
    String tagName() default "";
    
    /**
     * Locate by XPath expression
     * @return XPath expression
     */
    String xpath() default "";
    
    /**
     * Locate by CSS selector
     * @return CSS selector expression
     */
    String css() default "";
    
    /**
     * Locate link by exact text
     * @return Link text content
     */
    String linkText() default "";
    
    /**
     * Locate link by partial text
     * @return Partial link text content
     */
    String partialLinkText() default "";
}

FindBys Annotation

Annotation for combining multiple locators with AND logic.

/**
 * FindBys annotation for chaining multiple locators with AND logic
 * Element must match ALL specified locator conditions
 */
@interface FindBys {
    /**
     * Array of FindBy annotations that must all match
     * @return Array of FindBy conditions
     */
    FindBy[] value();
}

FindAll Annotation

Annotation for combining multiple locators with OR logic.

/**
 * FindAll annotation for combining multiple locators with OR logic
 * Element matches if ANY of the specified locator conditions match
 */
@interface FindAll {
    /**
     * Array of FindBy annotations where any can match
     * @return Array of FindBy conditions
     */
    FindBy[] value();
}

CacheLookup Annotation

Annotation for caching element lookups to improve performance.

/**
 * CacheLookup annotation for caching WebElement lookups
 * Element is located once and cached for subsequent access
 * Use with caution on dynamic content
 */
@interface CacheLookup {
}

Usage Examples

Basic Page Object Pattern

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

// Login page object class
public class LoginPage {
    private WebDriver driver;
    
    // Element declarations with @FindBy annotations
    @FindBy(id = "username")
    private WebElement usernameField;
    
    @FindBy(id = "password")  
    private WebElement passwordField;
    
    @FindBy(xpath = "//button[@type='submit']")
    private WebElement loginButton;
    
    @FindBy(className = "error-message")
    private WebElement errorMessage;
    
    @FindBy(linkText = "Forgot Password?")
    private WebElement forgotPasswordLink;
    
    // Constructor
    public LoginPage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }
    
    // Page actions
    public void enterUsername(String username) {
        usernameField.clear();
        usernameField.sendKeys(username);
    }
    
    public void enterPassword(String password) {
        passwordField.clear();
        passwordField.sendKeys(password);
    }
    
    public void clickLogin() {
        loginButton.click();
    }
    
    public void login(String username, String password) {
        enterUsername(username);
        enterPassword(password);
        clickLogin();
    }
    
    public String getErrorMessage() {
        return errorMessage.getText();
    }
    
    public boolean isErrorDisplayed() {
        return errorMessage.isDisplayed();
    }
    
    public void clickForgotPassword() {
        forgotPasswordLink.click();
    }
}

// Usage in test
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
loginPage.login("testuser", "testpass");

Advanced Locator Strategies

public class AdvancedPage {
    @FindBy(css = "input[data-testid='search-input']")
    private WebElement searchBox;
    
    @FindBy(xpath = "//div[@class='product-list']//div[contains(@class, 'product-item')]")
    private List<WebElement> productItems;
    
    // Using FindBys for AND logic (element must match ALL conditions)
    @FindBys({
        @FindBy(className = "btn"),
        @FindBy(xpath = ".//span[text()='Submit']")
    })
    private WebElement submitButton;
    
    // Using FindAll for OR logic (element matches ANY condition)
    @FindAll({
        @FindBy(id = "closeButton"),
        @FindBy(xpath = "//button[text()='Close']"),
        @FindBy(css = ".close-btn")
    })
    private WebElement closeButton;
    
    // Cached lookup for static elements
    @CacheLookup
    @FindBy(id = "header-logo")
    private WebElement headerLogo;
    
    // Multiple elements with same locator
    @FindBy(className = "nav-item")
    private List<WebElement> navigationItems;
    
    public AdvancedPage(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }
    
    public void searchFor(String query) {
        searchBox.clear();
        searchBox.sendKeys(query);
        searchBox.submit();
    }
    
    public int getProductCount() {
        return productItems.size();
    }
    
    public List<String> getNavigationItemTexts() {
        return navigationItems.stream()
                .map(WebElement::getText)
                .collect(Collectors.toList());
    }
}

Page Object with Wait Strategies

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

public class WaitingPage {
    private WebDriver driver;
    private WebDriverWait wait;
    
    @FindBy(id = "dynamic-content")
    private WebElement dynamicContent;
    
    @FindBy(className = "loading-spinner")
    private WebElement loadingSpinner;
    
    @FindBy(id = "ajax-button")
    private WebElement ajaxButton;
    
    @FindBy(css = ".result-item")
    private List<WebElement> results;
    
    public WaitingPage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        PageFactory.initElements(driver, this);
    }
    
    public void waitForPageLoad() {
        wait.until(ExpectedConditions.invisibilityOf(loadingSpinner));
        wait.until(ExpectedConditions.visibilityOf(dynamicContent));
    }
    
    public void clickAjaxButtonAndWait() {
        ajaxButton.click();
        wait.until(ExpectedConditions.invisibilityOf(loadingSpinner));
        wait.until(driver -> results.size() > 0);
    }
    
    public String getDynamicContentText() {
        wait.until(ExpectedConditions.visibilityOf(dynamicContent));
        return dynamicContent.getText();
    }
}

Hierarchical Page Objects

// Base page class with common elements
public abstract class BasePage {
    protected WebDriver driver;
    protected WebDriverWait wait;
    
    @FindBy(id = "header")
    protected WebElement header;
    
    @FindBy(id = "footer")
    protected WebElement footer;
    
    @FindBy(className = "user-menu")
    protected WebElement userMenu;
    
    @FindBy(linkText = "Logout")
    protected WebElement logoutLink;
    
    public BasePage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        PageFactory.initElements(driver, this);
    }
    
    public void logout() {
        userMenu.click();
        logoutLink.click();
    }
    
    public String getPageTitle() {
        return driver.getTitle();
    }
    
    public abstract boolean isPageLoaded();
}

// Specific page extending base
public class DashboardPage extends BasePage {
    @FindBy(className = "dashboard-widget")
    private List<WebElement> widgets;
    
    @FindBy(id = "welcome-message")
    private WebElement welcomeMessage;
    
    @FindBy(css = ".quick-action-btn")
    private List<WebElement> quickActionButtons;
    
    public DashboardPage(WebDriver driver) {
        super(driver);
    }
    
    @Override
    public boolean isPageLoaded() {
        return welcomeMessage.isDisplayed() && widgets.size() > 0;
    }
    
    public int getWidgetCount() {
        return widgets.size();
    }
    
    public void clickQuickAction(String actionText) {
        quickActionButtons.stream()
            .filter(btn -> btn.getText().equals(actionText))
            .findFirst()
            .ifPresent(WebElement::click);
    }
}

Custom Element Locator Factory

import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;

// Custom locator factory with retry logic
public class RetryingElementLocatorFactory implements ElementLocatorFactory {
    private final WebDriver driver;
    private final int timeOutInSeconds;
    
    public RetryingElementLocatorFactory(WebDriver driver, int timeOutInSeconds) {
        this.driver = driver;
        this.timeOutInSeconds = timeOutInSeconds;
    }
    
    @Override
    public ElementLocator createLocator(Field field) {
        return new RetryingElementLocator(driver, field, timeOutInSeconds);
    }
}

// Custom element locator with retry mechanism
public class RetryingElementLocator implements ElementLocator {
    private final WebDriver driver;
    private final By by;
    private final boolean shouldCache;
    private final int timeOutInSeconds;
    private WebElement cachedElement;
    private List<WebElement> cachedElementList;
    
    public RetryingElementLocator(WebDriver driver, Field field, int timeOutInSeconds) {
        this.driver = driver;
        this.timeOutInSeconds = timeOutInSeconds;
        this.shouldCache = field.getAnnotation(CacheLookup.class) != null;
        this.by = buildByFromField(field);
    }
    
    @Override
    public WebElement findElement() {
        if (shouldCache && cachedElement != null) {
            return cachedElement;
        }
        
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(timeOutInSeconds));
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(by));
        
        if (shouldCache) {
            cachedElement = element;
        }
        
        return element;
    }
    
    @Override
    public List<WebElement> findElements() {
        if (shouldCache && cachedElementList != null) {
            return cachedElementList;
        }
        
        List<WebElement> elements = driver.findElements(by);
        
        if (shouldCache) {
            cachedElementList = elements;
        }
        
        return elements;
    }
    
    private By buildByFromField(Field field) {
        FindBy findBy = field.getAnnotation(FindBy.class);
        if (findBy != null) {
            return buildByFromFindBy(findBy);
        }
        throw new IllegalArgumentException("Field must be annotated with @FindBy");
    }
    
    private By buildByFromFindBy(FindBy findBy) {
        if (!findBy.id().isEmpty()) return By.id(findBy.id());
        if (!findBy.name().isEmpty()) return By.name(findBy.name());
        if (!findBy.className().isEmpty()) return By.className(findBy.className());
        if (!findBy.tagName().isEmpty()) return By.tagName(findBy.tagName());
        if (!findBy.xpath().isEmpty()) return By.xpath(findBy.xpath());
        if (!findBy.css().isEmpty()) return By.cssSelector(findBy.css());
        if (!findBy.linkText().isEmpty()) return By.linkText(findBy.linkText());
        if (!findBy.partialLinkText().isEmpty()) return By.partialLinkText(findBy.partialLinkText());
        
        throw new IllegalArgumentException("No locator strategy specified in @FindBy");
    }
}

// Usage with custom factory
public class CustomPage {
    @FindBy(id = "slow-loading-element")
    private WebElement slowElement;
    
    public CustomPage(WebDriver driver) {
        RetryingElementLocatorFactory factory = new RetryingElementLocatorFactory(driver, 15);
        PageFactory.initElements(factory, this);
    }
}

Page Object Best Practices

// Complete page object example with best practices
public class ProductPage {
    private final WebDriver driver;
    private final WebDriverWait wait;
    
    // Page elements
    @FindBy(id = "product-title")
    private WebElement productTitle;
    
    @FindBy(className = "price")
    private WebElement price;
    
    @FindBy(id = "add-to-cart")
    private WebElement addToCartButton;
    
    @FindBy(id = "quantity")
    private WebElement quantityInput;
    
    @FindBy(css = ".product-image img")
    private WebElement productImage;
    
    @FindBy(className = "reviews")
    private List<WebElement> reviews;
    
    public ProductPage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        PageFactory.initElements(driver, this);
    }
    
    // Verification methods
    public boolean isProductPageLoaded() {
        return productTitle.isDisplayed() && price.isDisplayed();
    }
    
    // Information retrieval methods
    public String getProductTitle() {
        return productTitle.getText();
    }
    
    public String getPrice() {
        return price.getText();
    }
    
    public int getReviewCount() {
        return reviews.size();
    }
    
    // Action methods
    public void setQuantity(int quantity) {
        quantityInput.clear();
        quantityInput.sendKeys(String.valueOf(quantity));
    }
    
    public void addToCart() {
        wait.until(ExpectedConditions.elementToBeClickable(addToCartButton));
        addToCartButton.click();
    }
    
    public void addToCartWithQuantity(int quantity) {
        setQuantity(quantity);
        addToCart();
    }
    
    // Navigation methods
    public CartPage addToCartAndGoToCart(int quantity) {
        addToCartWithQuantity(quantity);
        // Wait for success indication or navigate to cart
        return new CartPage(driver);
    }
    
    // Validation methods
    public boolean isAddToCartButtonEnabled() {
        return addToCartButton.isEnabled();
    }
    
    public boolean isProductImageDisplayed() {
        return productImage.isDisplayed();
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-api

docs

alerts.md

configuration.md

drivers.md

elements.md

index.md

interactions.md

javascript.md

locators.md

page-objects.md

waits.md

webdriver.md

tile.json