playwright.config.ts establece el timeout de tests, la cantidad de reintentos, el número de workers paralelos, el comportamiento de trazas y la base URL para cada test de la suite. Los valores por defecto de npm init playwright@latest funcionan en local, pero tienen dos problemas en CI: workers: undefined provoca contención de recursos en runners compartidos, y la ausencia de forbidOnly: !!process.env.CI permite que un test.only() commiteado saltee silenciosamente toda la suite. Esta guía cubre cada opción que usarás en un proyecto real y termina con una configuración lista para producción que maneja el setup de autenticación, URLs específicas por entorno y reporters separados para local y CI.
La configuración mínima
Después de npm init playwright@latest obtienes algo así:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});Opciones de nivel superior
testDir
Dónde busca Playwright los archivos de test.
testDir: './tests',Puede ser una ruta relativa o absoluta. Los archivos que coincidan con /.spec.ts o /.test.ts dentro de esta carpeta se detectan automáticamente.
fullyParallel
fullyParallel: true,Con true: todos los tests corren en paralelo entre workers, incluso los tests del mismo archivo. Con false (por defecto anteriormente): los tests dentro de un archivo corren secuencialmente, pero distintos archivos corren en paralelo.
Para la mayoría de los proyectos: usa true. Los tests deberían ser suficientemente independientes para correr en cualquier orden.
forbidOnly
forbidOnly: !!process.env.CI,Si un test tiene test.only(), esto hace que toda la ejecución falle. El patrón !!process.env.CI significa que solo se aplica en CI: puedes usar test.only() en local mientras depuras, pero no puedes commitearlo por accidente.
Usa siempre esto en configuraciones de producción
retries
retries: process.env.CI ? 2 : 0,Cuántas veces reintentar un test fallido antes de marcarlo como fallido. En CI: dos reintentos (ayuda con tests inestables). En local: sin reintentos (para ver los fallos de inmediato).
Si un test falla en el primer intento pero pasa en el reintento, Playwright lo marca como "flaky" en el reporte.
workers
workers: process.env.CI ? 1 : undefined,Cuántos workers paralelos (instancias de navegador) ejecutar. undefined (por defecto) usa el 50% de los núcleos del CPU. 1 corre todos los tests secuencialmente, útil en CI para evitar contención de recursos. 4 corre 4 workers en paralelo.
Para desarrollo local con una máquina rápida, undefined está bien. Para CI, 1 es más seguro a menos que tengas un runner potente.
timeout
timeout: 30_000, // 30 segundos por testTiempo máximo que puede correr un solo test antes de marcarse como fallido. El valor por defecto es 30 segundos. Para tests lentos o máquinas de CI lentas, súbelo a 60 segundos.
expect.timeout
expect: {
timeout: 5_000, // 5 segundos por cada aserción expect()
},Las aserciones de Playwright hacen reintento automático: siguen intentando hasta que la condición se cumpla o expire este timeout. El valor por defecto es 5 segundos.
El bloque use (opciones compartidas de tests)
use contiene opciones que se aplican a todos los tests (salvo que un proyecto las sobreescriba).
use: {
baseURL: 'http://localhost:3000',
// Ajustes del navegador
headless: true,
viewport: { width: 1280, height: 720 },
// Grabación
trace: 'on-first-retry', // 'on-first-retry' | 'retain-on-failure' | 'always' | 'off'
video: 'retain-on-failure', // 'retain-on-failure' | 'always' | 'off'
screenshot: 'only-on-failure',
// Tiempos
actionTimeout: 10_000, // Timeout por cada acción (click, fill, etc.)
navigationTimeout: 30_000, // Timeout para page.goto() y page.waitForURL()
// HTTP
ignoreHTTPSErrors: true, // Útil para entornos de staging con certs autofirmados
// Localización
locale: 'es-AR',
timezoneId: 'America/Argentina/Buenos_Aires',
},baseURL
Cuando está configurado, page.goto('/login') resuelve a http://localhost:3000/login. Hace que los tests sean portables entre entornos.
Usa variables de entorno:
baseURL: process.env.BASE_URL || 'http://localhost:3000',trace
Las trazas de Playwright capturan una grabación completa de cada acción (snapshot del DOM, screenshots, llamadas de red) para depuración. Opciones:
'on-first-retry': captura en el primer reintento de un test fallido (recomendado)'retain-on-failure': captura en cualquier test fallido (más almacenamiento)'always': captura todo (mucho almacenamiento, lento)'off': sin trazas
Ver trazas con npx playwright show-trace trace.zip.
video
'retain-on-failure' guarda video solo para tests fallidos (recomendado). 'always' graba video de todo. 'off' desactiva el video.
projects: múltiples configuraciones de navegador
Los proyectos te permiten correr los mismos tests en distintos navegadores, viewports o configuraciones.
projects: [
// Navegadores de escritorio
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Móvil
{
name: 'mobile-chrome',
use: { ...devices['Pixel 7'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 14'] },
},
],devices['Desktop Chrome'] es un preset que configura viewport, user agent y otros valores por defecto específicos del navegador. Lista completa: npx playwright show-report o la documentación de Playwright.
Proyectos para distintos entornos
projects: [
{
name: 'staging',
use: {
...devices['Desktop Chrome'],
baseURL: 'https://staging.miapp.com',
},
testMatch: '**/*.spec.ts',
},
{
name: 'production',
use: {
...devices['Desktop Chrome'],
baseURL: 'https://miapp.com',
},
testMatch: '**/smoke/*.spec.ts', // Solo smoke tests en producción
},
],Proyectos de setup (configuración global)
projects: [
{
name: 'setup',
testMatch: /.*\.setup\.ts/, // Archivos como auth.setup.ts
},
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
dependencies: ['setup'], // Corre setup primero
},
],Se usa para ejecutar el setup de autenticación una sola vez antes de todos los tests y reutilizar el estado de auth guardado.
reporter
Qué formato usar para generar los resultados de tests:
reporter: [
['html'], // Reporte HTML en playwright-report/
['junit', { outputFile: 'results.xml' }], // XML JUnit para CI
['list'], // Salida en consola en tiempo real
],Para desarrollo local: 'html' es ideal; abre playwright-report/index.html para un reporte visual completo. Para CI: agrega 'junit' para que las herramientas de CI (GitHub Actions, GitLab CI, Jenkins) puedan parsear los resultados.
globalSetup y globalTeardown
Para setup que corre una sola vez antes de que empiece toda la suite de tests:
globalSetup: './global-setup.ts',
globalTeardown: './global-teardown.ts',// global-setup.ts
import { chromium } from '@playwright/test';
export default async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
// Iniciar sesión y guardar el estado de auth
await page.goto('http://localhost:3000/login');
await page.fill('[data-testid="email"]', 'admin@test.com');
await page.fill('[data-testid="password"]', 'AdminPass1');
await page.click('[data-testid="submit"]');
await page.context().storageState({ path: 'auth.json' });
await browser.close();
}Los tests usan el estado de auth guardado sin iniciar sesión de nuevo:
use: {
storageState: 'auth.json',
},Una configuración lista para producción
import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';
dotenv.config();
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
timeout: 45_000,
expect: { timeout: 8_000 },
reporter: [
['html'],
...(process.env.CI ? [['junit', { outputFile: 'test-results/results.xml' }] as const] : []),
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
video: 'retain-on-failure',
screenshot: 'only-on-failure',
actionTimeout: 10_000,
navigationTimeout: 30_000,
},
projects: [
{
name: 'setup',
testMatch: /auth\.setup\.ts/,
},
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
{
name: 'api',
testMatch: '**/api/**/*.spec.ts',
use: { storageState: undefined }, // Los tests de API no necesitan auth de navegador
},
],
});Ejecutar proyectos específicos
# Todos los tests en chromium
npx playwright test --project=chromium
# Solo el proyecto de setup
npx playwright test --project=setup
# Múltiples proyectos
npx playwright test --project=chromium --project=firefoxResumen de las opciones más importantes
| Opción | Recomendación |
|--------|---------------|
| testDir | './tests' |
| fullyParallel | true |
| forbidOnly | !!process.env.CI |
| retries | CI ? 2 : 0 |
| timeout | 30_000 a 60_000 |
| trace | 'on-first-retry' |
| video | 'retain-on-failure' |
| baseURL | Desde variable de entorno |
| reporter | HTML en local, más JUnit en CI |