Every page object needs a WebDriver, a WebDriverWait, and utility methods like click(), type(), and getText(). Instead of duplicating these in every page class, create a BasePage that all page classes extend. BasePage is the single most important class in your framework.
Think of BasePage like the foundation of a building. Every floor (page class) sits on it. If you need to add earthquake proofing (logging, retry logic), you add it to the foundation once and every floor benefits.
package com.practice.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.List;
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
protected void click(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator))
.click();
}
protected void type(By locator, String text) {
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(locator));
element.clear();
element.sendKeys(text);
}
protected String getText(By locator) {
return wait.until(
ExpectedConditions.visibilityOfElementLocated(locator))
.getText();
}
protected boolean isDisplayed(By locator) {
try {
return wait.until(
ExpectedConditions.visibilityOfElementLocated(locator))
.isDisplayed();
} catch (Exception e) {
return false;
}
}
protected boolean isPresent(By locator) {
return driver.findElements(locator).size() > 0;
}
protected void selectByVisibleText(By locator, String text) {
Select dropdown = new Select(wait.until(
ExpectedConditions.visibilityOfElementLocated(locator)));
dropdown.selectByVisibleText(text);
}
protected List<WebElement> findElements(By locator) {
return driver.findElements(locator);
}
public String getPageTitle() {
return driver.getTitle();
}
public String getCurrentUrl() {
return driver.getCurrentUrl();
}
}Every method in BasePage uses explicit waits. This means no page class ever needs to create its own WebDriverWait or write wait.until() directly. If you want to add logging to every click or capture intermediate screenshots, add it to BasePage once — every page class inherits the change.
Make BasePage methods protected, not public. Only page classes should call click() and type(). Test classes should call high-level page methods like loginAs() or searchFor(), not low-level actions like click(someLocator).
Q: What is BasePage and why do you use it?
A: BasePage is the parent class that all page objects extend. It holds the WebDriver and WebDriverWait instances and provides common utility methods like click(), type(), getText(), isDisplayed(), and dropdown selection. Every page class inherits these methods instead of duplicating WebDriver calls. If I need to add retry logic or logging to all interactions, I add it once in BasePage. It is the foundation of the framework.
Key Point: BasePage provides click(), type(), getText(), and waits. All page classes extend it. Add cross-cutting concerns here once.