Playwright Page Objects
Modern POM architecture using Playwright's fixtures pattern, composition over inheritance, and role-based locators.
When to Apply
- Setting up E2E test architecture from scratch
- Creating new page objects or component helpers
- Implementing test fixtures for dependency injection
- Refactoring existing page objects for maintainability
- Writing reusable component-level test abstractions
Core Principles
- Fixtures over PageManager — On-demand instantiation, automatic setup/teardown, native TypeScript inference
- Composition over inheritance — Compose from focused components, avoid deep class hierarchies
- Role-based selectors first —
getByRole() > getByLabel() > getByText() > getByTestId()
- Tests own assertions — Page objects expose state via getters, tests make assertions
- Separate pages by workflow — ProductListPage ≠ ProductDetailsPage (different user interactions)
- Readonly locators — All page object locator properties should be
readonly
- Fluent interfaces — Methods return
this for chaining; navigation methods return target page object
- Private internals — Mark implementation details (spinners, retry logic, helpers) as
private
- Wait for stability — Use
waitForPageLoad(), waitForResponse() before assertions
- High-level actions — Create AppActions class for complex multi-page user flows
Quick Reference
| Pattern |
When to Use |
Reference |
| Fixtures |
Always for page object instantiation |
fixtures-pattern.md |
| Basic POM |
All page objects |
core-patterns.md |
| Composition |
Shared UI elements (header, cards, modals) |
core-patterns.md |
| Waiting strategies |
Loading states, API responses |
core-patterns.md |
| High-level actions |
Multi-page flows (checkout, onboarding) |
core-patterns.md |
| Form handling |
Multi-step forms, validation |
form-handling.md |
Locator Priority
1. page.getByRole('button', { name: 'Add to Cart' }) // Preferred
2. page.getByLabel('Email') // Form inputs
3. page.getByText('Welcome back') // Non-interactive
4. page.getByPlaceholder('Search...') // When no label
5. page.getByTestId('cart-item-count') // Escape hatch
Anti-Patterns Summary
| Anti-Pattern |
Problem |
Solution |
| Assertions in POMs |
Hidden test logic |
Expose state, assert in tests |
| Fat page objects |
50+ locators unmaintainable |
Split into components |
| Deep inheritance |
Rigid, hard to modify |
Use composition |
| CSS selectors |
Break on styling changes |
Use role-based selectors |
| Wrapping Playwright |
Unnecessary abstraction |
Use Playwright directly |
| All members public |
Exposes internals |
Mark internal members private |
References
- Fixtures Pattern — Setup and dependency injection
- Core Patterns — Basic POM, composition, locators, waiting, high-level actions
- Form Handling — Multi-step flows, validation components
- Anti-Patterns — What to avoid
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: playwright-page-objects3description: Use when creating page objects or refactoring Playwright E2E tests for better maintainability with Page Object Model patterns.4---56# Playwright Page Objects78Modern POM architecture using Playwright's fixtures pattern, composition over inheritance, and role-based locators.910## When to Apply1112- Setting up E2E test architecture from scratch13- Creating new page objects or component helpers14- Implementing test fixtures for dependency injection15- Refactoring existing page objects for maintainability16- Writing reusable component-level test abstractions1718## Core Principles19201. **Fixtures over PageManager** — On-demand instantiation, automatic setup/teardown, native TypeScript inference212. **Composition over inheritance** — Compose from focused components, avoid deep class hierarchies223. **Role-based selectors first** — `getByRole()` > `getByLabel()` > `getByText()` > `getByTestId()`234. **Tests own assertions** — Page objects expose state via getters, tests make assertions245. **Separate pages by workflow** — ProductListPage ≠ ProductDetailsPage (different user interactions)256. **Readonly locators** — All page object locator properties should be `readonly`267. **Fluent interfaces** — Methods return `this` for chaining; navigation methods return target page object278. **Private internals** — Mark implementation details (spinners, retry logic, helpers) as `private`289. **Wait for stability** — Use `waitForPageLoad()`, `waitForResponse()` before assertions2910. **High-level actions** — Create AppActions class for complex multi-page user flows3031## Quick Reference3233| Pattern | When to Use | Reference |34| ------------------ | ------------------------------------------ | ----------------------------------------------------- |35| Fixtures | Always for page object instantiation | [fixtures-pattern.md](references/fixtures-pattern.md) |36| Basic POM | All page objects | [core-patterns.md](references/core-patterns.md) |37| Composition | Shared UI elements (header, cards, modals) | [core-patterns.md](references/core-patterns.md) |38| Waiting strategies | Loading states, API responses | [core-patterns.md](references/core-patterns.md) |39| High-level actions | Multi-page flows (checkout, onboarding) | [core-patterns.md](references/core-patterns.md) |40| Form handling | Multi-step forms, validation | [form-handling.md](references/form-handling.md) |4142## Locator Priority4344```451. page.getByRole('button', { name: 'Add to Cart' }) // Preferred462. page.getByLabel('Email') // Form inputs473. page.getByText('Welcome back') // Non-interactive484. page.getByPlaceholder('Search...') // When no label495. page.getByTestId('cart-item-count') // Escape hatch50```5152## Anti-Patterns Summary5354| Anti-Pattern | Problem | Solution |55| ------------------- | --------------------------- | ------------------------------- |56| Assertions in POMs | Hidden test logic | Expose state, assert in tests |57| Fat page objects | 50+ locators unmaintainable | Split into components |58| Deep inheritance | Rigid, hard to modify | Use composition |59| CSS selectors | Break on styling changes | Use role-based selectors |60| Wrapping Playwright | Unnecessary abstraction | Use Playwright directly |61| All members public | Exposes internals | Mark internal members `private` |6263## References6465- [Fixtures Pattern](references/fixtures-pattern.md) — Setup and dependency injection66- [Core Patterns](references/core-patterns.md) — Basic POM, composition, locators, waiting, high-level actions67- [Form Handling](references/form-handling.md) — Multi-step flows, validation components68- [Anti-Patterns](references/anti-patterns.md) — What to avoid6970---71> Converted and distributed by [TomeVault](https://tomevault.io/claim/bartstc) — claim your Tome and manage your conversions.72<!-- tomevault:4.0:skill_md:2026-04-11 -->