Instalas Playwright, corres el ejemplo que viene de fábrica y en veinte minutos tienes un test en verde.

Y después te quedas parada.

Porque ese verde no es tu framework. Es la herramienta funcionando sola, sin que hayas tomado todavía ninguna decisión de diseño. El trabajo de verdad —el que te tomó semanas la primera vez que lo hiciste— empieza en el test número dos.

Dónde estás parada

Esta es la parada 07 de una serie que le devuelve a QA tres palabras que el mundo de la IA usa como si las hubiera inventado. Hasta acá vimos de dónde salen loop, goal y harness, por qué tu framework de tests es un harness, las seis piezas que toda herramienta comparte y de dónde tiene que salir tu valor esperado. Todo eso fue modelo mental. Hoy bajamos al teclado.

En la anatomía te di las seis piezas de cualquier harness. Hoy las construimos, pero no en el orden en que las listé. Las piezas no aparecen cuando decides construirlas: aparecen cuando un dolor te obliga.

El recorrido son cinco dolores, en orden, y cada uno trae su pieza. Vas a saber en cuál estás por el título: todos dicen qué duele y qué pieza aparece. Y el orden es el mismo tengas Playwright, Cypress o Selenium.


Lo que te regalan sin pedirlo — piezas 2 y 3

Antes de escribir una línea, dos piezas ya vienen resueltas.

Terminal window
npm init playwright@latest
npx playwright test

Con eso tienes el runner (pieza 2: descubre los archivos, ordena los casos, los corre en paralelo, reintenta) y las herramientas (pieza 3: el navegador y la API para tocarlo). Son las dos que menos vas a pensar y las que más trabajo te ahorran.

Tu primer flujo verde vive ahí:

import { test, expect } from "@playwright/test";
test("un usuario válido entra al dashboard", async ({ page }) => {
await page.goto("/login");
await page.fill("#email", "qa@ejemplo.com");
await page.fill("#password", "claveValida");
await page.click("#entrar");
await expect(page).toHaveURL("/dashboard");
});

Eso es todo lo que necesitas para el primer verde. Y es exactamente donde la mayoría se queda: con un test que funciona y ninguna idea de cómo llegar al número cincuenta.

Si no tienes una app para practicar

No automatices el sitio de tu empresa para aprender: vas a mezclar el aprendizaje con datos reales y permisos ajenos. Usa una app de práctica —yo armé Academia sin Humo justamente para eso— y recién después lleva el modelo a tu trabajo.


Dolor 1: el login repetido en cada test — pieza 1, setup

Llegas al tercer caso y tu archivo se ve así:

test("el usuario ve su saldo", async ({ page }) => {
await page.goto("/login"); // ┐
await page.fill("#email", "qa@ejemplo.com"); // │ otra vez
await page.fill("#password", "claveValida"); // │ lo mismo
await page.click("#entrar"); // ┘
await expect(page.getByTestId("saldo")).toHaveText("₲ 90.000");
});
test("el usuario descarga su factura", async ({ page }) => {
await page.goto("/login"); // ┐
await page.fill("#email", "qa@ejemplo.com"); // │ y otra vez
await page.fill("#password", "claveValida"); // │ lo mismo
await page.click("#entrar"); // ┘
await expect(page.getByRole("link", { name: "Factura" })).toBeVisible();
});

El día que le cambien el id al botón de entrar, se te caen los quince tests de golpe. Ninguno por un bug: los quince por la misma línea repetida quince veces.

Ese es el aviso de la pieza 1: setup.

Antes del código, la idea. Cuando entras a una aplicación, el servidor te devuelve un pase —una cookie, un token— y el navegador lo guarda. Mientras lo tengas, no te vuelve a pedir la contraseña; por eso no haces login cada vez que abres tu correo. Playwright puede guardar ese pase en un archivo y entregárselo a los demás tests cuando arrancan. Ahí está todo el truco: entrar una sola vez, y que los otros empiecen ya adentro.

Son dos piezas y hacen falta las dos.

La primera es un test aparte, que no prueba nada: solo entra y guarda el pase.

auth.setup.ts
import { test as setup, expect } from "@playwright/test";
const PASE = "playwright/.auth/usuario.json";
setup("inicia sesión una vez", async ({ page }) => {
await page.goto("/login");
await page.fill("#email", "qa@ejemplo.com");
await page.fill("#password", process.env.CLAVE_QA!);
await page.click("#entrar");
await expect(page).toHaveURL("/dashboard");
// Guarda en un archivo las cookies y el storage del navegador.
await page.context().storageState({ path: PASE });
});

La segunda es la configuración, que es la que casi nadie copia: le dice al resto de los tests que arranquen con ese pase puesto, y que esperen a que el primero termine.

playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
projects: [
// 1. Primero corre este y deja el pase escrito.
{ name: "setup", testMatch: /auth\.setup\.ts/ },
// 2. Después corren tus tests, ya autenticados.
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
storageState: "playwright/.auth/usuario.json",
},
dependencies: ["setup"],
},
],
});

Si te saltas la segunda parte, el archivo se genera y nadie lo usa: tus tests siguen llegando a la pantalla de login y no entiendes por qué. Es el error más común al copiar este patrón suelto de la documentación.

Con las dos, tus tests quedan así:

test("el usuario ve su saldo", async ({ page }) => {
await page.goto("/dashboard"); // ya está dentro
await expect(page.getByTestId("saldo")).toHaveText("₲ 90.000");
});

Ningún test vuelve a saber cómo es el formulario de login. Uno solo lo sabe. Cuando cambie, tocas un archivo.

