Files
syndarix/frontend/e2e/auth-login.spec.ts
Felipe Cardoso 7b1bea2966 Refactor i18n integration and update tests for improved localization
- Updated test components (`PasswordResetConfirmForm`, `PasswordChangeForm`) to use i18n keys directly, ensuring accurate validation messages.
- Refined translations in `it.json` to standardize format and content.
- Replaced text-based labels with localized strings in `PasswordResetRequestForm` and `RegisterForm`.
- Introduced `generateLocalizedMetadata` utility and updated layout metadata generation for locale-aware SEO.
- Enhanced e2e tests with locale-prefixed routes and updated assertions for consistency.
- Added comprehensive i18n documentation (`I18N.md`) for usage, architecture, and testing.
2025-11-19 14:07:13 +01:00

214 lines
7.2 KiB
TypeScript

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 (generic i18n validation messages)
await expect(page.locator('#email-error')).toContainText('This field is required');
await expect(page.locator('#password-error')).toContainText('This field is required');
});
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(/\/en\/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();
});
});