Um passo de upload sem if: always() é ignorado quando os testes falham, deixando você sem relatório HTML e sem screenshots exatamente no momento em que você mais precisa. O relatório de testes no CI tem três camadas: artefatos para inspeção, JUnit XML para parsing nos dashboards, e comentários de PR que mostram as falhas onde os desenvolvedores olham.

O problema com o output bruto de testes

O output padrão do CI para uma execução Playwright com falha parece assim:

  ✘ tests/login.spec.ts > mostra erro para senha errada (15234ms)
    Error: Timed out 5000ms waiting for expect(locator).toBeVisible()

    Call log:
      - expect.toBeVisible with timeout 5000ms
      - waiting for locator('[data-testid="error-message"]')

Você tem que ler centenas de linhas de output para encontrar as falhas. Numa suite de 200 testes, isso é doloroso.

Formatos de relatório

JUnit XML

O formato universal: todo sistema de CI entende.

<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite name="Login Tests" tests="5" failures="1" time="12.5">
    <testcase name="mostra erro para senha errada" time="15.2">
      <failure message="Timed out waiting for error-message">
        Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
        ...
      </failure>
    </testcase>
    <testcase name="login bem-sucedido redireciona para dashboard" time="3.2" />
  </testsuite>
</testsuites>

Gerar no Playwright:

// playwright.config.ts
reporter: [
  ['junit', { outputFile: 'test-results/results.xml' }],
  ['list'],  // Mostrar também no console
],

Relatório HTML

O relatório HTML integrado do Playwright: visual, com screenshots e traces.

reporter: [
  ['html', { outputFolder: 'playwright-report', open: 'never' }],
],

Gerar e abrir localmente:

npx playwright show-report

JSON

Formato legível por máquina para processamento customizado:

reporter: [
  ['json', { outputFile: 'test-results/results.json' }],
],

Útil para construir dashboards customizados ou parsear resultados programaticamente.

Múltiplos reporters

Geralmente você quer vários formatos:

reporter: process.env.CI
  ? [
      ['junit', { outputFile: 'test-results/results.xml' }],
      ['html', { outputFolder: 'playwright-report', open: 'never' }],
      ['list'],  // Output ao vivo nos logs do CI
    ]
  : [
      ['html'],  // Local: apenas o relatório visual
      ['list'],
    ],

Integração com GitHub Actions

Configuração básica com upload de artefatos

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: Rodar testes
        run: npx playwright test
        env:
          BASE_URL: ${{ secrets.STAGING_URL }}
      
      # Sempre fazer upload do relatório, mesmo quando os testes falham
      - name: Upload relatório HTML
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30
      
      - name: Upload resultados JUnit
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: test-results/results.xml

O if: always() é crítico: sem ele, o passo de upload é ignorado quando os testes falham.

Anotações de teste JUnit no GitHub

O GitHub Actions consegue parsear JUnit XML e anotar o seu PR com falhas diretamente no diff:

- name: Reportar resultados dos testes
  uses: dorny/test-reporter@v1
  if: always()
  with:
    name: Playwright Tests
    path: test-results/results.xml
    reporter: java-junit
    fail-on-error: true

Isso mostra um resumo na interface do GitHub PR e anota os testes com falha no código.

Integração com GitLab CI

O GitLab tem suporte nativo a 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  # Suporte nativo a JUnit
    paths:
      - playwright-report/            # Upload do relatório HTML também
    expire_in: 1 week

O GitLab lê o JUnit XML e mostra um resumo de testes na UI do pipeline, sem ferramentas extras.

Notificações no Slack

Envie resumos de falhas para o Slack quando os testes do CI falham:

- name: Notificar Slack em caso de falha
  if: failure()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "❌ Testes Playwright falharam em ${{ github.ref_name }}",
        "attachments": [{
          "color": "danger",
          "fields": [
            {
              "title": "Branch",
              "value": "${{ github.ref_name }}",
              "short": true
            },
            {
              "title": "Execução",
              "value": "${{ github.run_id }}",
              "short": true
            },
            {
              "title": "Relatório",
              "value": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
            }
          ]
        }]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Parsing de resultados programaticamente

Às vezes você precisa de lógica customizada: notificar apenas em novas falhas, não em re-falhas:

// 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 falhas: ${failures.length}`);
  failures.forEach(f => {
    console.log(`- [${f.suite}] ${f.name}`);
    console.log(`  ${f.message}`);
  });
  
  process.exit(failures.length > 0 ? 1 : 0);
}

parseResults();

Comentário de resumo de testes nos PRs

Comente automaticamente um resumo de testes em cada PR:

- name: Comentário de resumo de testes
  uses: marocchino/sticky-pull-request-comment@v2
  if: always()
  with:
    header: test-results
    message: |
      ## Resultados dos Testes
      
      ${{ steps.test-results.outputs.summary }}
      
      [Ver relatório completo](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})

Retenção e histórico

Resultados brutos só são úteis se você os mantém por tempo suficiente.

GitHub Actions:

- uses: actions/upload-artifact@v4
  with:
    retention-days: 30  # Manter por 30 dias

Para histórico mais longo:
  • Allure TestOps: pago, mantém histórico ilimitado
  • Bucket S3/GCS self-hosted: armazene results.json e gere relatórios de tendência
  • Datadog ou Grafana: envie métricas das execuções de teste para dashboards

O que colocar em cada relatório de CI

Um relatório de testes útil responde:

1. Quantos testes passaram, falharam, foram ignorados? Contagens de resumo

2. Quais testes falharam? Lista completa de falhas com nomes dos testes

3. Por que falharam? Mensagens de erro e stack traces

4. Como está a aparência? Screenshots para testes de UI

5. Quanto tempo levou? Tempo de execução por teste

6. É uma nova falha? Comparação com a execução anterior

O relatório HTML do Playwright cobre de 1 a 5. Ferramentas de tendência (Allure, TestOps) adicionam o 6.

Resumo

| Formato | Melhor para |

|---------|-------------|

| JUnit XML | Integração com CI, anotações de PR |

| HTML | Leitura humana, screenshots, traces |

| JSON | Processamento customizado, dashboards |

| List | Output ao vivo no console |

Configuração mínima para CI:

1. Gerar JUnit XML (o CI consegue parsear)

2. Fazer upload do relatório HTML como artefato

3. Usar if: always() nos passos de upload

Configuração aprimorada:
  • Relatório Allure com tendências
  • Notificação no Slack em caso de falha
  • Comentário de PR com resumo
  • Anotação de teste na visualização do diff
→ Veja também: Relatórios Allure para Playwright: Configuração de Relatórios Enriquecidos | Relatórios de Testes Playwright: HTML Integrado vs Allure | CI/CD para QA: GitHub Actions, Jenkins e GitLab Comparados