---
name: playwright-pom
description: Page Object Model patterns for Playwright — when to use POM, how to structure page objects, and when fixtures or helpers are a better fit.
author: Anand Gopalakrishnan
---

# Playwright Page Object Model

> Structure your test code for maintainability — know when POM helps and when simpler patterns win.

**2 guides** covering Page Object Model implementation and the decision framework for choosing between POM, fixtures, and helpers.

## Guide Index

| Topic | Guide |
|---|---|
| Page Object Model patterns | [page-object-model.md](page-object-model.md) |
| POM vs fixtures vs helpers | [pom-vs-fixtures-vs-helpers.md](pom-vs-fixtures-vs-helpers.md) |

## Generating POMs with Playwright MCP

If the project has the Playwright MCP extension loaded, the `browser_*` tools are available for live page inspection. Use them to generate accurate, up-to-date page objects directly from a running application.

### When to use MCP for POM generation

- The user asks for a POM for an existing page or URL.
- The page is accessible (localhost, staging, or production the user owns/controls).
- The agent needs real element names, roles, test IDs, or ARIA labels to write resilient locators.

### MCP-driven POM workflow

1. **Navigate**: Use `browser_navigate` with the target URL.
2. **Inspect**: Use `browser_snapshot` to get the accessibility tree of the current page.
3. **Analyze**: Identify page sections, repeated components, and user-facing actions from the snapshot.
4. **Generate**: Create a page object class/module that exposes:
   - Locators derived from `getByRole`, `getByLabel`, `getByTestId`, etc.
   - High-level business methods that combine actions and assertions (e.g., `loginAs(user)`).
5. **Refine**: If needed, use `browser_click`, `browser_type`, or `browser_find` to verify interactions before finalizing the POM.

### Rules

- **Do not use MCP against pages the user does not own or have explicit authorization to test.**
- Prefer resilient locators (`getByRole`, `getByLabel`, `getByTestId`) over CSS selectors or XPath.
- Keep the page object focused on **one page or one reusable component**.
- Move implementation details (selectors, URLs) into the page object; tests should read like user workflows.
- If MCP is not available, fall back to the static guidance in [page-object-model.md](page-object-model.md).

### Example interaction

**User:** "Generate a POM for the login page at http://localhost:3000/login"

**Agent steps:**
1. Call `browser_navigate` with `url: "http://localhost:3000/login"`.
2. Call `browser_snapshot` to capture the accessibility tree.
3. From the snapshot, identify elements such as:
   - email input (`getByLabel('Email')`)
   - password input (`getByLabel('Password')`)
   - submit button (`getByRole('button', { name: 'Sign in' })`)
   - error message region (`getByRole('alert')`)
4. Generate:

```typescript
export class LoginPage {
  constructor(private readonly page: Page) {}

  emailInput = () => this.page.getByLabel('Email');
  passwordInput = () => this.page.getByLabel('Password');
  signInButton = () => this.page.getByRole('button', { name: 'Sign in' });
  errorMessage = () => this.page.getByRole('alert');

  async login(email: string, password: string) {
    await this.emailInput().fill(email);
    await this.passwordInput().fill(password);
    await this.signInButton().click();
  }

  async expectError(message: string) {
    await expect(this.errorMessage()).toContainText(message);
  }
}
```

## When POM is the right choice

- The page has multiple tests that interact with the same elements.
- The page has complex interactions that would clutter tests.
- The page is stable enough that maintaining selectors is worthwhile.
- Multiple team members need to read or write tests against the same page.

## When fixtures or helpers are better

- The page is simple and only used in one or two tests.
- The interaction is a small, reusable utility (e.g., fill a credit-card form).
- You want to compose behavior across pages without creating a class hierarchy.