La primera pieza que construyes de verdad no es la que más te enseñaron. Es la que te duele tres veces seguidas.


Dolor 2: el test pasa y no sé qué prueba — pieza 4, el goal

Este es el más silencioso, y el más caro.

Mira el primer test otra vez. Verifica que la URL cambió a /dashboard. ¿Eso prueba que el login funciona? Prueba que hubo una redirección. Si la app te manda al dashboard con la sesión vencida y sin datos, ese test sigue verde.

Acá aparece la pieza 4: el goal. Y no es una pieza que se instale — es una decisión tuya, caso por caso:

// Antes: verifica que algo pasó
await expect(page).toHaveURL("/dashboard");
// Después: verifica que pasó lo correcto
await expect(page).toHaveURL("/dashboard");
await expect(page.getByRole("heading", { name: "Hola, Adriana" })).toBeVisible();
await expect(page.getByTestId("saldo")).toHaveText("₲ 90.000");

Ese ₲ 90.000 es el que decide si tu suite sirve. Si lo sacaste de la regla de negocio, tienes un oráculo. Si lo sacaste de correr la app y copiar lo que mostraba, tienes una fotocopia — lo desarmé entero en la parada 06, El goal verificable: el problema del oráculo, y es el error que más suites en verde ha arruinado.


Dolor 3: toco un test y se rompen cuatro — organizar lo que ya tienes

Los selectores están repartidos por todos lados. Cambia un botón y sales a cazarlo por quince archivos.

Acá no aparece una pieza nueva: aparece la organización de las que ya tienes. Es el momento del Page Object Model, ni antes ni después. Antes de los quince tests es burocracia; después es una deuda que cuesta pagar.

No lo repito acá porque ya lo conté paso a paso en la Parte 2 de la trilogía. Lo que sí quiero que veas es esto: cuando llegas al POM con el modelo de las piezas en la cabeza, entiendes qué es realmente. Un Page Object no es una capa de arquitectura porque sí: es dónde guardas la pieza 3. Ahí vive lo que toca el sistema, separado de lo que decide si está bien.


Dolor 4: falló a las tres de la mañana y no sé por qué — pieza 5, reporting

El reporte dice TimeoutError en la línea 14. Corres el test en tu máquina y pasa. Bienvenida a la parte que separa un framework de un script.

Pieza 5: reporting. En Playwright se activa con una línea de configuración:

playwright.config.ts
export default defineConfig({
use: {
trace: "on-first-retry",
screenshot: "only-on-failure",
},
});

Con eso, cada fallo te deja un trace: la película completa de lo que pasó, paso por paso, con el DOM de cada momento y las peticiones de red.

Terminal window
npx playwright show-trace trace.zip

Y entonces la pregunta deja de ser “¿por qué falló?” y pasa a ser “¿qué veo acá?”. Es la diferencia entre reproducir a mano durante media hora y entender en dos minutos.

La pieza que casi nadie configura hasta que la necesita

El trace no sirve de nada si lo activas después del fallo que querías investigar. Configúralo el día uno, cuando todavía no lo necesitas. Es la única pieza del harness que se instala en pasado.


Dolor 5: corre solo cuando yo lo corro — pieza 6, CI

Última pieza, y la que decide si esto queda vivo o se muere en tu carpeta.

Pieza 6: CI. Mientras la suite dependa de que tú te acuerdes de ejecutarla, no es un quality gate: es un hobby. El día que estés de vacaciones, nadie la corre. El paso a paso de un pipeline real está en la guía de CI con GitHub Actions — ahí el ejemplo es mobile, pero el pipeline es el mismo: instalar, correr, publicar el reporte, fallar el build si algo se rompe.


El orden es el mapa

Fíjate lo que hicimos. No construimos un framework: seguimos una secuencia de dolores, y cada uno trajo su pieza.

2 y 3 Te las regala la herramienta
1 y 4 Aparecen en el test 3
5 y 6 Las que casi nadie termina

Ese orden no es de Playwright. Instala Cypress y vas a repetirlo igual: el runner y el navegador vienen puestos, el estado repetido te va a doler en el tercer caso, el should decorativo te va a mentir igual, y la orquestación va a seguir siendo lo último que hagas. Cambia el nombre del método, no la secuencia.

Y si estás mirando un framework heredado en vez de uno nuevo, este mismo orden te sirve de diagnóstico: casi siempre lo que falta son la 5 y la 6. Corre, pero solo cuando alguien está delante.

Un framework no se construye en el orden en que se explica. Se construye en el orden en que duele.


Lo que quiero que te lleves

El primer flujo verde te lo da la herramienta en veinte minutos. Las otras cuatro piezas te las va a pedir tu propia suite, en este orden: el estado que se repite, el criterio que decide, el desorden a los quince tests, el fallo que no puedes reproducir y el día que te vas de vacaciones.

Cuando reconoces la señal, dejas de improvisar. No estás aprendiendo Playwright: estás construyendo un arnés, y la herramienta es la piel.

Y si leíste lo que hacen los tres agentes de Playwright, ahora tienes el otro lado del mismo asunto: cada pieza que ellos te escriben solos la acabas de armar con las manos, así que ya sabes qué decide cada una y dónde entra tu criterio. Esa es exactamente la diferencia entre dirigir a esos agentes y confiar en ellos.

La próxima parada de la serie sale de acá: tienes el arnés armado en Playwright, pero ¿y si mañana te toca otra herramienta, u otro dominio? Vamos a elegir stack sin casarte con ninguna herramienta, por contexto y no por moda.