Selenium requiere gestión separada de drivers del navegador y esperas explícitas en casi cada interacción; Cypress no puede correr cross-browser y pone la ejecución paralela detrás de un plan pago. Playwright maneja todo esto de fábrica: un solo comando de instalación descarga Chromium, Firefox y WebKit, y el auto-waiting significa que los elementos no necesitan llamadas manuales a sleep() antes de las interacciones. Esta guía cubre la instalación, tu primer test que pasa, qué hace cada línea realmente, y cómo leer un trace cuando algo falla.

Por qué Playwright y no otra cosa

Antes de instalar cualquier cosa, la versión corta: Playwright es lo que la mayoría de los proyectos nuevos eligen en 2026, y con razón.

Selenium fue el estándar durante años. Funciona, pero es lento de configurar, verboso de escribir y requiere gestionar los drivers del navegador manualmente. Cypress es más fácil pero solo soporta navegadores basados en Chromium y tiene un muro de pago para las ejecuciones paralelas en CI.

Playwright maneja las aplicaciones web modernas de fábrica. Espera automáticamente a que los elementos estén listos antes de interactuar con ellos, así no necesitas llamadas aleatorias a sleep() en tus tests. Soporta Chromium, Firefox y WebKit. Tiene un test runner integrado, grabación de video de los fallos, y un generador de código que escribe locators por ti mientras haces clic en una página.

Si estás empezando desde cero en 2026, Playwright es la elección correcta.

Instalación

Primero necesitas tener Node.js instalado. Verifica si lo tienes:

node --version

Si eso devuelve un número de versión (v18 o superior está bien), estás listo. Si no, descarga la versión LTS desde nodejs.org.

Ahora crea una nueva carpeta para tu proyecto y ejecuta el instalador de Playwright:

mkdir mis-primeros-tests
cd mis-primeros-tests
npm init playwright@latest

El instalador hace algunas preguntas. Para aprender, acepta los valores por defecto:

  • TypeScript o JavaScript → TypeScript
  • Carpeta de tests → tests
  • Workflow de GitHub Actions → no (agregar después)
  • Instalar navegadores de Playwright →

La descarga de los navegadores tarda uno o dos minutos. Cuando termina, tu proyecto se ve así:

mis-primeros-tests/
  tests/
    example.spec.ts
  playwright.config.ts
  package.json

Eso es todo. Sin archivos de configuración que batallar, sin gestores de drivers, sin test runner separado que instalar.

Ejecutar el test de ejemplo

Antes de escribir cualquier cosa, ejecuta lo que vino con la instalación:

npx playwright test

Playwright ejecuta los tests de ejemplo en modo headless (sin ventana visible del navegador) e imprime los resultados en la terminal. Verás algo como:

Running 2 tests using 2 workers
  2 passed (3s)

Para ver qué pasó realmente, abre el reporte HTML:

npx playwright show-report

Esto abre un navegador con un reporte detallado de cada test: qué pasos corrieron, cuánto tardó cada uno, y capturas de pantalla al final.

Ejecuta npx playwright test --headed para ver el navegador abrirse y ejecutar el test en tiempo real. Útil cuando estás comenzando y querés ver qué está pasando.

Escribir tu primer test

Elimina el archivo de test de ejemplo y crea uno nuevo. Abre tests/login.spec.ts y escribe esto:

import { test, expect } from '@playwright/test';

test('el usuario puede iniciar sesión', async ({ page }) => {
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  await expect(page.getByText('My Travel Items')).toBeVisible();
});

Ejecútalo:

npx playwright test tests/login.spec.ts

Deberías ver 1 passed. Esto es lo que hace cada parte.

Entender lo que acabás de escribir

