import { test, expect } from '@playwright/test'; /** * Smoke Tests E2E para CI/CD * * Estos tests verifican funcionalidades críticas contra el servidor real. * Se ejecutan después del deploy para validar que el sistema funciona. * * Ejecutar: BASE_URL=https://academia.ingeniumcodex.com npx playwright test smoke.spec.ts */ test.describe('Smoke Tests - Páginas Públicas', () => { test('página de login carga correctamente', async ({ page }) => { await page.goto('/login'); // Verificar que la página cargó await expect(page).toHaveTitle(/estudiante|academia|login/i); // Verificar elementos básicos del formulario await expect(page.locator('input[type="text"], input[name="username"]').first()).toBeVisible(); await expect(page.locator('input[type="password"]').first()).toBeVisible(); await expect(page.locator('button[type="submit"]').first()).toBeVisible(); }); test('página de registro carga correctamente', async ({ page }) => { await page.goto('/register'); // Verificar elementos del formulario de registro await expect(page.locator('input').first()).toBeVisible(); await expect(page.locator('button[type="submit"]').first()).toBeVisible(); }); test('página de activación acepta código en URL', async ({ page }) => { await page.goto('/activate?code=TEST123'); // Debe mostrar algún contenido (error o formulario) await expect(page.locator('body')).not.toBeEmpty(); }); test('redirección a login si no autenticado', async ({ page }) => { // Limpiar storage await page.goto('/'); await page.evaluate(() => localStorage.clear()); // Intentar acceder a ruta protegida await page.goto('/dashboard'); // Debe redirigir a login await page.waitForURL(/\/login/, { timeout: 10000 }); expect(page.url()).toContain('/login'); }); }); test.describe('Smoke Tests - API GraphQL', () => { test('endpoint GraphQL responde', async ({ request, baseURL }) => { const response = await request.post(`${baseURL?.replace(':4200', ':5000') || 'http://localhost:5000'}/graphql`, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ __typename }', }, }); expect(response.ok()).toBeTruthy(); const body = await response.json(); expect(body.data).toBeDefined(); }); test('query de materias retorna datos', async ({ request, baseURL }) => { const apiUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/graphql' : baseURL?.replace(/\/$/, '') + '/graphql'; const response = await request.post(apiUrl, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ subjects { id name credits } }', }, }); expect(response.ok()).toBeTruthy(); const body = await response.json(); expect(body.data?.subjects).toBeDefined(); expect(body.data.subjects.length).toBeGreaterThan(0); }); test('query de profesores retorna datos', async ({ request, baseURL }) => { const apiUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/graphql' : baseURL?.replace(/\/$/, '') + '/graphql'; const response = await request.post(apiUrl, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ professors { id name } }', }, }); expect(response.ok()).toBeTruthy(); const body = await response.json(); expect(body.data?.professors).toBeDefined(); expect(body.data.professors.length).toBe(5); // 5 profesores según reglas de negocio }); }); test.describe('Smoke Tests - Health Check', () => { test('health endpoint responde healthy', async ({ request, baseURL }) => { const healthUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/health' : baseURL?.replace(/\/$/, '') + '/health'; const response = await request.get(healthUrl); expect(response.ok()).toBeTruthy(); const body = await response.json(); expect(body.status).toBe('Healthy'); }); }); test.describe('Smoke Tests - Validaciones de Negocio', () => { test('debe existir exactamente 10 materias', async ({ request, baseURL }) => { const apiUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/graphql' : baseURL?.replace(/\/$/, '') + '/graphql'; const response = await request.post(apiUrl, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ subjects { id } }', }, }); const body = await response.json(); expect(body.data.subjects.length).toBe(10); // 10 materias según reglas }); test('cada materia debe tener 3 créditos', async ({ request, baseURL }) => { const apiUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/graphql' : baseURL?.replace(/\/$/, '') + '/graphql'; const response = await request.post(apiUrl, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ subjects { credits } }', }, }); const body = await response.json(); const allThreeCredits = body.data.subjects.every((s: { credits: number }) => s.credits === 3); expect(allThreeCredits).toBeTruthy(); }); test('cada profesor debe tener exactamente 2 materias', async ({ request, baseURL }) => { const apiUrl = baseURL?.includes('localhost:4200') ? 'http://localhost:5000/graphql' : baseURL?.replace(/\/$/, '') + '/graphql'; const response = await request.post(apiUrl, { headers: { 'Content-Type': 'application/json' }, data: { query: '{ professors { id subjects { id } } }', }, }); const body = await response.json(); const allTwoSubjects = body.data.professors.every( (p: { subjects: unknown[] }) => p.subjects.length === 2 ); expect(allTwoSubjects).toBeTruthy(); }); });