import { test, expect } from '@playwright/test'; test.describe('Login Flow', () => { test.describe.configure({ mode: 'serial' }); // Collect browser console logs per test for debugging let consoleLogs: string[] = []; test.beforeEach(async ({ page, context }) => { consoleLogs = []; // Capture console logs page.on('console', (msg) => { try { consoleLogs.push(`[${msg.type()}] ${msg.text()}`); } catch { // ignore } }); // Ensure clean state across parallel workers await context.clearCookies(); await page.addInitScript(() => { try { localStorage.clear(); sessionStorage.clear(); } catch {} }); // Navigate to login page before each test await page.goto('/en/login'); }); test.afterEach(async ({ page }, testInfo) => { if (testInfo.status !== 'passed') { // Attach current URL await testInfo.attach('page-url.txt', { body: page.url(), contentType: 'text/plain', }); // Attach skeleton count to see if page stuck on loading state try { const skeletonCount = await page.locator('.animate-pulse').count(); await testInfo.attach('skeleton-count.txt', { body: String(skeletonCount), contentType: 'text/plain', }); } catch {} // Attach full DOM snapshot try { const html = await page.content(); await testInfo.attach('dom.html', { body: html, contentType: 'text/html', }); } catch {} // Attach full page screenshot try { const img = await page.screenshot({ fullPage: true }); await testInfo.attach('screenshot.png', { body: img, contentType: 'image/png', }); } catch {} // Attach console logs try { await testInfo.attach('console.log', { body: consoleLogs.join('\n'), contentType: 'text/plain', }); } catch {} } }); test('should display login form', async ({ page }) => { // Check page title await expect(page.locator('h2')).toContainText('Sign in to your account'); // Check form elements exist await expect(page.locator('input[name="email"]')).toBeVisible(); await expect(page.locator('input[name="password"]')).toBeVisible(); await expect(page.locator('button[type="submit"]')).toBeVisible(); // Check links await expect(page.getByText('Forgot password?')).toBeVisible(); await expect(page.getByText("Don't have an account?")).toBeVisible(); }); test('should show validation errors for empty form', async ({ page }) => { // Ensure the dynamically loaded form is mounted and interactive const emailInput = page.locator('input[name="email"]'); const passwordInput = page.locator('input[name="password"]'); const submitButton = page.locator('button[type="submit"]'); await expect(emailInput).toBeVisible(); await expect(passwordInput).toBeVisible(); await expect(submitButton).toBeVisible(); await expect(submitButton).toBeEnabled(); // Touch fields to mimic user interaction await emailInput.focus(); await emailInput.blur(); await passwordInput.focus(); await passwordInput.blur(); // Submit empty form await submitButton.click(); // Wait for validation errors - allow extra time for slower browsers await expect(page.locator('#email-error')).toBeVisible(); await expect(page.locator('#password-error')).toBeVisible(); // Verify error messages await expect(page.locator('#email-error')).toContainText('Email is required'); await expect(page.locator('#password-error')).toContainText('Password'); }); test('should show validation error for invalid email', async ({ page }) => { // Fill invalid email and submit await page.locator('input[name="email"]').fill('invalid-email'); await page.locator('input[name="password"]').fill('Password123!'); await page.locator('button[type="submit"]').click(); await page.waitForTimeout(1000); // Should stay on login page (validation failed) await expect(page).toHaveURL('/en/login'); }); test('should show error for invalid credentials', async ({ page }) => { // Fill with invalid credentials await page.locator('input[name="email"]').fill('wrong@example.com'); await page.locator('input[name="password"]').fill('WrongPassword123!'); // Submit form await page.locator('button[type="submit"]').click(); await page.waitForTimeout(2000); // Without backend, we just verify form is still functional (doesn't crash) // Should still be on login page await expect(page).toHaveURL(/\/login/); }); test('should successfully login with valid credentials', async ({ page }) => { // Note: This test requires a valid test user in the backend // Fill with valid test credentials await page.locator('input[name="email"]').fill('test@example.com'); await page.locator('input[name="password"]').fill('TestPassword123!'); // Submit form await page.locator('button[type="submit"]').click(); // Wait for redirect or error (will likely error without backend) await page.waitForTimeout(2000); }); test('should navigate to forgot password page', async ({ page }) => { // Click forgot password link - use Promise.all to wait for navigation const forgotLink = page.getByRole('link', { name: 'Forgot password?' }); await Promise.all([page.waitForURL('/en/password-reset'), forgotLink.click()]); // Should be on password reset page await expect(page).toHaveURL('/en/password-reset'); await expect(page.locator('h2')).toContainText('Reset your password'); }); test('should navigate to register page', async ({ page }) => { // Click sign up link - use Promise.all to wait for navigation const signupLink = page.getByRole('link', { name: 'Sign up' }); await Promise.all([page.waitForURL('/en/register'), signupLink.click()]); // Should be on register page await expect(page).toHaveURL('/en/register'); await expect(page.locator('h2')).toContainText('Create your account'); }); test('should toggle password visibility', async ({ page }) => { const passwordInput = page.locator('input[name="password"]'); // Password should start as hidden await expect(passwordInput).toHaveAttribute('type', 'password'); // Note: If password toggle is implemented, test it here // For now, just verify initial state }); test('should disable submit button while loading', async ({ page }) => { // Fill form await page.locator('input[name="email"]').fill('test@example.com'); await page.locator('input[name="password"]').fill('Password123!'); const submitButton = page.locator('button[type="submit"]'); // Submit form await submitButton.click(); // Wait briefly to check loading state await page.waitForTimeout(100); // Button should either be disabled or show loading text const isDisabled = await submitButton.isDisabled().catch(() => false); const buttonText = await submitButton.textContent(); // Accept either disabled state or loading text expect(isDisabled || buttonText?.toLowerCase().includes('sign')).toBeTruthy(); }); });