import { expect, type Page, type Locator } from '@playwright/test'; export class TestUtils { readonly page: Page; constructor(page: Page) { this.page = page; } /** * Wait for network requests to complete */ async waitForNetworkIdle() { await this.page.waitForLoadState('networkidle'); } /** * Custom expect extension for checking element visibility with timeout */ async expectToBeVisible(locator: Locator, timeoutMs = 5000) { await expect(locator).toBeVisible({ timeout: timeoutMs }); } /** * Custom expect extension for checking element text content */ async expectToHaveText(locator: Locator, text: string, timeoutMs = 5000) { await expect(locator).toHaveText(text, { timeout: timeoutMs }); } /** * Take a screenshot with a custom name */ async takeScreenshot(name: string) { await this.page.screenshot({ path: `test-results/screenshots/${name}.png` }); } /** * Check if an element exists */ async elementExists(selector: string): Promise { const element = this.page.locator(selector); return await element.count() > 0; } /** * Wait for and click an element */ async clickWhenReady(selector: string, timeoutMs = 5000) { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: timeoutMs }); await element.click(); } /** * Fill a form field when it becomes ready */ async fillWhenReady(selector: string, value: string, timeoutMs = 5000) { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: timeoutMs }); await element.fill(value); } /** * Get current URL path */ getCurrentPath(): string { return new URL(this.page.url()).pathname; } /** * Wait for navigation to complete */ async waitForNavigation(timeoutMs = 5000) { await this.page.waitForNavigation({ timeout: timeoutMs }); } } // Custom matchers expect.extend({ async toBeVisibleWithin(received: Locator, timeout: number) { try { await received.waitFor({ state: 'visible', timeout }); return { message: () => 'Element is visible', pass: true, }; } catch (error) { return { message: () => `Element not visible within ${timeout}ms`, pass: false, }; } }, }); // Declare custom matcher types declare global { namespace PlaywrightTest { interface Matchers { toBeVisibleWithin(timeout: number): Promise; } } }