Un paso de upload sin if: always() se saltea cuando los tests fallan, dejándote sin reporte HTML ni capturas de pantalla exactamente cuando más los necesitas. El reporte de tests en CI tiene tres capas independientes: almacenamiento de artefactos para inspección posterior, JUnit XML para parseo en dashboards, y comentarios en PRs que muestran los fallos donde los desarrolladores los van a ver. Este artículo cubre las tres capas con ejemplos de configuración para GitHub Actions y GitLab, más notificaciones de Slack y parseo programático de resultados.
El problema con la salida sin formato
La salida por defecto de CI para una ejecución fallida de Playwright se ve así:
✘ tests/login.spec.ts > muestra error para contraseña incorrecta (15234ms)
Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
Call log:
- expect.toBeVisible with timeout 5000ms
- waiting for locator('[data-testid="error-message"]')Tienes que leer cientos de líneas de salida para encontrar los fallos. En una suite de 200 tests, eso es un problema real.
Formatos de reporte
JUnit XML
El formato universal: todos los sistemas CI lo entienden.
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="Login Tests" tests="5" failures="1" time="12.5">
<testcase name="muestra error para contraseña incorrecta" time="15.2">
<failure message="Timed out waiting for error-message">
Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
...
</failure>
</testcase>
<testcase name="el login exitoso redirige al dashboard" time="3.2" />
</testsuite>
</testsuites>#### Generar desde Playwright
// playwright.config.ts
reporter: [
['junit', { outputFile: 'test-results/results.xml' }],
['list'], // También mostrar salida en consola
],Reporte HTML
El reporte HTML incorporado de Playwright: visual, con capturas de pantalla y trazas.
reporter: [
['html', { outputFolder: 'playwright-report', open: 'never' }],
],Generar y abrir localmente:
npx playwright show-reportJSON
Formato legible por máquinas para procesamiento personalizado:
reporter: [
['json', { outputFile: 'test-results/results.json' }],
],Útil para construir dashboards personalizados o parsear resultados de manera programática.
Múltiples reporters
Generalmente quieres varios formatos:
reporter: process.env.CI
? [
['junit', { outputFile: 'test-results/results.xml' }],
['html', { outputFolder: 'playwright-report', open: 'never' }],
['list'], // Salida en tiempo real en los logs de CI
]
: [
['html'], // Local: solo el reporte visual
['list'],
],Integración con GitHub Actions
Configuración básica con upload de artefactos
name: Playwright Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- name: Ejecutar tests
run: npx playwright test
env:
BASE_URL: ${{ secrets.STAGING_URL }}
# Siempre subir el reporte, incluso cuando los tests fallan
- name: Subir reporte HTML
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Subir resultados JUnit
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: test-results/results.xmlEl if: always() es crítico: sin él, el paso de upload se saltea cuando los tests fallan.
Anotaciones de test JUnit en GitHub
GitHub Actions puede parsear JUnit XML y anotar tu PR con los fallos directamente en el diff:
- name: Reportar resultados de tests
uses: dorny/test-reporter@v1
if: always()
with:
name: Playwright Tests
path: test-results/results.xml
reporter: java-junit
fail-on-error: trueEsto muestra un resumen en la interfaz de PR de GitHub y anota los tests fallidos en el código.
Integración con GitLab CI
GitLab tiene soporte nativo para JUnit:
test:
image: mcr.microsoft.com/playwright:v1.44.0-jammy
script:
- npm ci
- npx playwright test
artifacts:
when: always
reports:
junit: test-results/results.xml # Soporte JUnit nativo
paths:
- playwright-report/ # También subir el reporte HTML
expire_in: 1 weekGitLab lee el JUnit XML y muestra un resumen de tests en la UI del pipeline, sin herramientas adicionales.
Notificaciones en Slack
Enviar resúmenes de fallos a Slack cuando los tests de CI fallan:
- name: Notificar a Slack en caso de fallo
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ Tests de Playwright fallaron en ${{ github.ref_name }}",
"attachments": [{
"color": "danger",
"fields": [
{
"title": "Rama",
"value": "${{ github.ref_name }}",
"short": true
},
{
"title": "Ejecución",
"value": "${{ github.run_id }}",
"short": true
},
{
"title": "Reporte",
"value": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
]
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}Parseo de resultados de manera programática
A veces necesitas lógica personalizada: notificar solo en fallos nuevos, no en re-fallos:
// scripts/parse-results.js
const fs = require('fs');
const xml2js = require('xml2js');
async function parseResults() {
const xml = fs.readFileSync('test-results/results.xml', 'utf8');
const result = await xml2js.parseStringPromise(xml);
const testsuites = result.testsuites.testsuite;
const failures = [];
testsuites.forEach(suite => {
(suite.testcase || []).forEach(testcase => {
if (testcase.failure) {
failures.push({
name: testcase.$.name,
suite: suite.$.name,
message: testcase.failure[0].$.message,
time: testcase.$.time,
});
}
});
});
console.log(`Total de fallos: ${failures.length}`);
failures.forEach(f => {
console.log(`- [${f.suite}] ${f.name}`);
console.log(` ${f.message}`);
});
process.exit(failures.length > 0 ? 1 : 0);
}
parseResults();Comentario de resumen de tests en PRs
Comentar automáticamente un resumen de tests en cada PR:
- name: Comentario de resumen de tests
uses: marocchino/sticky-pull-request-comment@v2
if: always()
with:
header: test-results
message: |
## Resultados de Tests
${{ steps.test-results.outputs.summary }}
[Ver reporte completo](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})Retención e historial
Los resultados sin procesar solo son útiles si los conservas el tiempo suficiente.
GitHub Actions
- uses: actions/upload-artifact@v4
with:
retention-days: 30 # Conservar por 30 díasPara historial más largo
Algunas opciones: Allure TestOps (pago, conserva historial ilimitado), un bucket S3 o GCS propio donde guardas results.json y generas reportes de tendencias, o Datadog/Grafana para enviar métricas de las ejecuciones de tests a dashboards.
Qué incluir en cada reporte de CI
Un reporte de tests útil responde:
1. ¿Cuántos tests pasaron/fallaron/fueron saltados? Conteos de resumen.
2. ¿Qué tests fallaron? Lista completa de fallos con nombres de tests.
3. ¿Por qué fallaron? Mensajes de error y stack traces.
4. ¿Cómo se ve? Capturas de pantalla para tests de UI.
5. ¿Cuánto tardó? Tiempo de ejecución por test.
6. ¿Es un fallo nuevo? Comparación con la ejecución anterior.
El reporte HTML de Playwright cubre del 1 al 5. Las herramientas de tendencias (Allure, TestOps) añaden el 6.
Resumen
| Formato | Mejor para |
|---------|-----------|
| JUnit XML | Integración con CI, anotaciones en PRs |
| HTML | Lectura humana, capturas de pantalla, trazas |
| JSON | Procesamiento personalizado, dashboards |
| List | Salida en consola en tiempo real |
Configuración mínima para CI
1. Generar JUnit XML (el CI puede parsearlo)
2. Subir el reporte HTML como artefacto
3. Usar if: always() en los pasos de upload
Configuración mejorada
- Reporte Allure con tendencias
- Notificación de Slack en caso de fallo
- Comentario en PR con resumen
- Anotación de tests en la vista del diff