📋 Table of Contents
- What is Page Object Model?
- Why Use Page Object Model?
- The Problem with Manual POM Creation
- Automatic POM Generation Solution
- Step-by-Step: Using LocatorLab
- Generated JavaScript POM Example
- Generated Python POM Example
- Generated Java POM Example
- Best Practices for Generated POMs
- Time Savings Calculator
- Conclusion
What is Page Object Model (POM)?
The Page Object Model is a design pattern that creates an object repository for web elements. Each web page has a corresponding Page Object class that encapsulates the page structure and provides methods for interacting with it.
Simple Example:
// Instead of this in your test:
await page.locator('#email').fill('test@example.com');
await page.locator('#password').fill('password123');
await page.locator('#submit').click();
// You write this:
await loginPage.login('test@example.com', 'password123');
The Page Object hides the implementation details and provides a clean, reusable interface for your tests.
Why Use Page Object Model?
✅ Benefits:
- Maintainability - Change locators in one place when UI updates
- Reusability - Share page objects across multiple tests
- Readability - Tests read like user stories
- Reduced Code Duplication - DRY principle
- Separation of Concerns - Test logic vs page structure
- Easier Collaboration - Clear boundaries between pages
Without POM:
// test1.spec.js
await page.locator('#search-input').fill('laptop');
await page.locator('.search-button').click();
// test2.spec.js
await page.locator('#search-input').fill('phone');
await page.locator('.search-button').click();
// If locator changes, update in 50+ places! 😱
With POM:
// Both tests use:
await searchPage.search('laptop');
await searchPage.search('phone');
// If locator changes, update in 1 place! ✅
The Problem with Manual POM Creation
Creating Page Objects manually is time-consuming and error-prone:
❌ Manual Process:
- Open the page in browser
- Inspect each element (30-50 per page)
- Choose the best locator strategy
- Write the locator in your Page Object class
- Create getter methods for each element
- Write action methods (login, search, etc.)
- Repeat for every page in your app
⏱️ Time Investment
Manual POM creation takes 2-4 hours per page. For a 20-page application, that's 40-80 hours of repetitive work!
Common Issues:
- ❌ Inconsistent locator strategies across pages
- ❌ Fragile XPath that breaks easily
- ❌ Missing elements discovered during test writing
- ❌ No quality scoring for locator stability
- ❌ Tedious, repetitive work
Automatic POM Generation Solution
Modern tools can automatically scan your pages and generate complete Page Object classes in seconds.
How It Works:
- Scan Page - Tool analyzes all interactive elements
- Smart Locators - Chooses best strategy for each element
- Quality Scoring - Rates locator stability (0-10)
- Generate Code - Creates POM class in your language
- Export & Use - Copy into your test project
⚡ Speed Comparison
Manual: 2-4 hours per page
Automatic: 30 seconds per page
Time Saved: 95-99%
Step-by-Step: Using LocatorLab
Let's walk through generating a Page Object for a login page using LocatorLab.
Step 1: Navigate to Your Page
https://example.com/login
Step 2: Open LocatorLab Sidepanel
Click the LocatorLab extension icon and enable Side Panel mode.
Step 3: Scan the Page
Click "Scan Page Elements" button or press Alt+Shift+S
LocatorLab will:
- ✅ Find all interactive elements (inputs, buttons, links)
- ✅ Generate 10+ locator strategies for each
- ✅ Score each locator (0-10 quality rating)
- ✅ Show you a preview with element types
Step 4: Review Scanned Elements
The scanner found:
- 2 Input fields (email, password)
- 1 Button (submit)
- 1 Link (forgot password)
- 1 Link (sign up)
Step 5: Export Page Object Model
Click "Export POM" and choose your language:
- JavaScript (Playwright/WebdriverIO)
- Python (Selenium/Playwright)
- Java (Selenium)
LocatorLab generates a complete, production-ready Page Object class!
Generated JavaScript POM Example (Playwright)
Auto-Generated LoginPage.js:
// LoginPage.js - Generated by LocatorLab
// Date: 2026-01-14
// Page: https://example.com/login
class LoginPage {
constructor(page) {
this.page = page;
// Locators (Quality Score: 10/10)
this.emailInput = page.getByTestId('email-input');
// Locators (Quality Score: 10/10)
this.passwordInput = page.getByTestId('password-input');
// Locators (Quality Score: 9/10)
this.submitButton = page.getByRole('button', { name: 'Sign In' });
// Locators (Quality Score: 8/10)
this.forgotPasswordLink = page.getByText('Forgot password?');
// Locators (Quality Score: 8/10)
this.signUpLink = page.getByRole('link', { name: 'Sign up' });
}
// Navigate to login page
async goto() {
await this.page.goto('https://example.com/login');
}
// Fill email field
async fillEmail(email) {
await this.emailInput.fill(email);
}
// Fill password field
async fillPassword(password) {
await this.passwordInput.fill(password);
}
// Click submit button
async clickSubmit() {
await this.submitButton.click();
}
// Complete login action
async login(email, password) {
await this.fillEmail(email);
await this.fillPassword(password);
await this.clickSubmit();
}
// Click forgot password link
async clickForgotPassword() {
await this.forgotPasswordLink.click();
}
// Click sign up link
async clickSignUp() {
await this.signUpLink.click();
}
}
module.exports = { LoginPage };
Using the Generated Page Object:
// login.spec.js
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
test('user can login successfully', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('test@example.com', 'SecurePass123');
await expect(page).toHaveURL(/.*dashboard/);
});
💡 Notice
LocatorLab chose the best locator strategies automatically:
- data-testid for inputs (most stable)
- getByRole for buttons (accessibility-first)
- getByText for links (user-visible)
Generated Python POM Example (Selenium)
Auto-Generated login_page.py:
# login_page.py - Generated by LocatorLab
# Date: 2026-01-14
# Page: https://example.com/login
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
# URL
URL = "https://example.com/login"
# Locators (Quality Score: 10/10)
EMAIL_INPUT = (By.CSS_SELECTOR, '[data-testid="email-input"]')
# Locators (Quality Score: 10/10)
PASSWORD_INPUT = (By.CSS_SELECTOR, '[data-testid="password-input"]')
# Locators (Quality Score: 9/10)
SUBMIT_BUTTON = (By.XPATH, '//button[text()="Sign In"]')
# Locators (Quality Score: 8/10)
FORGOT_PASSWORD_LINK = (By.LINK_TEXT, 'Forgot password?')
# Locators (Quality Score: 8/10)
SIGN_UP_LINK = (By.LINK_TEXT, 'Sign up')
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def goto(self):
"""Navigate to login page"""
self.driver.get(self.URL)
def fill_email(self, email):
"""Fill email field"""
element = self.wait.until(
EC.element_to_be_clickable(self.EMAIL_INPUT)
)
element.clear()
element.send_keys(email)
def fill_password(self, password):
"""Fill password field"""
element = self.wait.until(
EC.element_to_be_clickable(self.PASSWORD_INPUT)
)
element.clear()
element.send_keys(password)
def click_submit(self):
"""Click submit button"""
element = self.wait.until(
EC.element_to_be_clickable(self.SUBMIT_BUTTON)
)
element.click()
def login(self, email, password):
"""Complete login action"""
self.fill_email(email)
self.fill_password(password)
self.click_submit()
def click_forgot_password(self):
"""Click forgot password link"""
element = self.wait.until(
EC.element_to_be_clickable(self.FORGOT_PASSWORD_LINK)
)
element.click()
def click_sign_up(self):
"""Click sign up link"""
element = self.wait.until(
EC.element_to_be_clickable(self.SIGN_UP_LINK)
)
element.click()
Using the Python Page Object:
# test_login.py
import pytest
from selenium import webdriver
from pages.login_page import LoginPage
def test_user_can_login():
driver = webdriver.Chrome()
login_page = LoginPage(driver)
login_page.goto()
login_page.login('test@example.com', 'SecurePass123')
assert '/dashboard' in driver.current_url
driver.quit()
Generated Java POM Example (Selenium)
Auto-Generated LoginPage.java:
// LoginPage.java - Generated by LocatorLab
// Date: 2026-01-14
// Page: https://example.com/login
package 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.WebDriverWait;
import java.time.Duration;
public class LoginPage {
private WebDriver driver;
private WebDriverWait wait;
// URL
private static final String URL = "https://example.com/login";
// Locators (Quality Score: 10/10)
private By emailInput = By.cssSelector("[data-testid='email-input']");
// Locators (Quality Score: 10/10)
private By passwordInput = By.cssSelector("[data-testid='password-input']");
// Locators (Quality Score: 9/10)
private By submitButton = By.xpath("//button[text()='Sign In']");
// Locators (Quality Score: 8/10)
private By forgotPasswordLink = By.linkText("Forgot password?");
// Locators (Quality Score: 8/10)
private By signUpLink = By.linkText("Sign up");
public LoginPage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public void goTo() {
driver.get(URL);
}
public void fillEmail(String email) {
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(emailInput)
);
element.clear();
element.sendKeys(email);
}
public void fillPassword(String password) {
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(passwordInput)
);
element.clear();
element.sendKeys(password);
}
public void clickSubmit() {
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(submitButton)
);
element.click();
}
public void login(String email, String password) {
fillEmail(email);
fillPassword(password);
clickSubmit();
}
public void clickForgotPassword() {
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(forgotPasswordLink)
);
element.click();
}
public void clickSignUp() {
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(signUpLink)
);
element.click();
}
}
Best Practices for Generated POMs
✅ DO:
- Review Generated Code - Verify locators make sense
- Add Business Logic - Enhance with domain-specific methods
- Use Meaningful Names - Rename auto-generated methods if needed
- Group Related Actions - Combine steps into workflows
- Add Assertions - Include validation methods in POM
- Version Control - Commit generated POMs to Git
❌ DON'T:
- Don't Blindly Trust - Always review generated locators
- Don't Put Tests in POMs - Only page interactions
- Don't Ignore Quality Scores - Fix low-scoring locators
- Don't Over-Engineer - Keep POMs simple
Enhance Generated POMs:
// Add validation methods
async isEmailErrorVisible() {
return await this.emailError.isVisible();
}
// Add custom wait conditions
async waitForLoginComplete() {
await this.page.waitForURL(/.*dashboard/);
}
// Add business logic
async loginAsAdmin() {
await this.login('admin@example.com', process.env.ADMIN_PASSWORD);
}
Time Savings Calculator
Let's calculate the actual time savings:
Example Project: E-commerce Site
| Task | Manual | Automated | Saved |
|---|---|---|---|
| Home Page (15 elements) | 2 hours | 30 sec | 1h 59m |
| Product Page (25 elements) | 3 hours | 45 sec | 2h 59m |
| Cart Page (12 elements) | 1.5 hours | 25 sec | 1h 29m |
| Checkout (30 elements) | 4 hours | 1 min | 3h 59m |
| Login/Signup (8 elements) | 1 hour | 20 sec | 59m |
| TOTAL (5 pages) | 11.5 hours | 3 minutes | 11h 27m (99% saved!) |
💰 ROI Calculation
Assumptions: QA Engineer salary = $60/hour
Cost of Manual POM: 11.5 hours × $60 = $690
Cost with LocatorLab: 3 minutes × $60 = $3
💵 Money Saved: $687 per project
Conclusion: Stop Writing POMs Manually
Automatic Page Object Model generation is a game-changer for test automation teams:
- ✅ Save 95-99% of time compared to manual creation
- ✅ Get consistent, high-quality locators across all pages
- ✅ Reduce errors from manual coding
- ✅ Start testing faster with ready-to-use POMs
- ✅ Support multiple languages (JS, Python, Java)
- ✅ Quality scoring guides you to stable locators
The Modern Workflow:
- Scan page with LocatorLab (30 seconds)
- Review generated locators
- Export POM in your language
- Copy into your project
- Enhance with business logic
- Start writing tests immediately
🚀 Try LocatorLab Today
Stop wasting hours creating Page Objects manually. LocatorLab scans your pages, generates intelligent locators with quality scores, and exports production-ready POM classes in JavaScript, Python, or Java.
Remember: The best code is the code you don't have to write.