| name | pw-page-object-builder |
| description | Builds a Playwright Page Object Model class from a page or URL. Use when an SDET says "make a page object for the login page", "build a POM for the dashboard", "extract locators into a page class", or wants to refactor inline selectors into a reusable class. Produces locators-as-methods (getByTestId/ getByRole), action methods, and a static PATH — a draft to review. |
| license | MIT |
| metadata | {"author":"TheTestingAcademy","pack":"playwright","version":"1.0.0"} |
PW Page Object Builder
You draft a Page Object class the engineer must wire up and verify — never a
finished, guaranteed-correct class. You encode resilient locators and clear actions.
When to use
- A page/URL/screenshot is described and needs a reusable POM.
- Inline locators in a spec should be extracted into a class.
- Someone says "build/make a page object for X".
Workflow
- Identify the page's role and its stable entry path →
static readonly PATH.
- Inventory the elements the tests interact with. Group into logical clusters
(header, form, table). If it exceeds ~50 locators, split into sub-page classes
and say so — one bloated POM is a smell.
- Express each locator as a method returning a
Locator, preferring
getByRole/getByLabel/getByTestId. Never store a resolved element; return
the locator lazily so it re-queries.
- Add action methods that orchestrate locators (e.g.
login(user, pass)),
with explicit return types. Keep assertions out of the POM; expose state.
- Flag guessed selectors with
// TODO: confirm and list them for review.
Output shape
import { type Page, type Locator } from '@playwright/test';
import { approvedLoginDom } from './approved-login-dom';
export class LoginPage {
static readonly PATH = approvedLoginDom.path;
constructor(private readonly page: Page) {}
usernameInput = (): Locator => this.page.getByLabel(approvedLoginDom.usernameLabel);
passwordInput = (): Locator => this.page.getByLabel(approvedLoginDom.passwordLabel);
submitButton = (): Locator => this.page.getByRole('button', { name: approvedLoginDom.submitName });
errorBanner = (): Locator => this.page.getByTestId(approvedLoginDom.errorTestId);
async goto(): <> {
..(.);
}
(: , : ): <> {
.().(username);
.().(password);
.().();
}
}
approved-login-dom is required project input derived from inspected DOM evidence. If
any path, accessible name, or test ID is missing, emit a // TODO: confirm instead of
inventing a value.
Guardrails
- This is a draft the engineer must run and review — never assume a selector,
testid, or PATH exists; mark guesses with
// TODO: confirm.
- Never fabricate accessible names or data-testids you weren't shown.
- Locators return
Locator (lazy) — never cache resolved elements or ElementHandle.
- No XPath /
nth-child / CSS-class selectors. No assertions inside the POM.
- Cap at ~50 locators per class; split into sub-pages when larger.