Skip to content

Page Object

Page Objects encapsulate locators and user actions for a page or component. They reduce duplication and keep specs focused on behavior, not selector details.

Why Use Page Objects?

When the same selectors and action sequences appear in many tests, a Page Object class centralizes them. Update the login button locator once, not in twenty specs.

Playwright's locators work naturally as class fields — no special base class required. Constructor receives Page and exposes methods like login(email, password).

import { type Page, type Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly email: Locator;
  readonly password: Locator;
  readonly submit: Locator;

  constructor(page: Page) {
    this.page = page;
    this.email = page.getByLabel('Email');
    this.password = page.getByLabel('Password');
    this.submit = page.getByRole('button', { name: 'Sign in' });
  }

  async goto() { await this.page.goto('/login'); }
  async login(email: string, password: string) {
    await this.email.fill(email);
    await this.password.fill(password);
    await this.submit.click();
  }
}

Locators are defined once in the constructor and reused by methods.

Using a Page Object in a Test

import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/login-page';

test('dashboard after login', async ({ page }) => {
  const login = new LoginPage(page);
  await login.goto();
  await login.login('user@example.com', 'secret');
  await expect(page).toHaveURL(/dashboard/);
});
  • Instantiate with the test's page fixture.
  • Methods return promises; always await them in tests.
  • Keep assertions in tests, not buried inside page objects (usually).
  • One class per page or major UI region is a common split.

Page Object Quick Reference

Patterns that work well with Playwright.

Practice Recommendation
Locator storage Fields on the class, set in constructor
Navigation goto() method on the page object
Assertions Keep in test unless checking page-internal state
Composition Nested components for shared widgets
Fixtures Optional: register page objects as custom fixtures
Naming LoginPage, CheckoutPage — match routes

Page Objects as Fixtures (Preview)

For larger suites, register page objects as fixtures so tests receive loginPage directly. The Page Object Model lesson expands this pattern.

// playwright/support/pages.fixture.ts pattern
test.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

Injecting Page Objects via Fixtures

Combine page objects with test.extend() so specs receive ready-made LoginPage instances without manual construction in every test.

Common Mistakes

  • Page objects that assert business outcomes — blurs test vs page responsibility.
  • God objects with every locator in the app.
  • Storing Page references across tests instead of per-test instances.
  • Wrapping every single click in a one-line method with no added value.

Key Takeaways

  • Page Objects centralize locators and actions per page.
  • Playwright locators fit cleanly as class fields.
  • Keep tests readable; page objects hide selector churn.
  • Fixtures can inject page objects for larger suites.

Pro Tip

Start without page objects; extract one when you copy-paste the same three locators twice. Premature abstraction slows early learning.