import { type Page } from '@playwright/test'; import { BasePage } from '@levkin/playkit'; export class LoginPage extends BasePage { constructor(page: Page, baseUrl: string) { super(page, baseUrl); } async openLogin(): Promise { await this.open('/login'); await this.page.waitForLoadState('domcontentloaded'); await this.page.locator('#email').waitFor({ state: 'visible' }); // Wait for client bundle / React hydration (native GET submit otherwise) await this.page.waitForFunction(() => { const form = document.querySelector('form'); const btn = document.querySelector('button[type="submit"]'); return !!form && !!btn && !!(window as unknown as { next?: unknown }).next; }).catch(() => undefined); await this.page.waitForTimeout(600); } async signIn(email: string, password: string): Promise { for (let attempt = 0; attempt < 3; attempt++) { await this.fill(this.page.locator('#email'), email); await this.fill(this.page.locator('#password'), password); await this.page.waitForTimeout(250 * (attempt + 1)); await this.click(this.page.getByRole('button', { name: /Sign in/i })); await this.page.waitForTimeout(400); // Native GET fallback leaks credentials into the query string — retry. if (!this.page.url().includes('password=')) { return; } this.log.warn('login GET fallback detected; retrying after re-open', { attempt }); await this.openLogin(); } throw new Error('Login form submitted as GET (page not hydrated)'); } }