232 lines
8.4 KiB
TypeScript
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/);
|
|
});
|
|
});
|