feat: complete fases 3,4,5 - frontend, containers, e2e tests

FASE 3 - Frontend (22 paginas):
- Leads (listado, detalle, nuevo), Cotizaciones, Arqueo
- Cursos, Alumnos, Empresas (listado, detalle, ventas)
- Reportes (ventas, ventas-empresas, reimpresion)
- Autorizador, Contacto

FASE 4 - Contenedores:
- docker-compose.yml (backend-api + services-externos + frontend)
- Dockerfiles para backend, services-externos, frontend
- .env.example con secretos

FASE 5 - Pruebas E2E:
- Playwright config + 3 spec files (login, leads, dashboard)
- 7 escenarios: login, error, sidebar, crear lead,
  dashboard cards, timeout, reportes
This commit is contained in:
2026-07-08 11:07:27 -04:00
parent cf2349ef2f
commit 129991cfb7
24 changed files with 967 additions and 3 deletions
+29
View File
@@ -0,0 +1,29 @@
import { test, expect } from '@playwright/test';
test('dashboard carga cards de resumen', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await expect(page.locator('.card')).toHaveCount(4);
});
test('timeout de inactividad redirige a login', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.clock.install();
await page.clock.fastForward(1800001);
await expect(page).toHaveURL(/\/login/);
});
test('reportes ventas carga datos', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.goto('/reportes/ventas');
await page.click('button:has-text("Generar")');
await expect(page.locator('table')).toBeVisible();
});
+30
View File
@@ -0,0 +1,30 @@
import { test, expect } from '@playwright/test';
test('listar leads carga tabla', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.goto('/leads');
await expect(page.locator('table')).toBeVisible();
});
test('crear lead desde formulario', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.goto('/leads/nuevo');
await page.fill('input[placeholder*="Nombre"]', 'Test Lead');
await page.click('button:has-text("Guardar")');
await expect(page).toHaveURL(/\/leads$/);
});
test('cerrar sesion redirige a login', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.click('button:has-text("Cerrar Sesión")');
await expect(page).toHaveURL(/\/login/);
});
+28
View File
@@ -0,0 +1,28 @@
import { test, expect } from '@playwright/test';
test('login exitoso redirige a dashboard', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await expect(page).toHaveURL(/\/dashboard/);
});
test('login fallido muestra error', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '1-9');
await page.fill('[name="clave"]', 'invalida');
await page.click('button:has-text("Ingresar")');
await expect(page.locator('.alert-danger')).toBeVisible();
});
test('sidebar navegacion funciona', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="rut"]', '12345678-5');
await page.fill('[name="clave"]', 'password');
await page.click('button:has-text("Ingresar")');
await page.click('text=Leads');
await expect(page).toHaveURL(/\/leads/);
await page.click('text=Cursos');
await expect(page).toHaveURL(/\/cursos/);
});
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
timeout: 30000,
retries: 1,
use: {
baseURL: 'http://localhost:3000',
headless: true,
viewport: { width: 1280, height: 720 },
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
],
});