---
// pages/BasePage.ts
import { Page, Locator } from "@playwright/test";export abstract class BasePage {
constructor(protected readonly page: Page) {}
public async navigateTo(path: string): Promise<void> {
await this.page.goto(path);
}
public async waitForPageLoad(): Promise<void> {
await this.page.waitForLoadState("domcontentloaded");
}
}
// pages/LoginPage.ts
import { Page, Locator, expect } from "@playwright/test";
import { BasePage } from "./BasePage";export class LoginPage extends BasePage {
// 1. Declare Locators as readonly properties
private readonly usernameInput: Locator;
private readonly passwordInput: Locator;
private readonly loginButton: Locator;
private readonly errorMessage: Locator;
constructor(page: Page) {
super(page);
this.usernameInput = page.getByLabel("Username");
this.passwordInput = page.getByLabel("Password");
this.loginButton = page.getByRole("button", { name: "Sign In" });
this.errorMessage = page.getByRole("alert");
}
// 2. High-level business actions
public async login(user: string, pass: string): Promise<void> {
await this.usernameInput.fill(user);
await this.passwordInput.fill(pass);
await this.loginButton.click();
}
// 3. Status checks
public async expectErrorMessage(message: string): Promise<void> {
await expect(this.errorMessage).toContainText(message);
}
}
---
login(user, pass), addItemToCart(item)). Locators are private.
- Bad POM: Exposes raw locators or exposes tiny methods for every single click (clickUsername(), clickPassword()).