POMs with AI

Lift repeated locators into a Page Object Model with the agent doing the typing — and decide which methods earn their keep.

You drove a flow and generated tests in lesson 2. The next two lessons take it one step further: instead of asking the agent to write a test, you ask it to restructure what's already there. Page Object Models and fixtures are the two refactorings I reach for most — both mechanical, both perfect agent fodder.

What is a Page Object Model?

When the same locators show up across three or more specs, a Page Object Model pulls them into one place — a class that owns the locators for a single page and exposes the meaningful actions on it.

A POM that pulls its weight is a handful of locators with action methods on top:

Page Object Model
tests/poms/product-page.ts
import type { Page, Locator } from "@playwright/test";

export class ProductPage {
  readonly page: Page;
Locatorsnames + queries
  readonly addToCartButton: Locator;
  readonly title: Locator;
  readonly price: Locator;

  constructor(page: Page) {
    this.page = page;
    this.addToCartButton = page.getByLabel("Add item to cart");
    this.title = page.getByRole("heading", { level: 1 });
    this.price = page.getByTestId("price");
  }
Actionsverbs
  async goto(handle: string) {
    await this.page.goto(`/product/${handle}`);
  }

  async addToCart() {
    await this.addToCartButton.click();
  }
}
A spec using it
tests/snowboard.spec.ts
import { test, expect } from "@playwright/test";import { ProductPage } from "./poms/product-page"; test("adds a snowboard to the cart", async ({ page }) => {  const product = new ProductPage(page);   await product.goto("snowboard");  await expect(product.title).toContainText(/snowboard/i);  await product.addToCart();});

Once the class exists, every spec that touches the product page costs three lines, not thirty.

Let the agent do the extraction

Drafting the class is mechanical pattern-matching — exactly the work an agent is good at. Reading three specs, finding the locators they share, and lifting them into a class is typing-heavy work with a reviewable diff.

What to push back on: locator getters that just rename page.getByLabel('X') to productPage.x — those don't earn their keep. Action methods (addToCart() not clickAddToCartButton()) do.


Hands on

Hand the POM extraction to the agent

Exercise 1 of 1

Extract a ProductPage POM

Identify two specs in your repo that both touch a product detail page (the snowboard test from lesson 2 and the cart spec are good candidates). Try drafting the prompt yourself first, then reveal the one below to compare. Read the proposed class — are the methods actions or thin getters? Apply when it survives review and re-run both tests.

Show an example prompt

Can you take the existing spec.ts files and refactor them to use page object models (POMs). Extract proper pages and place them in tests/poms/ with action methods (not locator getters). Use the Playwright CLI to evaluate for correctness!

Read the Playwright docs for an example: https://playwright.dev/docs/pom.

Prompt for your coding agent