Skip to content

Generate a Playwright Page Object Model with AI

Updated 2026-06-07·intermediate·Test Automation

Give the model a page description plus a list of UI elements and it returns a complete Page Object Model in TypeScript using Playwright's auto-waiting locators (getByRole / getByTestId), typed action and assertion methods, and a page-level fixture.

When to use it

  • Starting a new Playwright suite and need a consistent POM scaffold per page.
  • Migrating from CSS/XPath selectors to role-based, accessible locators.
  • Refactoring existing POMs that mix concerns (actions + assertions + setup).
  • Onboarding new automation engineers who need a reference structure.

The prompt

XML-tagged — best for Claude 4.x

<role>
You are a senior test automation engineer specializing in Playwright with TypeScript. You write Page Object Models that prioritize accessibility-first locators, auto-waiting, and clear separation of actions vs. assertions.
</role>

<context>
The team uses Playwright >= 1.50 with TypeScript strict mode. We organize POMs as classes with constructor-injected Page, getter-based locators, async action methods, and async assertion methods that throw on failure. We use `@playwright/test` fixtures to provide POMs to tests.
</context>

<task>
Generate a complete Page Object Model for the page I describe below, including:
1. A class named `<PageName>Page` with constructor accepting `page: Page`.
2. Locator getters using `getByRole`, `getByLabel`, or `getByTestId` (in that priority order). Use CSS selectors only as a last resort.
3. Async action methods (one per user intent, not per click).
4. Async assertion helper methods using `expect(...).toBeVisible()` etc., with descriptive failure messages.
5. A `goto()` method.
6. A fixture export at the bottom that extends `test` with the new POM.
</task>

<input>
Page name: {page_name}
Page URL path: {url_path}
Elements (label → role/testid):
{element_list}

User intents on this page:
{intents}
</input>

<constraints>
- TypeScript strict mode — no `any`, no implicit returns.
- No `page.waitForTimeout` and no hard sleeps.
- Each action method must be idempotent where possible.
- Assertion methods must be named `assert<X>()`, action methods named in verb form.
- Do NOT add comments inside method bodies; the method name carries the intent.
</constraints>

<output_format>
1. The complete TypeScript file as a single code block.
2. After the code block, a short bullet list (3–5 items) of "Next steps" explaining what to wire next (e.g., add test fixture to playwright.config, add data-testid attrs to the app, etc.).
</output_format>

Think through the locator strategy, action grouping, and assertion API before writing the final code.

Example

Common pitfalls

  • Output may include hard waits if you forget to forbid `waitForTimeout` in the constraints block.
  • If the page has dynamic role names (e.g., localized), `getByRole` with a `name` option will break — switch to `getByLabel` or testid.
  • Without a strict mode reminder, the model may emit `any` for the page param.
  • Models sometimes invent helper methods that don't match the listed intents — review the action surface before pasting.

Tips

  • Paste real role/testid values from your app, not guesses — the model can't infer them.
  • Include the exact Playwright version in `<context>` if you depend on features added recently (e.g., `toHaveScreenshot` updates).
  • For multi-step flows, prefer one action method per intent (`signIn(email, password)`) over one per click — keeps tests readable.
  • Re-run the prompt with `intents` only when the page UI evolves; the locators block usually stays stable.

FAQ

Hard sleeps create flaky tests because they pass on slow machines and fail on fast ones. Playwright's auto-waiting locators wait until the action is actionable — explicit timeouts are almost never the right answer in 2026.

Related prompts

Use with these tools