Cómo usar Playwright para pruebas de capturas de pantalla
Crea pruebas de capturas de Playwright con líneas base visuales, proyectos de navegador, esperas estables, umbrales de diferencias y revisión de actualizaciones.
14 min de lecturaCaptura de sitios webPor Temposhot Team
Las pruebas de capturas comparan cómo se ve una página ahora con una imagen aprobada de antes. Si los píxeles difieren más de la cantidad permitida, la prueba falla y muestra la diferencia. Playwright Test lo incluye de forma nativa a través de expect(page).toHaveScreenshot(), sin ningún plugin adicional. El método también se llama pruebas de regresión visual.
Los ejemplos de esta guía construyen una sola suite: 12 páginas de marketing y de la app probadas en 3 proyectos de navegador, más 8 componentes compartidos. Todas las cifras de abajo, desde el recuento de líneas base hasta los umbrales de diferencia, proceden de esa suite.
Cuándo merece la pena una prueba de capturas
Las pruebas visuales detectan problemas que las aserciones del DOM no ven: una hoja de estilos que no se cargó, un botón empujado fuera de la pantalla, una fuente que cayó a la del sistema. Merecen el mantenimiento cuando:
- la interfaz tiene un sistema de diseño o componentes compartidos que se usan en muchas páginas
- el CSS cambia a menudo y quien revisa no puede comprobar todas las páginas a mano
- los errores de maquetación ya han llegado a producción antes
Cuestan más de lo que devuelven en páginas llenas de datos en directo, anuncios o contenido de usuarios. Cada ejecución daría resultados distintos, y la suite se convertiría en ruido.
Dimensiona la suite antes de escribir pruebas
Cada aserción crea una imagen de línea base por proyecto. La suite del ejemplo tiene 12 aserciones de página completa y 8 aserciones de componentes, ejecutadas en 3 proyectos. Eso son 60 archivos PNG que crear, revisar y conservar en el repositorio.
Páginas que quedan fuera de la suite
Algunas páginas fallan en cada ejecución sin importar cómo esté escrita la prueba. Un panel con gráficos en directo, un feed de publicaciones de usuarios y una página con banners rotatorios entran en este grupo. Para esas páginas, prueba el estado vacío o un componente con datos fijos.
El equipo del ejemplo dejó fuera 5 páginas candidatas: 2 paneles, un índice de blog con entradas nuevas cada semana, una página de estado y una página de resultados de búsqueda. Cada una está cubierta por aserciones de elementos en sus partes compartidas. La lista de páginas completas se quedó en 12.
Una PNG de página completa de una página de marketing larga puede llegar a un megabyte o más. Sesenta archivos de ese tamaño se acumulan en el historial de Git cada vez que cambian las líneas base. Las aserciones de componentes producen archivos más pequeños y cambian con menos frecuencia, así que cargan con la mayor parte de la suite.
Instala Playwright Test
npm init playwright@latest
El instalador crea playwright.config.ts, una carpeta de pruebas de ejemplo y un flujo de CI opcional, y después descarga los navegadores. Las aserciones de capturas no necesitan nada más.
Fija la versión de Playwright
Cada versión de Playwright incluye compilaciones concretas de los navegadores. Una actualización del navegador puede cambiar ligeramente el renderizado del texto, lo que hace fallar pruebas de capturas que no cambiaron. Fija una versión exacta en package.json y actualízala en una solicitud de cambios aparte.
{
"devDependencies": {
"@playwright/test": "1.48.0"
}
}
Cuando cambie la versión, regenera todas las líneas base en la misma solicitud de cambios y revísalas como un solo lote. Mezclar una subida de versión con un cambio de funcionalidad impide saber qué causó cada diferencia.
Crea la primera prueba de capturas
// tests/visual.spec.ts
import { test, expect } from '@playwright/test';
test('home page matches baseline', async ({ page }) => {
await page.goto('https://example.com/');
await expect(page).toHaveScreenshot('home.png', { fullPage: true });
});
La primera ejecución no tiene línea base con la que comparar. Playwright guarda la captura como imagen esperada y hace fallar la prueba para que la nueva línea base se revise. Ejecuta npx playwright test de nuevo y la prueba pasará contra el archivo guardado.
Cuando una ejecución posterior difiere, Playwright guarda la imagen esperada, la captura real y una imagen de diferencias con los píxeles cambiados resaltados. El informe HTML de npx playwright show-report muestra las tres.
Prueba las 12 páginas desde una sola lista
Escribir 12 pruebas separadas repite las mismas líneas. Una lista de páginas y un bucle crean una prueba con nombre por página. El título de la prueba incluye el nombre de la página, así que -g puede apuntar a una sola más adelante.
// tests/pages.visual.spec.ts
import { test, expect } from '@playwright/test';
const pages = [
{ name: 'home', path: '/' },
{ name: 'pricing', path: '/pricing' },
{ name: 'features', path: '/features' },
{ name: 'docs-index', path: '/docs' },
// ...8 more entries
];
for (const p of pages) {
test(`page ${p.name} matches baseline`, async ({ page }) => {
await page.goto(p.path);
await page.getByRole('heading', { level: 1 }).waitFor();
await page.evaluate(() => document.fonts.ready);
await expect(page).toHaveScreenshot(`${p.name}.png`, { fullPage: true });
});
}
Las rutas relativas necesitan un baseURL en la configuración. La siguiente sección lo añade junto con un servidor web local.
Ejecuta contra una compilación local, no contra producción
Las páginas de producción cambian por motivos ajenos al código que se prueba: ediciones de contenido, pruebas A/B y scripts de terceros. Una compilación local de la rama muestra solo lo que cambió la rama. La opción webServer la arranca antes de las pruebas.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: { baseURL: 'http://localhost:3000' },
webServer: {
command: 'npm run build && npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
reuseExistingServer permite que una persona desarrolladora mantenga su propio servidor en marcha durante las ejecuciones locales. En CI está en false, así que el trabajo siempre arranca una compilación nueva.
Prueba una parte más pequeña de la página
test('pricing card', async ({ page }) => {
await page.goto('https://example.com/pricing');
await expect(page.getByTestId('pro-plan')).toHaveScreenshot('pro-card.png');
});
Las aserciones a nivel de elemento son más pequeñas y fallan menos por motivos no relacionados. Un cambio en la cabecera no romperá la prueba de la tarjeta de precios. Usa aserciones de página completa para unas pocas páginas clave y aserciones de elementos para los componentes.
Elige qué aserción tiene cada página
En la suite del ejemplo, las 12 páginas se dividen en 4 tipos de página. Solo las páginas que la gente juzga como un todo reciben una aserción de página completa. Las páginas construidas con componentes compartidos reciben aserciones de elementos sobre esos componentes.
Añade proyectos de navegador y de dispositivo
Los proyectos de playwright.config.ts ejecutan las mismas pruebas en varios navegadores y perfiles de dispositivo.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
],
});
Cada proyecto mantiene sus propias líneas base. Los nombres de archivo incluyen el proyecto y la plataforma, por ejemplo home-chromium-darwin.png. Tres proyectos significan, por tanto, tres líneas base por aserción, así que añade proyectos solo donde las maquetaciones difieran de verdad.
Guarda las líneas base en una carpeta previsible
La ubicación predeterminada coloca una carpeta -snapshots junto a cada archivo de prueba. Con varios archivos de prueba, las líneas base se reparten por todo el árbol de pruebas. La opción snapshotPathTemplate las mueve a una carpeta por archivo de prueba bajo una raíz común.
// playwright.config.ts
export default defineConfig({
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{-projectName}{ext}',
});
La plantilla omite el sufijo de plataforma, lo que funciona porque todas las ejecuciones usan la misma imagen Docker de Linux. Si algunas ejecuciones siguen haciéndose en macOS, conserva {-snapshotSuffix} en la plantilla para que las dos plataformas nunca compartan un archivo.
Quien revisa puede abrir una carpeta y ver todas las líneas base de un archivo de especificación. Renombrar un archivo de prueba mueve sus líneas base a una carpeta nueva, así que renombra en una solicitud de cambios que también las regenere.
Haz estable el estado de la página
Una captura solo es comparable si la página llega al mismo estado en cada ejecución.
- Espera a un elemento listo, como un título o una imagen cargada, antes de la aserción.
- Usa datos de prueba fijos en lugar de cuentas en directo.
- Define un viewport fijo en la configuración.
- Espera a las fuentes web con
await page.evaluate(() => document.fonts.ready).
toHaveScreenshot ya reintenta hasta que dos capturas consecutivas coinciden, y detiene las animaciones CSS por defecto. Esos dos comportamientos eliminan gran parte del ruido temporal sin código adicional.
Espera una señal, no un tiempo fijo
Un page.waitForTimeout(3000) fijo hace la suite más lenta y aun así falla en un runner lento. Esperar a un elemento concreto ata la aserción al estado de la página. La espera del título en el bucle de páginas de arriba es un ejemplo.
Esperar a que la red se quede en silencio es menos fiable de lo que parece. Las páginas que sondean una API o mantienen una conexión abierta nunca llegan a ese estado, y la espera termina por su tiempo límite. Prefiere un elemento que aparezca solo después de cargar los datos, como la primera fila de una tabla.
Las imágenes con carga diferida por debajo del pliegue no se cargan hasta que entran en la vista. Una captura de página completa de una página así muestra recuadros vacíos. Desplázate hasta el final una vez antes de la aserción, o desactiva la carga diferida en la compilación de prueba.
Congela el tiempo y bloquea los scripts de terceros
Fechas como "Actualizado hace 3 minutos" cambian entre ejecuciones. La API de reloj fija la hora que ve la página. Los scripts de terceros de widgets de chat y analítica pueden insertar elementos tarde, así que la prueba los bloquea a nivel de red.
test.beforeEach(async ({ page }) => {
await page.clock.setFixedTime(new Date('2026-05-01T10:00:00Z'));
await page.route(/(analytics|chat-widget)\./, (route) => route.abort());
});
Bloquear un script también elimina lo que dibuja. Eso es intencionado aquí: la prueba visual cubre la maquetación propia del sitio, no el widget.
Fuentes de inestabilidad y sus soluciones
La suite del ejemplo falló sin cambio de código 9 veces en sus primeras dos semanas. Cada fallo se rastreó hasta una de cinco causas. La tabla empareja cada causa con el cambio que la detuvo.
Oculta o enmascara los elementos volátiles
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [page.locator('.timestamp'), page.locator('.user-avatar')],
});
Las zonas enmascaradas se cubren con una caja sólida en ambas imágenes, así que su contenido se ignora. Para elementos que deben desaparecer por completo, pasa una hoja de estilos con la opción stylePath y ocúltalos allí. Enmascarar es mejor que eliminar elementos, porque la maquetación que los rodea sigue comprobándose.
Enmascara por rol, no por maquetación
Un localizador de máscara que depende de clases de maquetación se rompe cuando cambia el marcado. Un ID de prueba o un rol accesible se mantienen estables entre rediseños. En la suite del ejemplo, cada elemento volátil lleva un data-testid que empieza por volatile-.
await expect(page).toHaveScreenshot('account.png', {
mask: [page.locator('[data-testid^="volatile-"]')],
maskColor: '#FF00FF',
});
Un maskColor brillante hace que las cajas enmascaradas sean evidentes en el informe. Quien revisa puede ver de un vistazo qué zonas ignora la prueba.
Define el umbral de diferencias con cuidado
| Opción | Significado |
|---|---|
maxDiffPixels | Número de píxeles que pueden diferir. |
maxDiffPixelRatio | Proporción de píxeles que pueden diferir, de 0 a 1. |
threshold | Cuánto debe diferir el color de un píxel para contar, de 0 a 1. Por defecto 0,2. |
Mantén las tolerancias bajas. Un maxDiffPixelRatio de 0,01 permite que cambie el 1 por ciento de los píxeles, que en una captura de página completa de 1280×4000 son más de 50.000 píxeles. Es suficiente para ocultar un icono que falta. Empieza con un maxDiffPixels de alrededor de 100 y ajusta solo cuando un falso fallo concreto lo exija.
El cálculo del umbral para una captura de página completa
Las cifras siguientes usan una captura de 1280 por 4000 píxeles, el tamaño de la página de precios del ejemplo. Un icono de 24 por 24 píxeles ocupa 576 píxeles. Una tolerancia por proporción del 1 por ciento permitiría que ese icono desapareciera 88 veces sin fallar.
Define los valores predeterminados una vez en la configuración
Las opciones por llamada se olvidan fácilmente en una prueba nueva. El bloque expect.toHaveScreenshot de la configuración define valores predeterminados para cada aserción. Las pruebas individuales pueden seguir sustituyéndolos.
// playwright.config.ts
export default defineConfig({
expect: {
toHaveScreenshot: {
maxDiffPixels: 100,
threshold: 0.2,
animations: 'disabled',
caret: 'hide',
scale: 'css',
},
},
});
scale: 'css' hace que un píxel CSS equivalga a un píxel de imagen, así que un perfil de dispositivo tipo Retina no duplica el tamaño de la imagen. Las líneas base siguen siendo más pequeñas y los recuentos de píxeles anteriores siguen teniendo sentido.
Revisa y actualiza las líneas base
Cuando un cambio de diseño es intencionado, regenera las líneas base afectadas:
npx playwright test --update-snapshots
Limita las actualizaciones a las pruebas que fallan pasando un nombre de archivo o -g con el título de la prueba. Una actualización general de toda la suite hace fácil que se pasen por alto cambios accidentales.
npx playwright test tests/pages.visual.spec.ts -g "page pricing" --update-snapshots
Lee las tres imágenes en orden
Empieza por la imagen real, no por la diferencia. La imagen real muestra si la página parece rota para una persona. Después abre la diferencia para encontrar dónde cambiaron los píxeles, y la imagen esperada para ver lo que había antes.
La diferencia marca los píxeles cambiados en rojo. Un contorno rojo fino alrededor de cada bloque de texto suele indicar un cambio de fuente o de suavizado de bordes, no un error de maquetación. Un rectángulo rojo sólido en un punto suele significar que un elemento se movió, apareció o desapareció.
El informe HTML también ofrece una vista de deslizador entre la imagen esperada y la real. Arrastrarlo por la zona cambiada muestra desplazamientos pequeños, como un cambio de margen de 2 píxeles, con más claridad que la diferencia.
La vida de una línea base
Una línea base pasa por el mismo ciclo cada vez que la página cambia a propósito. El ciclo tiene un punto de decisión: si la diferencia es intencionada. Todo lo demás es mecánico.
Revisa los cambios de línea base en la solicitud de cambios
Un PNG cambiado en una solicitud de cambios es un cambio de código. GitHub y la mayoría de las herramientas de revisión muestran las diferencias de imagen lado a lado. La lista siguiente es lo que comprueba quien revisa en el equipo del ejemplo antes de aprobar.
Ejecuta las pruebas de capturas en CI
Las líneas base generadas en macOS no coincidirán con las capturas de un runner de Linux, porque las fuentes y el suavizado de bordes difieren. La configuración fiable consiste en generar y comparar las líneas base en el mismo entorno, normalmente la imagen oficial de Docker de Playwright con la misma versión que el paquete instalado.
Una rutina práctica:
- Las personas desarrolladoras ejecutan la suite visual dentro de la imagen Docker, no directamente en macOS.
- CI ejecuta la misma imagen y sube el informe HTML como artefacto si hay fallos.
- Los cambios intencionados reciben líneas base nuevas, confirmadas junto con el cambio de código.
- Quien revisa comprueba las diferencias de imagen en la solicitud de cambios.
Genera líneas base en Docker desde un Mac
La etiqueta de la imagen debe coincidir con la versión de Playwright fijada. Una discrepancia vuelve a traer las diferencias de fuentes y de renderizado que la imagen debe eliminar. Ejecuta el comando desde la raíz del proyecto.
docker run --rm --ipc=host -v "$(pwd)":/work -w /work \
mcr.microsoft.com/playwright:v1.48.0-noble \
/bin/bash -c "npm ci && npx playwright test --update-snapshots -g 'page pricing'"
Un trabajo de CI para la suite visual
El trabajo se ejecuta dentro de la misma imagen, así que no hace falta un paso de descarga de navegadores. Sube el informe solo cuando fallan las pruebas. El informe contiene las imágenes esperada, real y de diferencias de cada fallo.
# .github/workflows/visual.yml
name: visual
on: pull_request
jobs:
visual:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.0-noble
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test tests/*.visual.spec.ts
env:
HOME: /root
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
En el ejemplo, el trabajo completo tarda unos 5 minutos. Ejecutar las 8 aserciones de componentes en cada solicitud de cambios y las 36 aserciones de página solo en la rama principal reduce el trabajo de la solicitud a menos de 3 minutos.
Dónde sigue encajando la captura automatizada sencilla
Las pruebas visuales responden a una sola pregunta: si esta página cambió con respecto a su línea base. No producen un conjunto de revisión para una reunión de diseño, un informe para un cliente o un archivo con fecha, y las personas fuera de ingeniería rara vez las ejecutan.
Para esos trabajos, una herramienta de captura simple es más sencilla. Website Capture de Temposhot recibe una lista de URL en un Mac y captura cada página en Desktop 1920×1080, Tablet 768×1024 o Mobile 390×844 sin código de pruebas. Para comparar dos capturas a ojo, Compare Images resalta las diferencias en el navegador. La entrada del glosario comparación de capturas cubre el método general.
Dos herramientas para dos preguntas distintas
La suite visual y una herramienta de captura suelen convivir en el mismo equipo. Las personas ingenieras son dueñas de la suite. Las personas diseñadoras y de producto usan las capturas para revisiones y archivos.
Errores habituales
- Confirmar líneas base generadas en un portátil y compararlas en CI con Linux.
- Afirmar páginas completas que incluyen fechas, carruseles o anuncios.
- Subir los umbrales para silenciar una prueba inestable en lugar de arreglar el estado de la página.
- Ejecutar
--update-snapshotsen toda la suite después de un único cambio intencionado. - Añadir muchos proyectos de dispositivo antes de que las líneas base de escritorio sean estables.
Clasifica una compilación visual en rojo
Un trabajo visual fallido necesita una decisión, no una nueva ejecución. Volver a ejecutar una prueba inestable hasta que pase oculta la causa y deja pasar la siguiente regresión real. El orden siguiente resuelve la mayoría de los fallos en pocos minutos.
- Descarga el artefacto del informe y cuenta las aserciones fallidas.
- Si fallaron todas las aserciones de un proyecto, revisa primero un cambio de navegador o de fuente en ese proyecto.
- Si falló una página en los 3 proyectos, abre su imagen real; la causa suele estar en la página.
- Si falló una página en un solo proyecto, busca un problema de CSS específico del navegador o una fuente de inestabilidad de la tabla de arriba.
- Corrige la causa o actualiza esa única línea base, nunca las dos cosas en el mismo commit.
Preguntas frecuentes
¿Dónde se guardan las imágenes de línea base?
Por defecto, en una carpeta junto al archivo de prueba, con el nombre del archivo de prueba y el sufijo -snapshots. La opción de configuración snapshotPathTemplate cambia la ubicación.
¿Puedo comparar capturas sin Playwright Test?
Sí. Captura con cualquier herramienta y compara los archivos con una herramienta de diferencias de imagen. Playwright Test añade reintentos, enmascaramiento e informes sobre esa comparación.
¿Las pruebas de capturas deben ejecutarse en cada solicitud de cambios?
Para las aserciones a nivel de componente, normalmente sí. Las suites grandes de página completa pueden ejecutarse de forma programada o antes de las versiones si ralentizan la canalización.
¿Por qué una prueba pasa en local y falla en CI sin cambio de código?
Los dos entornos renderizan el texto de forma distinta. Las fuentes, el suavizado de bordes y la compilación del navegador pueden diferir entre macOS y Linux. Ejecuta la suite en la misma imagen Docker en local y en CI.
¿Con cuántos proyectos debe empezar una suite nueva?
Con uno. Empieza con un único proyecto de escritorio hasta que sus líneas base se mantengan en verde durante una o dos semanas. Después añade el proyecto móvil, porque las maquetaciones móviles son las que más difieren. Añade un segundo navegador de escritorio al final, y solo si las personas usuarias informan de errores de maquetación específicos de un navegador.
¿La suite puede probar páginas tras un inicio de sesión?
Sí. Inicia sesión una vez en un proyecto de configuración, guarda la sesión con storageState y carga ese archivo en los proyectos visuales. Usa una cuenta de prueba dedicada con datos fijos, para que los nombres y los números de la página sean los mismos en cada ejecución.
¿Las líneas base deben guardarse con Git LFS?
Ayuda cuando la suite tiene cientos de imágenes o cuando los archivos PNG cambian a menudo. Para una suite de unos 60 archivos, Git normal suele bastar. Revisa el tamaño del repositorio tras unos meses de actualizaciones de líneas base.
Referencias
¿Necesitas automatización de capturas de sitios web sin código?
Cuando el objetivo es un conjunto de capturas y no un resultado de aprobado o fallido, Website Capture de Temposhot ejecuta una lista de URL en local en un Mac. Consulta automatización de capturas de sitios web o la guía de inicio rápido de Website Capture.
playwrightvisual-testingscreenshot-comparisonci
Términos utilizados aquí: playwright, responsive screenshot, screenshot automation, screenshot comparison, viewport screenshot, visual regression testing
Convierte capturas repetibles en trabajo terminado
Temposhot toma capturas con un temporizador o en cada clic y exporta el resultado en PDF, imágenes, ZIP o MP4.
Publicaciones relacionadas
- Cómo automatizar capturas de sitios web con PlaywrightUsa Playwright para automatizar capturas de sitios web con JavaScript. Captura una página, páginas completas, vistas móviles y lotes de URL con reintentos.
- Cómo automatizar capturas de sitios web con PuppeteerUsa Puppeteer para automatizar capturas de sitios web con JavaScript. Captura una página, páginas completas, lotes de URL y viewports móviles con un script reutilizable.
- Cómo automatizar capturas de sitios web sin códigoElige una forma sin código de automatizar capturas de sitios web: captura desde una lista de URL, una plataforma de automatización con API de capturas, herramientas de monitorización o ayudas del navegador.
- Mejores alternativas a Xnapper para Mac en 2026Compara alternativas a Xnapper para Mac: captura repetida, captura y grabación con desplazamiento, fondos gratuitos, guías multiplataforma y la app integrada.