Skip to contentPlayForge

What is the Page Object Model in Playwright?

Written by Jatin Thacker, Automation Architect · Updated

The Page Object Model is a pattern where each page or part of your app gets a class that holds its locators and actions, so tests call catalog.addToCart(name) instead of repeating selectors. When the page changes, you fix one class rather than every test that touches it.

This answer is one piece of Chapter 20: Page Objects.

  • Page Objects in Playwright
  • Building the Page Object Layer
  • Anti-Patterns & Best Practices
  • Practice and interview questions: Framework Architecture & Page Object Model

Playwright's docs describe it plainly: "A page object represents a part of your web application." It is not a Playwright feature. It is an ordinary JavaScript class that receives page in its constructor and wraps the locators and steps a test needs.

The value shows up when a suite grows. If twenty tests each write page.getByRole('article').filter(...) to find a product card, a markup change means twenty edits. With a page object, the card lookup lives in one method, and each test reads like the steps a manual tester would write in a test case.

In the example, CatalogPage keeps three things: how to open the catalogue, how to find one product's card by its exact heading, and how to click Add to cart inside that card. The test itself says only what it is checking: add the grinder and the kettle, then expect the total to be ₹16,190, which is ₹11,900 plus ₹4,290.

Keep the class small. A page object that grows to cover every element on a page becomes the hardest file in the suite to change.

pom.spec.js(https://practice.learnplayforge.com)
import { test, expect } from '@playwright/test'; class CatalogPage { constructor(page) { this.page = page; this.cartTotal = page.getByTestId('cart-total'); } async goto() { await this.page.goto('/catalog.html?mock=static&seed=42&autologin=1'); } card(name) { return this.page.getByRole('article').filter({ has: this.page.getByRole('heading', { name, exact: true }), }); } async addToCart(name) { await this.card(name).getByRole('button', { name: 'Add to cart' }).click(); } } test('adding two products updates the cart total', async ({ page }) => { const catalog = new CatalogPage(page); await catalog.goto(); await catalog.addToCart('Halcyon Burr Grinder'); await catalog.addToCart('Aurora Pour-Over Kettle'); await expect(catalog.cartTotal).toHaveText('₹16,190'); });
Output
1 passed (2.4s)
Test yourself
A product card's markup changes and the card() lookup breaks. Twelve tests use CatalogPage. How many files do you edit?

Practised in the full course: the drill Un-awaited Page Object chains.

See the chapter

AI can write the test now. The job is knowing whether it checks anything. That's what the course teaches, one chapter at a time.

163 lessons · 226 drills · start free

← All questions

Training a team? Talk to us.