Saltar al contenido principal

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.sh usa .env y aplica overrides temporales (luego lo restaura).
  • Si necesitas un baseline coherente:
cp .env.e2e .env

Suites de Tests

  • Los specs viven en frontend/tests/e2e y 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.md y frontend/tests/e2e/AUDIT.md.
  • Para ejecutar un subconjunto usa --grep o 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étricaPorcentajeThreshold
Statements91.84%80% ✅
Branches81.43%80% ✅
Functions86.39%80% ✅
Lines91.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 tests
  • AvailableProjectsTab.test.tsx — 24 tests
  • ProjectsTab.test.tsx — 34 tests
  • EmpresaStatsCards.test.tsx — 12 tests
  • EmpresaProfileBanners.test.tsx — 21 tests
  • UnifiedProjectDashboard.test.tsx — tests de estado unificado

Componentes Admin

  • AdminInvitationsManagement.test.tsx — 31 tests
  • AdminProjectApplications.test.tsx — 23 tests
  • AdminExpertSelection.test.tsx — 57 tests
  • AdminProposalManagement.test.tsx — 43 tests
  • AdminUserProfileModal.test.tsx — 69 tests
  • AdminEditUserProfileModal.test.tsx — 22 tests
  • AdminToolPageManagement.test.tsx — 43 tests
  • AdminSkillPageFAQs.test.tsx — 30 tests
  • ExpertProfileManager.test.tsx — 33 tests
  • AdminUserManagement.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 con normalizeProfilePictureUrl)
  • auth.test.ts — 67 tests (registro SSR, acceptAdminInvitation, fallbacks)
  • metrics.test.ts — 29 tests (normalizeStatus, roundRating, calculateTopPerformers)

UI/Presentacionales

  • progress.test.tsx — 5 tests
  • simple-kanban.test.tsx — 5 tests
  • kanban.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/:

  • LoginPage
  • DashboardPage
  • ProjectPage
  • 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 Playwright
  • global-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ónRazón
src/actions/**Server actions de Astro (requieren SSR)
**/index.tsBarrel files (re-exports puros sin lógica)
src/components/shared/OpenObserveInit.tsxRequiere contexto de runtime Astro

Mejores Prácticas

Escribir Tests E2E

  1. Usar Page Object Model para reutilización
  2. Usar fixtures para datos de prueba
  3. Limpiar estado entre tests
  4. Usar selectores estables (data-testid)
  5. Esperar elementos antes de interactuar

Escribir Tests Unitarios

  1. Mockear dependencias externas
  2. Testear comportamiento, no implementación
  3. Usar describe/it para organización
  4. Testear casos edge y errores
  5. 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