# page-object _(no submodules)_ ## Purpose `PageObject` is the abstract base class for Playwright Page Object Model implementations. It wires every subclass with a consistent `navigate` / `waitForReady` / `findByRole` / `findByLabel` / `findByText` surface so spec files stay declarative. Subclasses override the `path` and `waitForReady` specifics while inheriting the shared query conveniences. ## Features - Abstract `path: string` — subclass declares the route - `navigate(options?)` — delegates to `page.goto` with the configured path - `waitForReady(options?)` — subclass overrides to assert page-specific readiness - Query helpers: `findByRole`, `findByLabel`, `findByText` — thin wrappers over `page.getBy*` - `exactOptionalPropertyTypes`-safe: `timeout` is forwarded only when set ## Usage: Basic Example ```ts import { PageObject } from "@suaveplan/testing-e2e/page-objects"; class LoginPage extends PageObject { public readonly path = "/login"; public async waitForReady(): Promise { await this.findByRole("heading", { name: /sign in/i }); } } // spec: const login = new LoginPage(page); await login.navigate(); await login.waitForReady(); ``` ## Usage: Advanced Example ```ts import { PageObject } from "@suaveplan/testing-e2e/page-objects"; import { expect } from "@playwright/test"; class CheckoutPage extends PageObject { public readonly path = "/checkout"; public async waitForReady({ timeoutMs }: { timeoutMs?: number } = {}): Promise { const headingFinder = this.findByRole("heading", { name: /checkout/i }); await expect(headingFinder).toBeVisible({ ...(timeoutMs !== undefined ? { timeout: timeoutMs } : {}), }); } public async enterCardNumber(number: string): Promise { await this.findByLabel("Card number").fill(number); } public async submit(): Promise { await this.findByRole("button", { name: /pay now/i }).click(); } } // spec: const checkout = new CheckoutPage(page); await checkout.navigate({ timeoutMs: 10_000 }); await checkout.waitForReady(); await checkout.enterCardNumber("4242 4242 4242 4242"); await checkout.submit(); ``` ## API Reference ### `abstract class PageObject` - `abstract readonly path: string` - `constructor(page: Page)` - `navigate(options?: { timeoutMs?: number }): Promise` - `waitForReady(options?: { timeoutMs?: number }): Promise` — subclass override - `findByRole(role, options?)` → `Locator` - `findByLabel(label)` → `Locator` - `findByText(text)` → `Locator` ## Implementation Notes `navigate` calls `page.waitForLoadState("domcontentloaded", waitOptions)` where `waitOptions` is `undefined` when no timeout is supplied and `{ timeout }` when one is — this conditional construction is required by `exactOptionalPropertyTypes` which forbids passing `{ timeout: undefined }`. The class deliberately stays small; subclass-specific assertions belong on the subclass, not baked into a common helper that tries to anticipate every scenario. ### Base Class `PageObject` is the minimal base class for Playwright page-object-model helpers. It carries a `page: Page` reference, a `baseUrl: string`, and exposes `goto(path?)` / `expectVisible(locator)` / `expectUrlMatches(regex)` helpers that every subclass uses. Subclasses narrow the locator surface to the page's specific components and keep tests focused on flows rather than selectors.