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-report

JSON

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.xml

El 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: true

Esto 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 week

GitLab 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ías

Para 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
→ See also: Informes Allure para Playwright: Configuración de Reportes Enriquecidos | Informes de Tests en Playwright: HTML Integrado vs Allure | CI/CD para QA: GitHub Actions, Jenkins y GitLab Comparados