Day 5: Page Object Model — Structure Tests That Scale
This is Day 5 of the 21-Day Playwright with TypeScript Challenge. One lesson per day. Zero to production-ready in 3 weeks.
🎭 Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.
Without POM, test code becomes unmaintainable spaghetti. Page Objects separate what you interact with (locators) from what you test (assertions). One locator change = one file change, not 50.
Contents
LoginPage Example
// src/pages/LoginPage.ts
import { Page, Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly loginButton: Locator;
readonly errorAlert: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.getByLabel('Email');
this.passwordInput = page.getByLabel('Password');
this.loginButton = page.getByRole('button', { name: 'Sign in' });
this.errorAlert = page.getByRole('alert');
}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
async expectError(message: string) {
await expect(this.errorAlert).toContainText(message);
}
}
🚀 Level Up Your Playwright
From locators to CI pipelines — build a production-grade Playwright + TypeScript framework step by step.
Test File Using POM
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../src/pages/LoginPage';
test.describe('Login', () => {
let loginPage: LoginPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
await loginPage.goto();
});
test('valid credentials redirect to dashboard', async ({ page }) => {
await loginPage.login('admin@test.com', 'password123');
await expect(page).toHaveURL(/dashboard/);
});
test('invalid password shows error', async () => {
await loginPage.login('admin@test.com', 'wrong');
await loginPage.expectError('Invalid credentials');
});
});
POM Anti-Patterns
- God Object: 500-line page class. Split into components (Navbar, Footer, Sidebar).
- Deep inheritance: BasePage > AbstractPage > LoginPage. Use composition, not inheritance.
- Business logic in POM: Page objects interact with UI only. Test files own assertions and logic.
- Returning page objects: Methods return void. Test navigates explicitly.
Tomorrow (Day 6): Fixtures — dependency injection that eliminates boilerplate.
🎓 Master Playwright End to End
Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.
