Testing e2e
Para continuar la misma TaskApp, después de Vitest añadiría Playwright para los tests E2E. Es perfecto para mostrar la diferencia: Vitest prueba funciones; Playwright prueba la aplicación como lo haría un usuario.
1. ¿Qué vamos a probar? #
Hasta ahora nuestro test unitario hace algo así:
1formatearFecha(fecha) 2 ↓ 3"18/09/2026 10:30" 4 ↓ 5 ✅
Ahora queremos comprobar un flujo real:
1Abrir TaskApp 2 ↓ 3Escribir "Comprar leche" 4 ↓ 5Pulsar "Añadir" 6 ↓ 7Comprobar que aparece la tarea 8 ↓ 9 ✅
Esto es un test End-to-End (E2E).
2. Instalar Playwright #
Dentro del proyecto:
1npm init playwright@latest
Durante la instalación aparecerán varias preguntas. Para esta práctica escogería JavaScript, no TypeScript.
Playwright instalará los paquetes necesarios y los navegadores que utilizará para las pruebas.
Después tendremos algo parecido a:
1taskapp/ 2├── src/ 3│ ├── main.js 4│ ├── style.css 5│ ├── utils.js 6│ └── utils.test.js 7│ 8├── tests/ 9│ └── example.spec.js 10│ 11├── playwright.config.js 12├── package.json 13├── package-lock.json 14└── index.html
Podemos borrar el test de ejemplo:
1tests/example.spec.js
3. ¿Qué ha cambiado en package.json? #
Esta parte es especialmente interesante para el curso.
Ahora aparecerá Playwright como otra devDependency, por ejemplo:
1"devDependencies": { 2 "@playwright/test": "...", 3 "vite": "...", 4 "vitest": "..." 5}
¿Por qué?
1dependencies 2└── dayjs 3 → la utiliza TaskApp 4 5devDependencies 6├── vite 7│ → desarrollo/build 8├── vitest 9│ → tests unitarios 10└── @playwright/test 11 → tests E2E
El usuario de TaskApp no necesita Playwright.
4. Preparar Vite para los tests #
Queremos que Playwright pueda arrancar nuestra aplicación automáticamente.
En playwright.config.js podemos configurar:
1import { defineConfig } from "@playwright/test"; 2 3export default defineConfig({ 4 5 testDir: "./tests", 6 7 use: { 8 baseURL: "http://localhost:5173" 9 }, 10 11 webServer: { 12 command: "npm run dev", 13 url: "http://localhost:5173", 14 reuseExistingServer: true 15 } 16 17});
Esto es muy cómodo.
Cuando lancemos los tests:
1Playwright 2 ↓ 3npm run dev 4 ↓ 5Vite 6 ↓ 7http://localhost:5173 8 ↓ 9navegador 10 ↓ 11test
No necesitamos arrancar Vite manualmente.
5. Crear nuestro primer test E2E #
Creamos:
1tests/ 2└── taskapp.spec.js
Con:
1import { test, expect } from "@playwright/test"; 2 3test("muestra el título de la aplicación", async ({ page }) => { 4 5 await page.goto("/"); 6 7 await expect( 8 page.getByRole("heading", { name: "Mis tareas" }) 9 ).toBeVisible(); 10 11});
Aquí aparecen conceptos nuevos:
1page
representa la página del navegador.
1page.goto("/")
abre nuestra aplicación.
Y:
1expect(...)
comprueba el resultado.
6. Nuestro primer test E2E de verdad #
Ahora probamos TaskApp como lo haría un usuario:
1import { test, expect } from "@playwright/test"; 2 3test("permite añadir una tarea", async ({ page }) => { 4 5 await page.goto("/"); 6 7 await page 8 .getByPlaceholder("Escribe una tarea") 9 .fill("Comprar leche"); 10 11 await page 12 .getByRole("button", { name: "Añadir" }) 13 .click(); 14 15 await expect( 16 page.getByText(/Comprar leche/) 17 ).toBeVisible(); 18 19});
Este test ya es muy ilustrativo.
Playwright está haciendo:
1🌐 Abre navegador 2 ↓ 3abre TaskApp 4 ↓ 5📝 Escribe "Comprar leche" 6 ↓ 7🖱️ pulsa "Añadir" 8 ↓ 9👀 busca "Comprar leche" 10 ↓ 11¿Existe? 12 ↓ 13 ✅
7. Añadir un id o mejorar los selectores #
En nuestro HTML tenemos:
1<input 2 id="tarea" 3 placeholder="Escribe una tarea" 4> 5 6<button id="añadir"> 7 Añadir 8</button>
Playwright recomienda en general seleccionar elementos como lo haría el usuario, por ejemplo por rol, etiqueta o texto.
Por eso:
1page.getByRole("button", { name: "Añadir" })
es preferible en muchos casos a:
1page.locator("#añadir")
Esto también introduce de pasada accesibilidad y HTML semántico.
8. Probar que el input queda vacío #
Podemos ampliar el test:
1test("permite añadir una tarea", async ({ page }) => { 2 3 await page.goto("/"); 4 5 const input = page.getByPlaceholder("Escribe una tarea"); 6 7 await input.fill("Comprar leche"); 8 9 await page 10 .getByRole("button", { name: "Añadir" }) 11 .click(); 12 13 await expect( 14 page.getByText(/Comprar leche/) 15 ).toBeVisible(); 16 17 await expect(input).toHaveValue(""); 18 19});
Estamos comprobando dos comportamientos:
1Añadir tarea 2 │ 3 ├── aparece en la lista ✅ 4 │ 5 └── input queda vacío ✅
9. Ejecutar los tests #
Podemos ejecutar:
1npx playwright test
Playwright normalmente ejecutará los tests sin mostrar el navegador.
Para ver lo que está haciendo, podemos ejecutar:
1npx playwright test --headed
10. Modo interfaz #
También podemos utilizar:
1npx playwright test --ui
Se abre la interfaz de Playwright, desde donde podemos ejecutar y analizar los tests.
11. Añadir scripts al package.json #
1npx playwright test --headed
Creamos scripts:
1"scripts": { 2 "dev": "vite", 3 "build": "vite build", 4 "preview": "vite preview", 5 6 "test": "vitest", 7 "test:unit": "vitest", 8 "test:e2e": "playwright test", 9 "test:e2e:ui": "playwright test --ui" 10}
Ahora tenemos:
1npm run test:unit
para tests unitarios y:
1npm run test:e2e
para E2E.
Para abrir la interfaz:
1npm run test:e2e:ui
Esto vuelve a reforzar el papel de package.json como centro de configuración/comandos del proyecto.
12. Hacer que falle #
Como hicimos con Vitest, provocaría un error intencionadamente.
Por ejemplo:
1await expect( 2 page.getByText(/Comprar pan/) 3).toBeVisible();
Pero hemos introducido:
1Comprar leche
Ejecutamos:
1npm run test:e2e
💥 Test fallido.
Playwright no está simplemente automatizando clics: está verificando el comportamiento esperado de la aplicación.