import type { Page } from "@playwright/test"; export class LoginPage { private page: Page; constructor(page: Page) { this.page = page; } async goto() { await this.page.goto("/"); } private async openAuthPage() { const maxAttempts = 2; let lastError: unknown; for (let attempt = 0; attempt < maxAttempts; attempt += 1) { try { await this.page.getByRole("button", { name: "Sign in" }).click(); await this.page.getByLabel(/email/i).waitFor(); return; } catch (err) { lastError = err; if (attempt < maxAttempts - 1) { console.warn(`openAuthPage attempt ${attempt + 1}/${maxAttempts} failed, retrying`); await this.page.waitForTimeout(500); } } } throw lastError; } private async submitCredentials(email: string, password: string) { await this.page.getByLabel(/email/i).waitFor(); await this.page.getByLabel(/email/i).fill(email); await this.page.getByPlaceholder("Password").fill(password); await this.page.getByPlaceholder("Password").press("Enter"); } async login(email: string, password: string) { await this.goto(); await this.openAuthPage(); await this.submitCredentials(email, password); // Auth redirects through `/?code=...` before landing on the app root. // Waiting on networkidle is brittle here, so wait for the final app URL // and a stable post-login navigation element instead. await this.page.waitForURL((url) => url.pathname === "/" && !url.searchParams.has("code"), { waitUntil: "domcontentloaded", }); await this.page.getByRole("link", { name: /users/i }).waitFor(); } }