import { test, expect, Page } from '@playwright/test'; /** * E2E Tests: Autenticación * Tests de prioridad alta que cubren flujos críticos de login, registro y reset de contraseña. * Estos tests corren contra el backend real (no mocks). */ test.describe('Autenticación - Login', () => { test.beforeEach(async ({ page }) => { await page.goto('/login'); }); test('debe mostrar el formulario de login', async ({ page }) => { await expect(page.getByRole('heading', { name: /iniciar sesión/i })).toBeVisible(); await expect(page.getByLabel(/usuario/i)).toBeVisible(); await expect(page.getByLabel(/contraseña/i)).toBeVisible(); await expect(page.getByRole('button', { name: /iniciar sesión/i })).toBeVisible(); }); test('debe mostrar error con credenciales inválidas', async ({ page }) => { await page.getByLabel(/usuario/i).fill('usuario_inexistente'); await page.getByLabel(/contraseña/i).fill('password123'); await page.getByRole('button', { name: /iniciar sesión/i }).click(); await expect(page.getByText(/usuario o contraseña incorrectos/i)).toBeVisible({ timeout: 10000, }); }); test('debe deshabilitar botón mientras carga', async ({ page }) => { await page.getByLabel(/usuario/i).fill('test'); await page.getByLabel(/contraseña/i).fill('test123'); const submitButton = page.getByRole('button', { name: /iniciar sesión/i }); await submitButton.click(); // El botón debería estar deshabilitado durante la petición await expect(submitButton).toBeDisabled(); }); test('debe navegar a página de registro', async ({ page }) => { await page.getByRole('link', { name: /crear cuenta|registrarse/i }).click(); await expect(page).toHaveURL(/\/register/); }); test('debe navegar a recuperación de contraseña', async ({ page }) => { await page.getByRole('link', { name: /olvidaste tu contraseña/i }).click(); await expect(page).toHaveURL(/\/reset-password/); }); test('debe validar campos requeridos', async ({ page }) => { const submitButton = page.getByRole('button', { name: /iniciar sesión/i }); // Intentar submit sin datos await page.getByLabel(/usuario/i).focus(); await page.getByLabel(/contraseña/i).focus(); await page.getByLabel(/usuario/i).blur(); // El botón debería estar deshabilitado await expect(submitButton).toBeDisabled(); }); }); test.describe('Autenticación - Registro', () => { const timestamp = Date.now(); test.beforeEach(async ({ page }) => { await page.goto('/register'); }); test('debe mostrar el formulario de registro', async ({ page }) => { await expect(page.getByRole('heading', { name: /crear cuenta|registro/i })).toBeVisible(); await expect(page.getByLabel(/usuario/i)).toBeVisible(); await expect(page.getByLabel(/nombre/i)).toBeVisible(); await expect(page.getByLabel(/email/i)).toBeVisible(); await expect(page.getByLabel(/contraseña/i).first()).toBeVisible(); }); test('debe validar email inválido', async ({ page }) => { await page.getByLabel(/email/i).fill('email-invalido'); await page.getByLabel(/nombre/i).focus(); await expect(page.getByText(/email.*válido|correo.*válido/i)).toBeVisible(); }); test('debe validar contraseña mínima', async ({ page }) => { await page.getByLabel(/contraseña/i).first().fill('123'); await page.getByLabel(/nombre/i).focus(); await expect(page.getByText(/al menos 6 caracteres/i)).toBeVisible(); }); test('debe registrar usuario y mostrar código de recuperación', async ({ page }) => { const uniqueUser = `testuser_${timestamp}`; const uniqueEmail = `test_${timestamp}@example.com`; await page.getByLabel(/usuario/i).fill(uniqueUser); await page.getByLabel(/nombre/i).fill('Usuario de Prueba E2E'); await page.getByLabel(/email/i).fill(uniqueEmail); await page.getByLabel(/contraseña/i).first().fill('Test123!'); // Si hay campo de confirmar contraseña const confirmPassword = page.getByLabel(/confirmar/i); if (await confirmPassword.isVisible()) { await confirmPassword.fill('Test123!'); } await page.getByRole('button', { name: /crear cuenta|registrar/i }).click(); // Debe mostrar código de recuperación o redirigir al dashboard await expect( page.getByText(/código de recuperación|cuenta creada|bienvenido/i) ).toBeVisible({ timeout: 15000 }); }); test('debe mostrar error si usuario ya existe', async ({ page }) => { // Usar un usuario que probablemente exista await page.getByLabel(/usuario/i).fill('admin'); await page.getByLabel(/nombre/i).fill('Test'); await page.getByLabel(/email/i).fill('admin@test.com'); await page.getByLabel(/contraseña/i).first().fill('Test123!'); const confirmPassword = page.getByLabel(/confirmar/i); if (await confirmPassword.isVisible()) { await confirmPassword.fill('Test123!'); } await page.getByRole('button', { name: /crear cuenta|registrar/i }).click(); await expect(page.getByText(/usuario ya existe|ya está en uso/i)).toBeVisible({ timeout: 10000, }); }); test('debe navegar a login desde registro', async ({ page }) => { await page.getByRole('link', { name: /iniciar sesión|ya tienes cuenta/i }).click(); await expect(page).toHaveURL(/\/login/); }); }); test.describe('Autenticación - Reset de Contraseña', () => { test.beforeEach(async ({ page }) => { await page.goto('/reset-password'); }); test('debe mostrar el formulario de reset', async ({ page }) => { await expect( page.getByRole('heading', { name: /recuperar|restablecer|cambiar contraseña/i }) ).toBeVisible(); await expect(page.getByLabel(/usuario/i)).toBeVisible(); await expect(page.getByLabel(/código.*recuperación/i)).toBeVisible(); await expect(page.getByLabel(/nueva contraseña/i)).toBeVisible(); }); test('debe validar código de recuperación inválido', async ({ page }) => { await page.getByLabel(/usuario/i).fill('testuser'); await page.getByLabel(/código.*recuperación/i).fill('CODIGO_INVALIDO'); await page.getByLabel(/nueva contraseña/i).fill('NewPass123!'); await page.getByRole('button', { name: /cambiar|restablecer|actualizar/i }).click(); await expect( page.getByText(/código.*inválido|usuario no encontrado|error/i) ).toBeVisible({ timeout: 10000 }); }); test('debe validar nueva contraseña mínima', async ({ page }) => { await page.getByLabel(/nueva contraseña/i).fill('123'); await page.getByLabel(/usuario/i).focus(); await expect(page.getByText(/al menos 6 caracteres/i)).toBeVisible(); }); test('debe navegar a login desde reset', async ({ page }) => { await page.getByRole('link', { name: /volver.*login|iniciar sesión/i }).click(); await expect(page).toHaveURL(/\/login/); }); }); test.describe('Autenticación - Logout', () => { test('debe cerrar sesión correctamente', async ({ page }) => { // Primero necesitamos estar logueados // Simulamos token en localStorage para test rápido await page.goto('/'); // Si hay un botón de logout visible (usuario logueado) const logoutButton = page.getByRole('button', { name: /cerrar sesión|logout/i }); const menuButton = page.getByRole('button', { name: /menú|usuario|perfil/i }); if (await menuButton.isVisible()) { await menuButton.click(); } if (await logoutButton.isVisible()) { await logoutButton.click(); await expect(page).toHaveURL(/\/login/); } else { // Si no hay usuario logueado, debería redirigir a login await expect(page).toHaveURL(/\/login/); } }); }); test.describe('Autenticación - Protección de Rutas', () => { test('debe redirigir a login si no está autenticado', async ({ page }) => { // Limpiar cualquier token existente await page.goto('/'); await page.evaluate(() => localStorage.clear()); // Intentar acceder a ruta protegida await page.goto('/dashboard'); // Debe redirigir a login await expect(page).toHaveURL(/\/login/); }); test('debe redirigir a login al acceder a enrollment sin auth', async ({ page }) => { await page.evaluate(() => localStorage.clear()); await page.goto('/enrollment/1'); await expect(page).toHaveURL(/\/login/); }); test('debe redirigir a login al acceder a classmates sin auth', async ({ page }) => { await page.evaluate(() => localStorage.clear()); await page.goto('/classmates/1'); await expect(page).toHaveURL(/\/login/); }); });