# Tests del Frontend - Documentación Este documento describe los tests implementados para el frontend del taller CI/CD. ## Estructura de Tests ``` frontend/src/ ├── utils/ │ ├── text.js # Función prettify() │ └── text.test.js # Tests unitarios ├── services/ │ ├── api.js # Funciones de API │ └── api.test.js # Tests con mocking ├── App.svelte # Componente principal └── App.test.js # Tests de componente ``` ## Tecnologías Utilizadas | Herramienta | Propósito | |-------------|-----------| | **Vitest** | Framework de testing (compatible con Vite) | | **@testing-library/svelte** | Renderizado de componentes Svelte | | **jsdom** | Simulación del DOM en Node.js | --- ## 1. Tests Unitarios: `text.test.js` ### Objetivo Testear funciones puras sin efectos secundarios. La función `prettify()` convierte strings en formato `snake_case` a `Title Case`. ### Tests Implementados | Test | Input | Output Esperado | Concepto | |------|-------|-----------------|----------| | Conversión básica | `'hello_world'` | `'Hello World'` | Transformación de texto | | Múltiples palabras | `'cafe_con_leche'` | `'Cafe Con Leche'` | Manejo de múltiples `_` | | Sin underscore | `'bocadillo'` | `'Bocadillo'` | Edge case | | String vacío | `''` | `''` | Boundary testing | | Mayúsculas | `'TOSTADA_INTEGRAL'` | `'Tostada Integral'` | Normalización | ### Código Explicado ```javascript import { describe, it, expect } from 'vitest'; import { prettify } from './text'; describe('prettify', () => { it('convierte snake_case a Title Case', () => { // Dado un string con underscores const input = 'hello_world'; // Cuando llamamos a prettify const result = prettify(input); // Entonces obtenemos Title Case expect(result).toBe('Hello World'); }); }); ``` ### ¿Por qué este test es ideal para principiantes? 1. **Sin dependencias externas**: No necesita mocking 2. **Determinístico**: Mismo input = mismo output siempre 3. **Fácil de romper**: Cambiar la función hace fallar el test inmediatamente 4. **Concepto claro**: Input → Función → Output --- ## 2. Tests de API: `api.test.js` ### Objetivo Testear funciones que hacen llamadas HTTP sin depender de un servidor real. ### Concepto Clave: Mocking **Mocking** significa "simular" una dependencia externa. En este caso, simulamos `fetch()`: ```javascript beforeEach(() => { // Reemplazamos fetch global con una función simulada vi.stubGlobal('fetch', vi.fn()); }); afterEach(() => { // Restauramos el fetch original vi.unstubAllGlobals(); }); ``` ### Tests Implementados | Test | Qué Verifica | Concepto de Testing | |------|--------------|---------------------| | `getMenu()` éxito | Devuelve datos cuando API OK | Happy path | | `getMenu()` error | Lanza excepción cuando API falla | Error handling | | `getPrices()` items | Extrae `.items` del response | Data transformation | | `getPrices()` vacío | Devuelve `[]` si no hay items | Defensive programming | | `getCiStatus()` | Llama a `/health` | Contract testing | | `getBuildHistory()` | Llama a `/builds` | Contract testing | ### Código Explicado ```javascript describe('getMenu', () => { it('devuelve datos del menu cuando la API responde correctamente', async () => { // 1. ARRANGE: Preparamos el mock const mockMenu = { starters: ['Sopa'], mains: ['Pescado'] }; fetch.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockMenu), }); // 2. ACT: Ejecutamos la función const result = await getMenu(); // 3. ASSERT: Verificamos el resultado expect(fetch).toHaveBeenCalledWith('/taller/api/menu'); expect(result).toEqual(mockMenu); }); }); ``` ### ¿Por qué este test es importante para CI/CD? - **Independencia**: No necesita backend corriendo - **Rapidez**: Se ejecuta en milisegundos - **Fiabilidad**: No falla por problemas de red - **Contract testing**: Verifica que la API se llama correctamente --- ## 3. Tests de Componente: `App.test.js` ### Objetivo Verificar que el componente Svelte renderiza correctamente y muestra la información esperada. ### Concepto Clave: Testing Library `@testing-library/svelte` renderiza componentes y permite buscar elementos como lo haría un usuario: ```javascript import { render, screen, waitFor } from '@testing-library/svelte'; import App from './App.svelte'; // Renderiza el componente render(App); // Busca elementos como un usuario screen.getByText('Menú del día'); screen.getByRole('heading', { level: 1 }); ``` ### Tests Implementados | Test | Qué Verifica | Selector Usado | |------|--------------|----------------| | Título principal | H1 existe | `getByRole('heading', { level: 1 })` | | Secciones menú | Primeros/Segundos/Postres | `getByText('Primeros')` | | Estado API | Muestra "Operativa" | `getByText(/Operativa/)` | | Build number | Muestra "#42" | `getByText('#42')` | | Precios | Muestra "Cafe", "Tostada" | `getByText('Cafe')` | ### Código Explicado ```javascript describe('App.svelte', () => { beforeEach(() => { // Mockeamos fetch para cada endpoint vi.stubGlobal('fetch', createMockFetch()); }); it('muestra las secciones del menu cuando carga', async () => { // Renderizamos el componente render(App); // Esperamos a que cargue (es async) await waitFor(() => { expect(screen.getByText('Primeros')).toBeInTheDocument(); expect(screen.getByText('Segundos')).toBeInTheDocument(); }); }); }); ``` ### ¿Por qué `waitFor`? El componente hace llamadas async al montarse. `waitFor` espera hasta que: 1. La condición se cumple, O 2. Pasa el timeout (falla el test) --- ## Ejecutar los Tests ### Localmente ```bash cd frontend npm install npm test ``` ### En modo watch (desarrollo) ```bash npm test -- --watch ``` ### Con coverage ```bash npm test -- --coverage ``` --- ## Integración con Jenkins Los tests se ejecutan en el pipeline CI (`Jenkinsfile.ci`): ```groovy stage('Frontend: check & build') { steps { dir('frontend') { sh ''' npm install npm run check npm test # ← Ejecuta estos tests npm run build ''' } } } ``` ### Flujo en Jenkins ``` 1. Push/MR → 2. Jenkins detecta → 3. npm test → 4. ¿Pasa? → 5. Build ↓ NO Pipeline FALLA (estudiante corrige) ``` --- ## Cómo Romper los Tests (Ejercicio) Para ver Jenkins fallar, prueba: | Cambio | Test que Falla | |--------|----------------| | Cambiar `prettify()` para no capitalizar | `text.test.js` | | Cambiar endpoint de `/menu` a `/menus` | `api.test.js` | | Eliminar sección "Primeros" del HTML | `App.test.js` | --- ## Glosario | Término | Definición | |---------|------------| | **Unit Test** | Test de una función/módulo aislado | | **Mock** | Simulación de una dependencia | | **Assertion** | Verificación de que algo es verdadero | | **Happy Path** | Escenario donde todo funciona bien | | **Edge Case** | Escenario límite o inusual | | **Coverage** | % del código ejecutado por tests |