Playwright_Page_Object_Model

Playwright Page Object Model (POM)

As automation projects grow, maintaining hundreds or even thousands of test scripts can become challenging. Duplicate code, hard-coded locators, and repeated business logic make test suites difficult to maintain. The Page Object Model (POM) is a design pattern that solves these problems by separating page interactions from test logic. In this guide, you’ll learn what the Page Object Model is, why it’s important, and how to implement it in both TypeScript and Java.


What is the Page Object Model?

The Page Object Model (POM) is a design pattern in which each web page is represented as a separate class.

Instead of writing locators and actions directly inside test scripts, they are organized into page classes.

For example:

Login Page
├── Username field
├── Password field
├── Login button
└── login() method

The test simply calls methods from the page class, making the test more readable and easier to maintain.


Why Use the Page Object Model?

Without POM, test scripts often contain repeated locators and duplicated logic.

Without POM

await page.goto("https://example.com/login");
await page.getByLabel("Username").fill("admin");
await page.getByLabel("Password").fill("admin123");
await page.getByRole("button", { name: "Login" }).click();

If the login button changes, every test using it must be updated.


With POM

await loginPage.login("admin", "admin123");

Only the page class needs to be updated when the UI changes.


Benefits of the Page Object Model

Using POM provides several advantages:

  • Better code organization
  • Reduced code duplication
  • Easier maintenance
  • Improved readability
  • Reusable page methods
  • Simplified debugging
  • Scalable framework design

Recommended Project Structure

TypeScript

playwright-project
├── pages
├── LoginPage.ts
├── HomePage.ts
└── CartPage.ts
├── tests
└── login.spec.ts
├── utils
├── playwright.config.ts
└── package.json

Java

playwright-java
├── pages
│ ├── LoginPage.java
│ ├── HomePage.java
│ └── CartPage.java
├── tests
├── utils
└── pom.xml

Creating a Page Object in TypeScript

LoginPage.ts

import { Page } from '@playwright/test';
export class LoginPage {
constructor(private page: Page) {}
async navigate() {
await this.page.goto('https://example.com/login');
}
async login(username: string, password: string) {
await this.page.getByLabel('Username').fill(username);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Login' }).click();
}
}

The page class contains all locators and page-specific actions.


Using the Page Object in a Test

import { test } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test('Valid Login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login('admin', 'admin123');
});

The test focuses only on business logic.


Creating a Page Object in Java

import com.microsoft.playwright.Page;
public class LoginPage {
private final Page page;
public LoginPage(Page page) {
this.page = page;
}
public void navigate() {
page.navigate("https://example.com/login");
}
public void login(String username, String password) {
page.getByLabel("Username").fill(username);
page.getByLabel("Password").fill(password);
page.getByRole(
AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Login")
).click();
}
}

Using the Java Page Object

LoginPage loginPage = new LoginPage(page);
loginPage.navigate();
loginPage.login("admin", "admin123");

The test remains concise and easy to understand.


Separating Locators and Actions

A Page Object should contain:

  • Element locators
  • Page-specific methods
  • Navigation methods
  • Validation methods

It should not contain test assertions or business workflows spanning multiple pages.


Base Page Pattern

In larger frameworks, common functionality can be moved to a base page.

Example:

export class BasePage {
constructor(protected page: Page) {}
async takeScreenshot() {
await this.page.screenshot();
}
async getTitle() {
return await this.page.title();
}
}

Other page classes can extend this base class.


Combining POM with Fixtures

Playwright fixtures can simplify page creation.

Example:

test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
});

This avoids creating page objects repeatedly in every test.


Common Mistakes

Adding Assertions Inside Page Objects

Avoid mixing verification with page interaction.

Incorrect:

async verifyLogin() {
await expect(this.page).toHaveTitle("Dashboard");
}

Assertions belong in test files, not page classes.


Creating Large Page Classes

Keep page objects focused on a single page or component.


Duplicating Common Methods

Move shared functionality into a base page or utility class.


Hard-Coding Test Data

Use configuration files or test data providers instead of embedding values in page classes.


Best Practices

  • One class per page
  • Keep locators private when possible
  • Expose meaningful business methods
  • Avoid duplicate locators
  • Use Playwright locators (getByRole, getByLabel, getByTestId)
  • Store reusable methods in a base page
  • Keep assertions in test classes
  • Follow consistent naming conventions

POM vs Tests

Page ObjectTest File
LocatorsTest scenarios
Page actionsAssertions
NavigationBusiness flow
Reusable methodsTest data

This separation improves readability and maintainability.


Frequently Asked Questions

What is the Page Object Model?

The Page Object Model is a design pattern that represents each web page as a class containing locators and reusable methods.


Why is POM important?

It reduces code duplication, improves maintainability, and keeps test scripts clean.


Should assertions be placed inside Page Objects?

Generally, no. Assertions should remain in the test layer, while page objects focus on interactions.


Can Playwright use POM with TypeScript?

Yes. TypeScript is the most common language used for implementing Playwright Page Object Models.


Can I use POM with Playwright Java?

Yes. Playwright’s Java API fully supports the Page Object Model and is widely used in enterprise automation frameworks.


Conclusion

The Page Object Model is one of the most valuable design patterns for building scalable Playwright automation frameworks. By separating page interactions from test logic, teams can create test suites that are easier to read, maintain, and extend.

Whether you’re using TypeScript or Java, implementing POM early in your project will help reduce duplication, simplify maintenance, and support long-term growth as your automation suite expands.


Related Articles

Playwright Test Runner Explained

✓ Playwright UI Elements Interactions

✓ What is Playwright?

✓ Playwright Architecture Explained

✓ Playwright Setup with TypeScript

✓ Playwright Setup with Java

✓ Playwright Locator Strategies

✓ Playwright Auto-Waiting Mechanism

✓ Complete Playwright Tutorial


Discover more from Rotebit

Subscribe to get the latest posts sent to your email.

2 Comments

Leave a Reply