academia/src/frontend/e2e/auth.spec.ts

232 lines
8.4 KiB
TypeScript

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/);
});
});