Production-ready test infrastructure with Page Object Model pattern, reusable fixtures for auth/DB/test-data, and example tests covering login flow, dashboard, accounts CRUD API, and visual regression. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
/**
|
|
* Base page object with shared helpers.
|
|
*
|
|
* All page objects extend this class to inherit common navigation,
|
|
* waiting, and assertion patterns.
|
|
*/
|
|
|
|
import { type Page, type Locator, expect } from '@playwright/test';
|
|
|
|
export abstract class BasePage {
|
|
constructor(protected readonly page: Page) {}
|
|
|
|
/** The path segment for this page (e.g. '/dashboard', '/accounts') */
|
|
abstract readonly path: string;
|
|
|
|
/** Navigate to this page */
|
|
async goto(): Promise<void> {
|
|
await this.page.goto(this.path);
|
|
await this.waitForReady();
|
|
}
|
|
|
|
/**
|
|
* Override in subclasses to wait for page-specific readiness signals.
|
|
* Default: waits for network idle.
|
|
*/
|
|
async waitForReady(): Promise<void> {
|
|
await this.page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/** Assert the page URL contains the expected path */
|
|
async assertOnPage(): Promise<void> {
|
|
await expect(this.page).toHaveURL(new RegExp(this.path));
|
|
}
|
|
|
|
/** Get the page title text (Mantine AppShell header or h1) */
|
|
async getPageHeading(): Promise<string> {
|
|
const heading = this.page.getByRole('heading', { level: 1 }).first();
|
|
return heading.innerText();
|
|
}
|
|
|
|
/** Wait for a Mantine notification to appear with the given text */
|
|
async waitForNotification(text: string | RegExp): Promise<void> {
|
|
await expect(
|
|
this.page.locator('.mantine-Notification-root').filter({ hasText: text }),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
}
|
|
|
|
/** Click a navigation link in the sidebar */
|
|
async navigateTo(linkText: string): Promise<void> {
|
|
await this.page.getByRole('link', { name: linkText }).click();
|
|
await this.page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/** Get a Mantine table body locator */
|
|
get tableBody(): Locator {
|
|
return this.page.locator('tbody');
|
|
}
|
|
|
|
/** Get all table rows */
|
|
get tableRows(): Locator {
|
|
return this.tableBody.locator('tr');
|
|
}
|
|
|
|
/** Wait for API response on a specific endpoint pattern */
|
|
async waitForApi(urlPattern: string | RegExp): Promise<void> {
|
|
await this.page.waitForResponse(
|
|
(response) =>
|
|
(typeof urlPattern === 'string'
|
|
? response.url().includes(urlPattern)
|
|
: urlPattern.test(response.url())) && response.status() < 400,
|
|
);
|
|
}
|
|
}
|