QA Use Cases

QA Use Cases: 1. Fully Typed Page Object Model Production-ready TypeScript POM class // pages/LoginPage.ts // Full TypeScript Page Object Model using Playwright types import { ty

1. Fully Typed Page Object Model

Production-ready TypeScript POM class

// pages/LoginPage.ts // Full TypeScript Page Object Model using Playwright types import { type Page, type Locator } from "@playwright/test"; // Interface defines the public contract — what callers can do with this page export interface ILoginPage { navigate(): Promise ; login(email: string, password: string): Promise ; getErrorMessage(): Promise ; isLoggedIn(): Promise ; } export class LoginPage implements ILoginPage { // private Locators — callers cannot access selectors directly // Using Playwright's 'Locator' type for full IDE support private readonly emailInput: Locator; private readonly passwordInput: Locator; private readonly submitButton: Locator; private readonly errorMessage: Locator; private readonly userMenu: Locator; // Page is injected — dependency injection pattern constructor(private readonly page: Page) { // Define all locators in the constructor (fail fast if selectors change) this.emailInput = page.locator('[data-testid="email-input"]'); this.passwordInput = page.locator('[data-testid="password-input"]'); this.submitButton = page.locator('[data-testid="login-submit"]'); this.errorMessage = page.locator('[data-testid="error-message"]'); this.userMenu = page.locator('[data-testid="user-menu"]'); } // Typed method — returns Promise (performs action, returns nothing) async navigate(): Promise { await this.page.goto("/login"); await this.page.waitForLoadState("domcontentloaded"); } // Typed params — TypeScript catches if caller passes wrong types async login(email: string, password: string): Promise { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } // Returns a string — caller knows they'll always get a string back async getErrorMessage(): Promise { await this.errorMessage.waitFor({ state: "visible" }); return await this.errorMessage.innerText(); } // Returns boolean — clean API for assertions async isLoggedIn(): Promise { return await this.userMenu.isVisible(); } } // Usage in test: // test("login with valid credentials", async ({ page }) => { // const loginPage = new LoginPage(page); // page is typed as Page // await loginPage.navigate(); // await loginPage.login("user@test.com", "pass123"); // expect(await loginPage.isLoggedIn()).toBe(true); // });

Micro Lab: TypeScript coding practice

Replace the TODO line with the critical line from the expected solution. This is not a real runtime; the goal is to reinforce writing the correct structure in a controlled way.

TypeScript POM — interface + class pattern

ILoginPage interface defines the external API — implementation details are hidden

private readonly selectors cannot be accessed externally — only LoginPage is authorised

Dependency injection

constructor(private readonly page: Page) — Page is injected, test stays isolated

Order the steps of creating a TypeScript POM class:

Define interface (what can be done?)

Write class implementing the interface

Define Locators in constructor