Tutorial: Una prueba de punta a punta con Playwright
1. Objetivos de aprendizaje
Sección titulada «1. Objetivos de aprendizaje»Al terminar este tutorial vas a ser capaz de:
- Instalar Playwright en tu proyecto Angular sin mezclarlo con las pruebas de Vitest.
- Escribir un recorrido de usuario con localizadores por rol y por etiqueta, y aserciones que esperan solas.
- Preparar datos propios en cada corrida para que la prueba no dependa de lo que dejó la anterior.
- Comprobar que la prueba falla cuando el back no permite el origen del front, y que Vitest no se entera.
- Reconocer los errores más frecuentes de Playwright y qué dicen.
Prerrequisitos: los tutoriales de rutas y menú, detalle por ruta y asociación, porque el recorrido usa esas pantallas. Qué es una prueba de punta a punta y en qué se diferencia de las de Vitest está en la teoría de Pruebas de punta a punta con Playwright: este tutorial la aplica y no la repite.
2. De dónde partes
Sección titulada «2. De dónde partes»Tienes el front funcionando, con el menú, el detalle por ruta y el componente de asociación, y tu back corriendo. La prueba va a hacer, en un navegador real, lo que haría una persona:
- Abrir la aplicación y entrar a “Nuevo artista”.
- Llenar el formulario y guardar. La aplicación vuelve a la lista.
- Abrir el artista nuevo en su propia página.
- Entrar a “Agregar un álbum”, elegir uno y asociarlo.
- Ver el álbum en el detalle del artista.
Es un recorrido que cruza todas las capas: el menú, el formulario, el POST de
creación, el detalle por ruta, el selector con los álbumes del back y el POST de
asociación. Por eso vale una prueba de punta a punta: un fallo en cualquier capa lo
rompe.
3. Instalar Playwright
Sección titulada «3. Instalar Playwright»En la carpeta de tu proyecto Angular:
pnpm add -D @playwright/testnpx playwright install chromiumEl primer comando agrega Playwright como dependencia de desarrollo. El segundo descarga el navegador que va a controlar (Chromium); solo hace falta una vez por computador.
Agrega a tu .gitignore las carpetas que Playwright genera, para no subirlas al
repositorio:
/test-results/playwright-report4. Configurar Playwright
Sección titulada «4. Configurar Playwright»Crea playwright.config.ts en la raíz del proyecto:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({ testDir: './e2e', // Un solo navegador y una prueba a la vez: las pruebas escriben en el back y comparten sus datos. workers: 1, reporter: 'list', use: { // El front debe estar corriendo (ng serve) y el back también. baseURL: 'http://localhost:4200', trace: 'retain-on-failure', }, projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],});testDir: './e2e'pone las pruebas en una carpeta propia, fuera desrc. Asíng test(Vitest) no las encuentra y no intenta correrlas: son dos herramientas con dos comandos distintos. Se comprobó: con la carpetae2ey este archivo presentes, las pruebas de Vitest siguen pasando igual.baseURLes la dirección del front. Con ella,page.goto('/')abrehttp://localhost:4200/.workers: 1corre una prueba a la vez. Las pruebas de punta a punta escriben en el back, y dos corriendo juntas pueden pisarse.trace: 'retain-on-failure'guarda una traza de cada prueba que falla: una grabación paso a paso con lo que la página mostraba, que se abre más adelante.
5. El recorrido
Sección titulada «5. El recorrido»Crea e2e/asociar-album.spec.ts:
import { expect, test } from '@playwright/test';
test('crear un artista, agregarle un álbum y verlo en su detalle', async ({ page }) => { // Un nombre distinto en cada corrida: el back guarda lo que se crea, y la prueba no // debe depender de lo que dejó la anterior. const nombre = `Artista de prueba ${Date.now()}`;
await page.goto('/'); await page.getByRole('link', { name: 'Nuevo artista' }).click();
await page.getByLabel('Nombre').fill(nombre); await page.getByLabel('URL de la foto').fill('https://example.com/foto.jpg'); await page.getByLabel('País de origen').fill('Colombia'); await page.getByLabel('Fecha de nacimiento').fill('1990-05-14'); await page.getByLabel('Biografía').fill('Artista creado por una prueba de punta a punta.'); await page.getByRole('button', { name: 'Crear artista' }).click();
// Al guardar, la aplicación vuelve a la lista, donde aparece el artista nuevo. await expect(page).toHaveURL('/artistas'); await page.getByRole('button', { name: nombre }).click(); await page.getByRole('link', { name: 'Abrir en su propia página' }).click(); await expect(page).toHaveURL(/\/artistas\/\d+$/);
await page.getByRole('link', { name: 'Agregar un álbum' }).click(); await page.getByLabel('Álbum').selectOption({ index: 1 }); await page.getByRole('button', { name: 'Asociar álbum' }).click();
// Al asociar, vuelve al detalle del artista, que ahora muestra un álbum. await expect(page).toHaveURL(/\/artistas\/\d+$/); await expect(page.getByRole('heading', { name: `Álbumes de ${nombre}` })).toBeVisible(); await expect(page.locator('app-album-list a.card')).toHaveCount(1);});Cómo leerla:
pagees la pestaña del navegador que Playwright abre para esta prueba. Cada prueba recibe una nueva y no comparte con las demás el almacenamiento del navegador.- Los localizadores buscan lo que ve el usuario.
getByRole('link', { name: 'Nuevo artista' })busca un enlace por su nombre,getByLabel('Nombre')un campo por su etiqueta. Por eso la prueba no depende de las clases de Bootstrap ni de la estructura del HTML. - No hay esperas. Cada acción (
click,fill) y cadaexpectesperan hasta que el elemento exista y esté listo, o hasta que se acabe el tiempo. No hace falta escribir pausas fijas. Date.now()hace el nombre único. Sin eso, la segunda corrida buscaría un artista que ya existe, y el botón de su nombre aparecería dos veces.selectOption({ index: 1 })elige la primera opción real: la opción 0 es “Elige un álbum”. Lo importante es que la prueba no depende de un álbum concreto.toHaveURLcomprueba la URL con una expresión regular (/\/artistas\/\d+$/) porque eliddel artista nuevo cambia en cada corrida.- La última línea es la prueba de verdad. Las anteriores solo llevan hasta ahí. Comprobar que el detalle muestra un álbum es lo que demuestra que la asociación se creó en el back y que el front la volvió a pedir.
6. Correrla
Sección titulada «6. Correrla»Con el back y el front andando (ng serve), en otra terminal:
npx playwright testDebes ver algo así:
ok 1 [chromium] › e2e\asociar-album.spec.ts:3:5 › crear un artista, agregarle un álbum y verlo en su detalle (3.9s)
1 passed (15.0s)Córrela una segunda vez: también debe pasar. Se comprobó que dos corridas seguidas pasan, porque cada una crea un artista distinto.
7. Lo que solo ve esta prueba: CORS
Sección titulada «7. Lo que solo ve esta prueba: CORS»Vas a comprobar el argumento principal de la teoría. En tu back, cambia
temporalmente el origen permitido en CorsConfig:
.allowedOrigins("http://localhost:4201") // antes: "http://localhost:4200"Reinicia el back y corre las dos herramientas:
ng test --watch=false: todas las pruebas de Vitest siguen en verde. Ninguna hace una petición real, así que ninguna ve que el navegador bloquearía la respuesta.npx playwright test: la prueba falla.
1) [chromium] › e2e\asociar-album.spec.ts:3:5 › crear un artista, agregarle un álbum y verlo en su detalle
Error: expect(page).toHaveURL(expected) failed
Expected: "http://localhost:4200/artistas" Received: "http://localhost:4200/artistas/nuevo" Timeout: 5000msEl mensaje dice qué pasó: la aplicación debía volver a la lista al guardar, y se quedó
en el formulario, porque el navegador bloqueó la respuesta del POST y el front mostró
su error. Playwright esperó los cinco segundos por defecto antes de rendirse.
Postman tampoco habría encontrado el error: no es un navegador y no manda la cabecera
Origin. Se comprobó con la configuración rota: una petición sin Origin, como la de
Postman, sigue respondiendo 200, y la misma petición con Origin: http://localhost:4200
responde 403.
Restaura el origen a http://localhost:4200 y reinicia el back antes de seguir.
La traza
Sección titulada «La traza»Al fallar, Playwright guardó una traza en test-results, dentro de la carpeta de la
prueba que falló (trace.zip). Se abre con:
npx playwright show-trace test-results/<carpeta-de-la-prueba>/trace.zipLa traza muestra, paso a paso, qué hizo la prueba, qué mostraba la página en cada momento y qué peticiones salieron. Es la forma de ver por qué falló sin tener que repetir el recorrido a mano.
8. Errores frecuentes
Sección titulada «8. Errores frecuentes»Cada mensaje de esta lista se provocó a propósito y se leyó tal como aparece:
page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:4200/. El front no está corriendo. Arrancang servey el back antes de correr la prueba.strict mode violation: getByRole('link', { name: 'artista' }) resolved to 2 elements. El localizador coincide con más de un elemento: aquí, con “Artistas” y con “Nuevo artista”, porque el nombre se compara como un fragmento sin distinguir mayúsculas. Haz el nombre más específico ({ name: 'Nuevo artista' }) o agregaexact: true.element is not enabled, o la acción agota el tiempo esperando. El botón está deshabilitado: el formulario es inválido, o falta elegir una opción. Playwright hace clic como una persona y no puede pulsar un botón deshabilitado.expect(page).toHaveURL(...) failed ... Timeout: 5000ms. La URL esperada no llegó. Mira elReceiveddel mensaje y abre la traza: casi siempre la aplicación se quedó en otra pantalla porque una petición falló.- La prueba pasa una vez y falla la siguiente. Depende de datos que dejó la corrida
anterior. Crea datos propios y únicos en cada corrida, como el nombre con
Date.now(). pnpm testno corre las pruebas de Playwright. Es lo esperado:pnpm testcorre Vitest. Las de punta a punta se corren connpx playwright test.
9. Con ayuda de la IA
Sección titulada «9. Con ayuda de la IA»Se puede pedir a la IA el esqueleto de un recorrido: los pasos y los localizadores, a partir de la lista de pasos que escribas.
Lo que debes revisar tú:
- Que los localizadores sean por rol, etiqueta o texto, y no selectores CSS frágiles.
- Que no haya pausas fijas (
waitForTimeout): las aserciones ya esperan. - Que la prueba cree sus propios datos y no dependa de los que ya existen.
- Que el último paso compruebe el resultado de verdad (lo que ve el usuario al final), y no solo que se llegó a una pantalla.
- Que el recorrido no repita casos que ya cubren las pruebas de Vitest.
¿Dónde quedaste?
Sección titulada «¿Dónde quedaste?»Al final de este tutorial tienes:
- Playwright instalado, con su configuración y sus pruebas separadas de las de Vitest.
- Un recorrido de punta a punta que crea un artista, le asocia un álbum y comprueba el resultado.
- La comprobación de que esta prueba detecta un error de CORS que las pruebas de Vitest no ven.
Con esto termina la serie de tutoriales del front del Release 8.