๐๏ธ Module 6 โ Page Object Model (POM)
Page Object Model is the most widely used design pattern in test automation.
โ What is POM?โ
Without POM:
// Test file doing everything
test('Login test', async ({ page }) => {
await page.goto('https://practicetestautomation.com/practice-test-login/');
await page.locator('#username').fill('student');
await page.locator('#password').fill('Password123');
await page.locator('#submit').click();
await expect(page.locator('.post-title')).toHaveText('Logged In Successfully');
});
With POM:
// Test file is clean and readable
test('Login test', async ({ page }) => {
const homePage = new HomePage(page);
const loginResultsPage = new LoginResultsPage(page);
await homePage.goto();
await homePage.login('student', 'Password123');
await loginResultsPage.verifyLoginSuccess();
});
โ Why Use POM?โ
| Without POM | With POM |
|---|---|
| Locators duplicated everywhere | Locators defined once |
| Hard to maintain | Easy to maintain |
| Tests hard to read | Tests read like plain English |
| One change = fix everywhere | One change = fix in one place |
โ Project Structureโ
playwright-tests/
โ
โโโ pages/
โ โโโ BasePage.js
โ โโโ HomePage.js
โ โโโ LoginResultsPage.js
โ
โโโ tests/
โ โโโ google-search-pom.spec.js
โ
โโโ playwright.config.js
โ BasePageโ
BasePage contains common methods shared across all pages.
class BasePage {
constructor(page) {
this.page = page;
}
async navigate(url) {
await this.page.goto(url);
}
async getTitle() {
return await this.page.title();
}
async waitForPageLoad() {
await this.page.waitForLoadState('domcontentloaded');
}
}
module.exports = { BasePage };
Key Concept:โ
- Every page object extends BasePage
- Common methods written once
- All pages inherit them automatically
โ HomePageโ
Each page object represents one page of the application.
const { BasePage } = require('./BasePage');
class HomePage extends BasePage {
constructor(page) {
super(page);
// Define locators here
this.usernameInput = page.locator('#username');
this.passwordInput = page.locator('#password');
this.loginButton = page.locator('#submit');
}
async goto() {
await this.navigate('https://practicetestautomation.com/practice-test-login/');
}
async login(username, password) {
await this.usernameInput.fill(username);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
}
module.exports = { HomePage };
Key Concepts:โ
constructorโ defines locatorssuper(page)โ passes page to BasePage- Methods represent user actions
โ LoginResultsPageโ
const { BasePage } = require('./BasePage');
const { expect } = require('@playwright/test');
class LoginResultsPage extends BasePage {
constructor(page) {
super(page);
this.successMessage = page.locator('.post-title');
this.errorMessage = page.locator('#error');
this.logoutButton = page.getByRole('link', { name: 'Log out' });
}
async verifyLoginSuccess() {
await expect(this.successMessage).toHaveText('Logged In Successfully');
}
async verifyLogoutButtonVisible() {
await expect(this.logoutButton).toBeVisible();
}
async verifyLoginFailed() {
await expect(this.errorMessage).toBeVisible();
}
}
module.exports = { LoginResultsPage };
โ Test File Using POMโ
const { test } = require('@playwright/test');
const { HomePage } = require('../pages/HomePage');
const { LoginResultsPage } = require('../pages/LoginResultsPage');
test.describe('Login Page - POM', () => {
test('Successful login with valid credentials', async ({ page }) => {
const homePage = new HomePage(page);
const loginResultsPage = new LoginResultsPage(page);
await homePage.goto();
await homePage.login('student', 'Password123');
await loginResultsPage.verifyLoginSuccess();
await loginResultsPage.verifyLogoutButtonVisible();
});
test('Failed login with invalid credentials', async ({ page }) => {
const homePage = new HomePage(page);
const loginResultsPage = new LoginResultsPage(page);
await homePage.goto();
await homePage.login('wronguser', 'wrongpassword');
await loginResultsPage.verifyLoginFailed();
});
});
โ POM Best Practicesโ
- โ One file per page
- โ Define all locators in constructor
- โ Methods should represent user actions
- โ Assertions can go in page objects or test files
- โ Always extend BasePage
- โ Keep test files clean and readable
- โ Never hardcode URLs in test files
โ POM Flow Diagramโ
Test File
โ
โโโ HomePage
โ โโโ goto()
โ โโโ login()
โ
โโโ LoginResultsPage
โโโ verifyLoginSuccess()
โโโ verifyLogoutButtonVisible()
โโโ verifyLoginFailed()
๐ฏ Summaryโ
- โ POM separates test logic from page logic
- โ BasePage holds shared methods
- โ Each page has its own class
- โ Tests become clean and readable
- โ Easy to maintain when UI changes
๐ Keep Goingโ
Apply what you learned in this module:
- ๐งช Practice Exercises โ hands-on tasks to build real skills
- ๐๏ธ Real Projects โ real-world automation examples