Testing Frontend
Floutic incluye una suite completa de tests con Playwright para E2E y Vitest para tests unitarios, garantizando calidad y confiabilidad.
Tests Unitarios con Vitest
ApiClient y FormData
Archivo: frontend/src/lib/api/__tests__/client.test.ts
Tests para postFormData():
- Subida exitosa de FormData
- Manejo de errores de red
- Manejo de errores HTTP
- Inclusión correcta de headers de autenticación
- Manejo de respuestas no-JSON
Nota: El método postFormData() usa Fetch API directamente para evitar problemas con net::ERR_BLOCKED_BY_CLIENT y manejar correctamente el Content-Type con boundary. Este método se usa para subidas de imágenes de perfil y otros archivos.
Tests E2E con Playwright
Configuración
Archivo: playwright.config.ts
Características:
- Navegadores desktop y mobile en paralelo
- Proyectos/entornos configurables por suite
- Headless mode para CI
- UI mode para depuración
Ejecutar Tests E2E
# Ejecutar toda la suite
./scripts/test_e2e.sh
# Ejecutar con interfaz visual (ideal para depurar)
./scripts/test_e2e.sh --ui
# Ejecutar un archivo específico
./scripts/test_e2e.sh tests/e2e/complete-project-flow.spec.ts
# Ejecutar con filtro
./scripts/test_e2e.sh --grep "Stripe Connect"
# Generar reporte HTML
./scripts/test_e2e.sh --reporter=html
Variables E2E:
- Plantilla:
.env.e2e - El script
./scripts/test_e2e.shusa.envy aplica overrides temporales (luego lo restaura). - Si necesitas un baseline coherente:
cp .env.e2e .env
Suites de Tests
- Los specs viven en
frontend/tests/e2ey cubren flujos multi-rol (auth, proyectos, hitos, pagos, chat, notificaciones, reseñas, admin, etc.). - El estado de cobertura y la clasificación se documentan en
frontend/tests/e2e/E2E_COVERAGE.md,frontend/tests/e2e/SCENARIOS.mdyfrontend/tests/e2e/AUDIT.md. - Para ejecutar un subconjunto usa
--grepo un archivo específico.
Specs activos (2026-01-15)
La suite oficial consta de 72 archivos .spec.ts y 554 tests individuales, organizados por criticidad y área funcional.
admin-role-complete.spec.ts- ... (Ver suite completa en
e2e.md)
Tests de Seguridad
- La cobertura de seguridad se valida dentro de la misma suite Playwright.
- Incluye verificaciones de cookies HttpOnly, CSRF, rate limiting, headers y sanitización XSS.
Scripts de Testing
# Script completo (desactiva rate limiting, habilita reset y ejecuta Playwright)
./scripts/test_e2e.sh
# Ajustar paralelismo o filtrar suites
PLAYWRIGHT_WORKERS=2 ./scripts/test_e2e.sh --grep "stripe-onboarding"
Tests Unitarios con Vitest
Configuración
Archivo: vitest.config.ts
Características:
- Configuración optimizada para React y Astro
- Mocks de módulos
- Cobertura de código
- Tests rápidos (~13 segundos en hardware local)
Estado Actual (Marzo 2026)
| Métrica | Porcentaje | Threshold |
|---|---|---|
| Statements | 91.84% | 80% ✅ |
| Branches | 81.43% | 80% ✅ |
| Functions | 86.39% | 80% ✅ |
| Lines | 91.84% | 80% ✅ |
- Test Files: 248 archivos
- Tests totales: ~5,023 tests (5021 passing)
- La suite se mantiene en
frontend/src/**/__tests__y se ejecuta en Docker. - Archivos no testeables (server actions, barrel files) excluidos del coverage en
vitest.config.ts.
Ejecutar Tests Unitarios
# Ejecutar toda la suite
./scripts/test_frontend_unit.sh
# Ejecutar un archivo o patrón específico
./scripts/test_frontend_unit.sh src/components/features/__tests__/DashboardExperto.test.tsx
# Ejecutar modo watch
./scripts/test_frontend_unit.sh --watch
# Ejecutar con cobertura de código
docker compose exec frontend npm run test:coverage
Tests por Categoría
Formularios Críticos
RegisterForm.test.tsx— 80 tests (registro completo)LoginForm.test.tsx— 1038 líneas (referencia principal de formularios)MilestoneDeliveryForm.test.tsx— 101 tests (entregas de hitos)ProposalForm.test.tsx— 92 tests (propuestas de experto)ReviewForm.test.tsx— 75 tests (reseñas)RequestBudgetForm.test.tsx— 24 tests (solicitar presupuesto)
Dashboards y Tabs
OverviewTab.test.tsx— 23 testsAvailableProjectsTab.test.tsx— 24 testsProjectsTab.test.tsx— 34 testsEmpresaStatsCards.test.tsx— 12 testsEmpresaProfileBanners.test.tsx— 21 testsUnifiedProjectDashboard.test.tsx— tests de estado unificado
Componentes Admin
AdminInvitationsManagement.test.tsx— 31 testsAdminProjectApplications.test.tsx— 23 testsAdminExpertSelection.test.tsx— 57 testsAdminProposalManagement.test.tsx— 43 testsAdminUserProfileModal.test.tsx— 69 testsAdminEditUserProfileModal.test.tsx— 22 testsAdminToolPageManagement.test.tsx— 43 testsAdminSkillPageFAQs.test.tsx— 30 testsExpertProfileManager.test.tsx— 33 testsAdminUserManagement.test.tsx— tests de gestión de usuarios
Pagos
PaymentModalStripe.test.tsx— 22 tests (Stripe mock, errores, estados)
Utilidades y API
format.test.ts— 106 tests (formateo, rutas SSR connormalizeProfilePictureUrl)auth.test.ts— 67 tests (registro SSR,acceptAdminInvitation, fallbacks)metrics.test.ts— 29 tests (normalizeStatus,roundRating,calculateTopPerformers)
UI/Presentacionales
progress.test.tsx— 5 testssimple-kanban.test.tsx— 5 testskanban.test.tsx— 6 tests
Tests de Seguridad
email-verification-xss.test.ts— mitigaciones XSS en verificación de email
Patrones Avanzados de Testing
Mock de Radix Dialog y Select
Radix UI usa @radix-ui/react-presence que espera animationend en jsdom — evento que nunca se dispara, causando timeouts. Solución: mockear con implementaciones ligeras.
// Dialog mock — retorna null cuando open=false (eliminación inmediata)
vi.mock('@/components/ui/dialog', () => ({
Dialog: ({ open, children, ...props }: any) =>
open ? <div role="dialog" {...props}>{children}</div> : null,
DialogPortal: ({ children }: any) => <>{children}</>,
// ...demás subcomponentes
}));
// Select mock — HTML nativo para tests determinísticos
vi.mock('@/components/ui/select', () => ({
Select: ({ children, value, onValueChange }: any) => (
<select value={value} onChange={(e) => onValueChange?.(e.target.value)}>
{children}
</select>
),
SelectContent: ({ children }: any) => <>{children}</>,
SelectItem: ({ children, value }: any) => <option value={value}>{children}</option>,
}));
Testing de Rutas SSR
Para cubrir branches typeof window === 'undefined' en jsdom:
beforeEach(() => {
delete (globalThis as any).window;
vi.stubGlobal('Astro', { url: new URL('https://example.com/es/page') });
});
afterEach(() => {
globalThis.window = originalWindow;
});
Inputs Controlados
userEvent.type() dispara onChange carácter por carácter en inputs controlados. Usar fireEvent.change() para valor completo:
fireEvent.change(input, { target: { value: 'Vue' } }); // ✅
// userEvent.type(input, 'Vue'); // ❌ dispara V, u, e por separado
Infraestructura de Testing
Page Object Model
Patrones reutilizables en tests/page-objects/:
LoginPageDashboardPageProjectPage- etc.
Fixtures
Datos de prueba centralizados en tests/fixtures/:
- Usuarios de prueba
- Proyectos de prueba
- Datos mock
Mocks de API
Simulación de backend en tests/mocks/:
- Endpoints mockeados
- Respuestas predefinidas
- Estados de error
Global Setup/Teardown
global-setup.ts: Reinicia el escenario demo antes de Playwrightglobal-teardown.ts: Limpia después de Playwright- Evita residuos en la base de datos
Utilidades
Helpers para tests en tests/utils.ts:
- Helpers de autenticación
- Helpers de navegación
- Helpers de formularios
Cobertura de Código
Generar Reporte
# Ejecutar con coverage (tarda ~3-4 minutos)
./scripts/test_frontend_unit.sh -- --coverage
# Alternativa directa
docker compose exec frontend npx vitest run --coverage 2>&1 | tail -100
Objetivos
- Threshold global: 80% en las 4 métricas (statements, branches, functions, lines)
- Estado actual: ✅ Los 4 thresholds se cumplen (Marzo 2026)
Exclusiones de Coverage
Los siguientes archivos están excluidos en vitest.config.ts porque no son testeables con jsdom:
| Patrón | Razón |
|---|---|
src/actions/** | Server actions de Astro (requieren SSR) |
**/index.ts | Barrel files (re-exports puros sin lógica) |
src/components/shared/OpenObserveInit.tsx | Requiere contexto de runtime Astro |
Mejores Prácticas
Escribir Tests E2E
- Usar Page Object Model para reutilización
- Usar fixtures para datos de prueba
- Limpiar estado entre tests
- Usar selectores estables (data-testid)
- Esperar elementos antes de interactuar
Escribir Tests Unitarios
- Mockear dependencias externas
- Testear comportamiento, no implementación
- Usar describe/it para organización
- Testear casos edge y errores
- Mantener tests rápidos y aislados
Troubleshooting Common Issues
Error ECONNREFUSED en Tests de Autenticación
Síntoma: Tests que fallan con FetchError: request to http://localhost:8000/... failed, reason: connect ECONNREFUSED.
Causa: Los tests unitarios de frontend corren en un entorno aislado sin un backend real levantado. El servicio de autenticación (secureAuthService) intenta conectar al backend al inicializarse.
Solución Implementada:
En el método initializeAuth, los errores de conexión se capturan y se tratan como warnings en entorno de test (NODE_ENV === 'test'), permitiendo que la app de prueba cargue incluso si el backend no está disponible.
Mocking de window.secureAuthService
Dado que secureAuthService es ahora un singleton global, los tests que involucran autenticación deben mockear correctamente esta instancia para evitar efectos secundarios o dependencias de red.
// Ejemplo de setup en tests
beforeEach(() => {
// Mock de la instancia global
window.secureAuthService = {
isAuthenticated: vi.fn().mockReturnValue(true),
getUser: vi.fn().mockReturnValue(mockUser),
login: vi.fn().mockResolvedValue(undefined),
logout: vi.fn().mockResolvedValue(undefined),
// ... otros métodos necesarios
} as any;
});
afterEach(() => {
vi.clearAllMocks();
});
Más Información
- Testing Backend - Tests del backend
- Testing E2E - Guía de pruebas manuales
- Testing Security - Tests de seguridad