test('el usuario puede iniciar sesión', async ({ page }) => {

test es la función que define un caso de prueba. El primer argumento es el nombre: escríbelo como una oración que describe qué hace el usuario, no qué hace el código. async significa que la función es asíncrona, lo cual es obligatorio porque cada acción del navegador lleva tiempo. { page } es el fixture de página de Playwright, que te da una pestaña del navegador nueva para trabajar.

await page.goto('https://lab.becomeqa.com')

Abre la URL. await le dice a JavaScript que espere a que esta acción termine antes de pasar a la siguiente línea. Usarás await delante de cada acción de Playwright.

page.getByRole('button', { name: 'Login' })

Esto es un locator. Encuentra un elemento en la página. getByRole encuentra elementos por su rol ARIA y nombre accesible. Encuentra un botón con el texto "Login". Es el tipo de locator más confiable porque está vinculado a cómo funciona el elemento realmente, no a su clase CSS o posición.

await page.getByLabel('Username').fill('admin@becomeqa.com')

Encuentra el campo de input asociado a una etiqueta "Username" y escribe el email en él.

await expect(page.getByText('My Travel Items')).toBeVisible()

Esta es la aserción, la parte que realmente verifica algo. Comprueba que el texto "My Travel Items" es visible en la página después del login. Si el login falla y este texto no aparece, el test falla.

Locators: cómo encontrar elementos

Los locators son cómo le dices a Playwright con qué elemento interactuar. Hay varios tipos y cada uno tiene un caso de uso diferente.

getByRole es la opción preferida. Usa roles ARIA, que describen el significado semántico de los elementos:

page.getByRole('button', { name: 'Enviar' })    // un botón con etiqueta Enviar
page.getByRole('link', { name: 'Inicio' })       // un enlace con etiqueta Inicio
page.getByRole('textbox', { name: 'Buscar' })    // un input de texto con etiqueta Buscar

getByLabel funciona para inputs de formulario que tienen una etiqueta visible:

page.getByLabel('Correo electrónico')
page.getByLabel('Contraseña')

getByPlaceholder cuando no hay etiqueta pero sí hay texto de placeholder:

page.getByPlaceholder('Ingresá tu email')

getByText encuentra elementos por su contenido de texto visible:

page.getByText('My Travel Items')
page.getByText('Cerrar sesión')

getByTestId usa un atributo data-testid. Útil cuando los desarrolladores los agregan específicamente para testing:

page.getByTestId('submit-button')

Evitá los locators basados en clases CSS o XPath. Se rompen cada vez que un desarrollador renombra una clase o reestructura el HTML. Los locators basados en roles sobreviven esos cambios.

Acciones: interactuar con la página

Una vez que tienes un locator, puedes realizar acciones sobre él:

await locator.click()                    // hacer clic en un elemento
await locator.fill('algún texto')        // limpiar y escribir en un input
await locator.type('algún texto')        // escribir carácter por carácter (para inputs que reaccionan a las teclas)
await locator.check()                    // marcar un checkbox
await locator.selectOption('valor')      // seleccionar de un dropdown
await locator.hover()                    // hacer hover sobre un elemento
await locator.press('Enter')             // presionar una tecla del teclado

Una cosa importante: casi nunca necesitas esperar elementos tú mismo. Playwright espera automáticamente a que un elemento sea visible y esté habilitado antes de realizar una acción. Esto se llama auto-waiting y es lo que elimina la mayor parte de la inestabilidad en los tests de Playwright.

Aserciones: verificar el resultado

Una aserción es lo que hace que un test realmente testee algo. Sin aserciones, solo estás haciendo clic por una página.

// Verificar visibilidad
await expect(page.getByText('Bienvenido')).toBeVisible();
await expect(page.getByRole('dialog')).toBeHidden();

// Verificar contenido de texto
await expect(page.getByRole('heading')).toHaveText('My Travel Items');
await expect(page.getByRole('heading')).toContainText('Travel');

// Verificar valores de input
await expect(page.getByLabel('Email')).toHaveValue('test@example.com');

// Verificar URL
await expect(page).toHaveURL('https://lab.becomeqa.com/dashboard');

// Verificar título de la página
await expect(page).toHaveTitle('BecomeQA Lab');

// Verificar conteo de elementos
await expect(page.getByRole('row')).toHaveCount(5);

Las aserciones de Playwright también hacen auto-wait. toBeVisible() no solo verifica en este momento. Reintenta durante hasta 5 segundos por defecto, esperando que aparezca el elemento. Esto significa que tampoco necesitas esperas explícitas antes de las aserciones.

Un test más completo

Acá hay un segundo test que continúa después del login:

import { test, expect } from '@playwright/test';

test('el usuario puede iniciar sesión', async ({ page }) => {
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  await expect(page.getByText('My Travel Items')).toBeVisible();
});

test('la tabla de ítems de viaje tiene datos', async ({ page }) => {
  // iniciar sesión primero
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  // verificar la tabla
  await expect(page.getByRole('table')).toBeVisible();
  const rows = page.getByRole('row');
  await expect(rows).toHaveCount(6); // encabezado + 5 filas de datos
});

Ejecuta los dos tests:

npx playwright test tests/login.spec.ts

Notarás que los pasos de login están duplicados entre los tests. Está bien por ahora. Una vez que tengas más tests, moverás el setup repetido a un bloque beforeEach o a un fixture, pero eso es un tema intermedio.

Cada test en Playwright corre en un contexto de navegador completamente aislado por defecto. Aunque un test inicie sesión, el siguiente comienza desde cero. Esto es intencional: los tests que comparten estado son mucho más difíciles de depurar.

El archivo playwright.config.ts

Abre playwright.config.ts. Los valores por defecto están bien, pero dos configuraciones vale la pena conocer:

export default defineConfig({
  testDir: './tests',
  timeout: 30000,          // tiempo máximo por test en ms
  retries: 0,              // cuántas veces reintentar un test fallido
  use: {
    baseURL: 'https://lab.becomeqa.com',   // agregá esto
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
  ],
});

Si configuras baseURL, puedes escribir page.goto('/') en lugar de la URL completa en tus tests. Agrégala a la config y actualiza tus tests:

await page.goto('/');   // en lugar de 'https://lab.becomeqa.com'

Por defecto Playwright ejecuta los tests en los tres navegadores (Chromium, Firefox, WebKit). Para ejecutar solo en Chromium mientras aprendes:

npx playwright test --project=chromium

Cuando un test falla

Rompamos el test a propósito. Cambia el texto esperado por algo que no existe:

await expect(page.getByText('Este texto no existe')).toBeVisible();

Ejecútalo y verás:

1 failed
  login.spec.ts:10:3 › el usuario puede iniciar sesión ✗

    Error: expect(locator).toBeVisible()
    Locator: getByText('Este texto no existe')
    Expected: visible
    Received: hidden
    Timeout: 5000ms

El error te dice exactamente qué estaba esperando y que nunca se volvió visible. Ahora revisa el trace:

npx playwright show-report

Haz clic en el test fallido. Verás un desglose paso a paso con capturas de pantalla en cada acción. Este es el trace viewer, una de las funcionalidades más útiles de Playwright para depurar fallos.

→ See also: Instalando Playwright: Guía de Configuración Paso a Paso (2026) | Locators de Playwright: getByRole, getByLabel, getByText, getByTestId Comparados | Assertions en Playwright: La Guía Completa | Playwright Trace Viewer: Depura Tests Fallidos Como un Pro

Corrige el test de vuelta a 'My Travel Items' antes de continuar.

FAQ

¿Necesito saber TypeScript antes de empezar?

No. El TypeScript que escribirás para los tests es mínimo, principalmente async/await y tipos básicos. El curso cubre exactamente lo que necesitas a medida que avanzas.

¿Debería usar JavaScript en lugar de TypeScript?

TypeScript es la mejor opción aunque seas nuevo en él. Detecta errores antes de ejecutar el test. La sintaxis extra es pequeña y los beneficios son inmediatos.

¿Cómo encuentro el locator correcto para un elemento?

Usa la herramienta codegen de Playwright: npx playwright codegen https://lab.becomeqa.com. Abre un navegador, registra tus clics y genera el código del locator automáticamente. Úsalo para explorar, luego limpiá lo que genera.

Mi test pasa localmente pero falla en GitHub Actions. ¿Por qué?

Generalmente es un problema de timing o un await faltante. Ejecuta con --headed localmente para ver qué pasa, luego verifica si te falta un await en algún lugar.