Let us build page objects for a different type of application — the Shopping Portal with catalog browsing, search, cart, and checkout. This shows how POM handles complex multi-page flows.
package com.practice.pages.shopping;
import com.practice.pages.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class ShopHomePage extends BasePage {
private By searchBox = By.id("search-input");
private By searchButton = By.id("search-btn");
private By cartIcon = By.id("cart-icon");
private By cartCount = By.id("cart-count");
private By featuredProducts = By.cssSelector(".featured-product");
public ShopHomePage(WebDriver driver) {
super(driver);
}
public CatalogPage searchFor(String term) {
type(searchBox, term);
click(searchButton);
return new CatalogPage(driver);
}
public CatalogPage clickCategory(String categoryName) {
click(By.linkText(categoryName));
return new CatalogPage(driver);
}
public CartPage openCart() {
click(cartIcon);
return new CartPage(driver);
}
public int getFeaturedProductCount() {
return findElements(featuredProducts).size();
}
public String getCartCount() {
return getText(cartCount);
}
}package com.practice.pages.shopping;
import com.practice.pages.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import java.util.List;
import java.util.stream.Collectors;
public class CatalogPage extends BasePage {
private By productCards = By.cssSelector(".product-card");
private By productNames = By.cssSelector(".product-card .product-name");
private By sortDropdown = By.id("sort-select");
private By noResults = By.cssSelector(".no-results");
public CatalogPage(WebDriver driver) {
super(driver);
}
public int getProductCount() {
return findElements(productCards).size();
}
public List<String> getProductNames() {
return findElements(productNames).stream()
.map(WebElement::getText)
.collect(Collectors.toList());
}
public CatalogPage sortBy(String option) {
selectByVisibleText(sortDropdown, option);
return this; // Stay on same page — enables chaining
}
public CatalogPage addProductByName(String productName) {
click(By.xpath(
"//div[contains(@class,'product-card')]" +
"[.//span[text()='" + productName + "']]" +
"//button[contains(@class,'add-to-cart')]"));
return this;
}
public CatalogPage addProductByIndex(int index) {
List<WebElement> buttons = findElements(
By.cssSelector(".product-card .add-to-cart-btn"));
if (index < buttons.size()) {
buttons.get(index).click();
}
return this;
}
public boolean hasNoResults() {
return isPresent(noResults);
}
}package com.practice.pages.shopping;
import com.practice.pages.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import java.util.List;
import java.util.stream.Collectors;
public class CartPage extends BasePage {
private By cartItems = By.cssSelector(".cart-item");
private By cartItemNames = By.cssSelector(".cart-item .item-name");
private By cartTotal = By.id("cart-total");
private By checkoutButton = By.id("checkout-btn");
private By emptyCartMsg = By.cssSelector(".empty-cart");
private By continueShopping = By.id("continue-shopping");
public CartPage(WebDriver driver) {
super(driver);
}
public int getItemCount() {
return findElements(cartItems).size();
}
public List<String> getItemNames() {
return findElements(cartItemNames).stream()
.map(WebElement::getText)
.collect(Collectors.toList());
}
public String getCartTotal() {
return getText(cartTotal);
}
public boolean isEmpty() {
return isPresent(emptyCartMsg);
}
public CheckoutPage proceedToCheckout() {
click(checkoutButton);
return new CheckoutPage(driver);
}
public ShopHomePage continueShopping() {
click(continueShopping);
return new ShopHomePage(driver);
}
}package com.practice.pages.shopping;
import com.practice.pages.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class CheckoutPage extends BasePage {
private By firstName = By.id("first-name");
private By lastName = By.id("last-name");
private By email = By.id("email");
private By address = By.id("address");
private By city = By.id("city");
private By zipCode = By.id("zip-code");
private By placeOrderBtn = By.id("place-order-btn");
private By orderConfirmation = By.id("order-confirmation");
private By orderNumber = By.id("order-number");
public CheckoutPage(WebDriver driver) {
super(driver);
}
public void fillShipping(String first, String last,
String emailAddr, String addr,
String cityName, String zip) {
type(firstName, first);
type(lastName, last);
type(email, emailAddr);
type(address, addr);
type(city, cityName);
type(zipCode, zip);
}
public void placeOrder() {
click(placeOrderBtn);
}
public boolean isOrderConfirmed() {
return isDisplayed(orderConfirmation);
}
public String getOrderNumber() {
return getText(orderNumber);
}
}Key Point: Each page class encapsulates its locators and actions. CatalogPage methods return "this" for chaining, CartPage.proceedToCheckout() returns CheckoutPage.