Ir al contenido

Reporter de Playwright en proveedores de CI

El reporter se ejecuta dentro de npx playwright test: pasa el token a ese paso, y cada prueba se envía mientras se ejecuta. No hace falta un paso de envío aparte. En todos los proveedores:

  • Guarda un token de aplicación de la tarjeta Playwright en Workspace › Integraciones (primeros pasos) como un secreto llamado PROBARA_API_TOKEN. El ID del Proyecto no es un secreto: ponlo en playwright.config (projectId) o en el pipeline (PROBARA_PROJECT).
  • Usa Node.js 22.12 o posterior, como el resto de tu ejecución de Playwright.
  • Las pruebas que fallan hacen fallar el paso; los problemas de envío, no. El código de salida de Playwright sigue siendo el de las pruebas.
  • Fija la versión del reporter en package.json (o en el lockfile), para que una versión nueva nunca cambie un pipeline por sorpresa. La CLI no es una dependencia del reporter: los pasos de abajo que la usan fijan su versión en el propio comando (npx --yes @probara/cli@0.2). Con @probara/cli en tus devDependencies, npx @probara/cli después de npm ci ejecuta la versión del lockfile.
  • Mantén el secreto lejos del código en el que no confías. Un build de un pull request desde un fork ejecuta el código de ese fork: cada sección explica cómo el proveedor mantiene el token fuera de su alcance. Sin el token, el reporter no envía nada (si el proyecto está definido, registra que falta el token), y las pruebas se ejecutan como siempre. La CLI termina con código 2 sin token, así que las recetas desactivan el envío (PROBARA_ENABLED=false) donde la ejecutaría el build de un fork: entonces termina con código 0.

Para una sola ejecución con varios shards, consulta shards.

En el repositorio (o en la organización): Settings → Secrets and variables → Actions → New repository secret, con el nombre PROBARA_API_TOKEN.

name: Playwright
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm ci
- run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
env:
PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
PROBARA_PROJECT: SHOP

Los shards escriben informes blob de Playwright, y un job los combina con el reporter: todo el pipeline se convierte en una ejecución, con los intentos y los adjuntos incluidos. Registra blob en CI y el reporter en el resto de entornos:

playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: process.env.CI ? 'blob' : [['list'], ['@probara/playwright-reporter']],
});
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1
report:
needs: test
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm ci
- uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- run: npx playwright merge-reports --reporter @probara/playwright-reporter ./all-blob-reports
env:
PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
PROBARA_PROJECT: SHOP

Jobs en paralelo: una ejecución, cerrada por el job final

Sección titulada «Jobs en paralelo: una ejecución, cerrada por el job final»

En GitHub Actions los jobs de un mismo workflow envían a una misma ejecución: el origen de la ejecución identifica la ejecución del workflow por su repositorio, su id y su intento, y el primer job que envía crea la ejecución. Por defecto, cada job cierra su ejecución al terminar, así que los jobs en paralelo definen PROBARA_CLOSE_RUN: false. Un job final cierra la ejecución con probara run close, que no necesita aquí el ULID: needs hace que espere a todos los jobs de pruebas, y if: always() lo ejecuta aunque fallen.

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shard }}/2
env:
PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
PROBARA_PROJECT: SHOP
# A shard leaves the run open for the others.
PROBARA_CLOSE_RUN: false
probara:
needs: test
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npx --yes @probara/cli@0.6 run close
env:
PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
PROBARA_PROJECT: SHOP
  • Un job tras el cierre recibe 409 ci_run_closed: el reporter registra el motivo, no envía nada más, y las pruebas conservan su código de salida (solución de problemas). Mantén cada job que envía en needs.
  • Una nueva ejecución del workflow (re-run) es otro intento: envía a una ejecución propia.
  • Un solo job no necesita nada de esto: su ejecución se cierra al terminar, como en cualquier otro caso.
CampoDesde
Nombre de la ejecuciónGITHUB_WORKFLOW #GITHUB_RUN_NUMBER, por ejemplo Playwright #42
RamaGITHUB_HEAD_REF (la rama del pull request), si no GITHUB_REF_NAME; ninguna para una etiqueta
CommitGITHUB_SHA
URL del buildGITHUB_SERVER_URL/GITHUB_REPOSITORY/actions/runs/GITHUB_RUN_ID
RepositorioGITHUB_REPOSITORY
Autor del commitGITHUB_ACTOR
Pull request<n> de GITHUB_REF refs/pull/<n>/merge
WorkflowGITHUB_WORKFLOW
Ejecución de CIGITHUB_RUN_ID, intento GITHUB_RUN_ATTEMPT
Job de CIGITHUB_JOB

En los eventos pull_request, GITHUB_SHA es el commit de merge que crea GitHub, no el último commit de la rama. Para registrar este último, añade PROBARA_COMMIT: ${{ github.event.pull_request.head.sha }} al env del paso. En GitHub Actions el commit debe ser un SHA hexadecimal de 7 a 40 caracteres: otro valor se descarta con una advertencia.

Los workflows lanzados por un pull request desde un fork reciben los secretos vacíos, así que los builds de forks ejecutan sus pruebas y no envían nada, sin ningún ajuste extra. No uses pull_request_target para llevar el secreto a un build de un fork: ejecuta el código del fork con tus secretos.

Settings → CI/CD → Variables → Add variable: clave PROBARA_API_TOKEN, el token como valor, con Mask variable activado. Protect variable la limita a las ramas y etiquetas protegidas: actívalo solo si envías resultados únicamente desde ellas.

Un .gitlab-ci.yml con la imagen de Playwright:

stages:
- test
playwright:
stage: test
image: mcr.microsoft.com/playwright:v1.56.1-noble
variables:
PROBARA_PROJECT: SHOP
script:
- npm ci
- npx playwright test
artifacts:
when: always
paths:
- test-results/
  • Haz coincidir la versión de Playwright de la imagen con la de tu package.json.
  • Un job sin la variable (el pipeline de un fork, una rama no protegida con una variable protegida) ejecuta sus pruebas y no envía nada.

Los shards de parallel: escriben informes blob, y un job los combina con el reporter en una sola ejecución. Registra blob en CI y el reporter en el resto de entornos, como en shards:

stages:
- test
- report
playwright:
stage: test
image: mcr.microsoft.com/playwright:v1.56.1-noble
parallel: 4
script:
- npm ci
- npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
artifacts:
when: always
paths:
- blob-report/
report-to-probara:
stage: report
image: mcr.microsoft.com/playwright:v1.56.1-noble
needs:
- job: playwright
artifacts: true
variables:
PROBARA_PROJECT: SHOP
rules:
# After failed shards too, and only where the token is set.
- if: $PROBARA_API_TOKEN
when: always
script:
- npm ci
- npx playwright merge-reports --reporter @probara/playwright-reporter ./blob-report

Un needs: sobre un job parallel: descarga los artefactos de todas las instancias en la misma carpeta; el informe blob de cada shard tiene su propio nombre de archivo (report-1.zip…), así que quedan uno junto a otro.

CampoDesde
Nombre de la ejecuciónCI_PROJECT_NAME #CI_PIPELINE_IID, por ejemplo shop #42
RamaCI_MERGE_REQUEST_SOURCE_BRANCH_NAME en un pipeline de merge request, si no CI_COMMIT_REF_NAME; ninguna en un pipeline de etiqueta
CommitCI_COMMIT_SHA
URL del buildCI_PIPELINE_URL

Un merge request desde un fork ejecuta su pipeline en el proyecto del fork, que no tiene tus variables de CI/CD: allí el reporter queda desactivado. Si los maintainers ejecutan pipelines de forks en el proyecto principal, esos pipelines sí reciben la variable, así que revisa el cambio antes de ejecutarlo.

Manage Jenkins → Credentials → (un dominio) → Add Credentials: tipo Secret text, ID probara-api-token, y el token como secreto.

Un Jenkinsfile declarativo con Node.js 22 en el agente vincula la credencial a la etapa de pruebas con withCredentials:

pipeline {
agent any
environment {
PROBARA_PROJECT = 'SHOP'
}
stages {
stage('Test') {
steps {
sh 'npm ci'
sh 'npx playwright install --with-deps'
withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
sh 'npx playwright test'
}
}
}
}
}

Jenkins oculta el secreto vinculado en el log del build. Para etapas en paralelo que comparten una ejecución, créala en una primera etapa y pasa PROBARA_RUN_ULID a cada una (ejecución compartida).

Con JENKINS_URL definida, la ejecución se llama JOB_NAME #BUILD_NUMBER, y su rama, su commit y la URL del build salen de BRANCH_NAME (o GIT_BRANCH), GIT_COMMIT y BUILD_URL.

Jenkins no oculta por sí mismo las credenciales a los builds de pull requests: un job multibranch que construye forks puede entregar el token a su código.

  1. Configura Discover pull requests from forks → Trust para que un pull request de cualquier otra persona se construya con el Jenkinsfile de su rama de destino: en el branch source de GitHub, From users with Admin or Write permission (o Nobody); en el de Bitbucket, Nobody, o Forks in the same account desde la versión 871 del plugin (la corrección de CVE-2024-28152). Everyone construye un fork con su propio Jenkinsfile, que puede vincular la credencial por su cuenta, y deja inútil la protección de abajo.
  2. En el Jenkinsfile de la rama de destino, vincula la credencial solo cuando env.CHANGE_FORK no está definida (Jenkins la define para un pull request desde un fork), y desactiva el envío en un build de un fork:
pipeline {
agent any
environment {
PROBARA_PROJECT = 'SHOP'
}
stages {
stage('Test') {
steps {
sh 'npm ci'
sh 'npx playwright install --with-deps'
script {
if (env.CHANGE_FORK) {
withEnv(['PROBARA_ENABLED=false']) {
sh 'npx playwright test'
}
} else {
withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
sh 'npx playwright test'
}
}
}
}
}
}
}

Para etapas en paralelo que comparten una ejecución, envuelve cada withCredentials del mismo modo: con el envío desactivado, run create no escribe ningún ULID y run close termina con código 0 sin token.

Organization Settings → Contexts → Create Context (por ejemplo probara-reporting), y añade la variable de entorno PROBARA_API_TOKEN. Una variable de entorno del proyecto (Project Settings → Environment Variables) también sirve; en ese caso quita context: de abajo.

Un .circleci/config.yml con la imagen de Playwright, dividido en shards con parallelism en una sola ejecución creada de antemano (ejecución compartida); sin parallelism, deja solo los pasos de prueba:

version: 2.1
jobs:
create-run:
docker:
- image: mcr.microsoft.com/playwright:v1.56.1-noble
environment:
PROBARA_PROJECT: SHOP
steps:
- run: |
# Without the token (a fork's build), turn reporting off: run create prints nothing.
[ -n "$PROBARA_API_TOKEN" ] || export PROBARA_ENABLED=false
PROBARA_RUN_ULID=$(npx --yes @probara/cli@0.2 run create)
echo "PROBARA_RUN_ULID=$PROBARA_RUN_ULID" > probara.env
- persist_to_workspace:
root: .
paths: [probara.env]
test:
docker:
- image: mcr.microsoft.com/playwright:v1.56.1-noble
parallelism: 4
environment:
PROBARA_PROJECT: SHOP
steps:
- checkout
- attach_workspace:
at: .
- run: npm ci
- run: |
export $(cat probara.env)
npx playwright test --shard=$((CIRCLE_NODE_INDEX + 1))/$CIRCLE_NODE_TOTAL
close-run:
docker:
- image: mcr.microsoft.com/playwright:v1.56.1-noble
environment:
PROBARA_PROJECT: SHOP
steps:
- attach_workspace:
at: .
- run:
when: always
command: |
export $(cat probara.env)
[ -n "$PROBARA_API_TOKEN" ] || export PROBARA_ENABLED=false
npx --yes @probara/cli@0.2 run close
workflows:
playwright:
jobs:
- create-run:
context: probara-reporting
- test:
context: probara-reporting
requires: [create-run]
- close-run:
context: probara-reporting
requires:
- test: [success, failed]
  • CIRCLE_NODE_INDEX empieza en 0; los shards de Playwright, en 1.
  • Con CIRCLECI=true, la ejecución se llama CIRCLE_PROJECT_REPONAME #CIRCLE_BUILD_NUM, con la rama CIRCLE_BRANCH, el commit CIRCLE_SHA1 y la URL del build CIRCLE_BUILD_URL.

CircleCI no pasa los contextos ni las variables del proyecto a los builds de pull requests desde forks salvo que Pass secrets to builds from forked pull requests esté activado: déjalo desactivado. Los jobs de un fork que usan un contexto no llegan a ejecutarse (CircleCI muestra un error de autorización). Con variables del proyecto se ejecutan sin el token: el reporter no envía nada, y la línea PROBARA_ENABLED=false de create-run y close-run hace que run create no imprima nada y que ambos terminen con código 0.

Pipelines → tu pipeline → Edit → Variables → New variable: nombre PROBARA_API_TOKEN, el token como valor, con Keep this value secret activado. Un grupo de variables de la biblioteca también sirve. Las variables secretas nunca llegan solas a los scripts: pásalas al env de cada paso, como abajo.

Un azure-pipelines.yml, dividido en shards con parallel en una sola ejecución creada por un primer job (ejecución compartida); sin shards, deja solo variables y el job de pruebas, sin strategy ni PROBARA_RUN_ULID:

variables:
# Off in a fork's build, which gets no secrets (see forks below).
PROBARA_ENABLED: $[ ne(variables['System.PullRequest.IsFork'], 'True') ]
jobs:
- job: create_run
pool:
vmImage: ubuntu-latest
steps:
- task: NodeTool@0
inputs:
versionSpec: '22.x'
- script: |
set -e
ulid=$(npx --yes @probara/cli@0.2 run create)
echo "##vso[task.setvariable variable=ulid;isOutput=true]$ulid"
name: probara
env:
PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
PROBARA_PROJECT: SHOP
- job: test
dependsOn: create_run
strategy:
parallel: 4
variables:
PROBARA_RUN_ULID: $[ dependencies.create_run.outputs['probara.ulid'] ]
pool:
vmImage: ubuntu-latest
steps:
- task: NodeTool@0
inputs:
versionSpec: '22.x'
- script: npm ci
- script: npx playwright install --with-deps
- script: npx playwright test --shard=$(System.JobPositionInPhase)/$(System.TotalJobsInPhase)
env:
PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
PROBARA_PROJECT: SHOP
PROBARA_RUN_ULID: $(PROBARA_RUN_ULID)
- job: close_run
dependsOn: [create_run, test]
condition: always()
variables:
PROBARA_RUN_ULID: $[ dependencies.create_run.outputs['probara.ulid'] ]
pool:
vmImage: ubuntu-latest
steps:
- task: NodeTool@0
inputs:
versionSpec: '22.x'
- script: npx --yes @probara/cli@0.2 run close
env:
PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
PROBARA_PROJECT: SHOP
PROBARA_RUN_ULID: $(PROBARA_RUN_ULID)
  • set -e hace que un run create fallido haga fallar el primer job: Azure solo comprueba el último comando de un script, y de lo contrario cada shard crearía su propia ejecución.
  • Con TF_BUILD=True, la ejecución se llama BUILD_DEFINITIONNAME #BUILD_BUILDNUMBER, y su rama, su commit y la URL del build salen de las variables del pipeline.

Los builds de pull requests desde forks no reciben variables secretas salvo que Make secrets available to builds of forks esté activado: déjalo desactivado. Azure deja tal cual una macro que no puede resolver, así que en el build de un fork $(PROBARA_API_TOKEN) llega al paso como ese texto literal, un token que Probara rechaza con 401: run create haría fallar el primer job, y el reporter registraría el 401 en cada ejecución (el código de salida sigue siendo el de las pruebas). La variable PROBARA_ENABLED del pipeline vale False cuando System.PullRequest.IsFork es True, así que el build de un fork ejecuta sus pruebas con el envío desactivado, y run create y run close terminan con código 0.

Repository settings → Pipelines → Repository variables: nombre PROBARA_API_TOKEN, el token como valor, con Secured activado. Bitbucket pasa las variables del repositorio a cada paso, así que el reporter la encuentra en npx playwright test.

Un bitbucket-pipelines.yml con la imagen de Playwright, con dos shards en paralelo que combinan sus informes blob en una sola ejecución (combinar los informes blob); sin shards, deja un solo paso que ejecute npx playwright test:

image: mcr.microsoft.com/playwright:v1.56.1-noble
pipelines:
default:
- parallel:
- step:
name: Shard 1
script:
- npm ci
- npx playwright test --shard=1/2 || touch shard-1.failed
artifacts:
- blob-report/**
- shard-*.failed
- step:
name: Shard 2
script:
- npm ci
- npx playwright test --shard=2/2 || touch shard-2.failed
artifacts:
- blob-report/**
- shard-*.failed
- step:
name: Report to Probara
script:
- npm ci
- export PROBARA_PROJECT=SHOP
- npx playwright merge-reports --reporter @probara/playwright-reporter ./blob-report
- if ls shard-*.failed >/dev/null 2>&1; then echo "Tests failed in a shard" >&2; exit 1; fi

Bitbucket no ejecuta ningún paso después de uno fallido, así que un shard que hiciera fallar su paso impediría que el paso de envío mandara nada. En su lugar, cada shard deja constancia de las pruebas fallidas en un artefacto shard-<n>.failed, y el paso de envío manda todos los resultados y luego hace fallar el pipeline si algún shard dejó uno: los shards aparecen como correctos, y el fallo lo muestra el paso de envío.

Con BITBUCKET_BUILD_NUMBER definida, la ejecución se llama BITBUCKET_REPO_SLUG #BITBUCKET_BUILD_NUMBER, con la rama BITBUCKET_BRANCH, el commit BITBUCKET_COMMIT y un enlace al pipeline.

Los pull requests desde forks no reciben variables seguras: el reporter no envía nada allí.

Expón PROBARA_API_TOKEN a los pasos desde un almacén de secretos: un hook environment del agente que lo lea de tu gestor de secretos, o los secretos de Buildkite.

Un pipeline.yml cuyos jobs en paralelo envían a una sola ejecución creada por un primer paso (ejecución compartida):

env:
PROBARA_PROJECT: SHOP
steps:
- label: Create the Probara run
key: create-run
command: |
PROBARA_RUN_ULID="$(npx --yes @probara/cli@0.2 run create)"
if [ -n "$$PROBARA_RUN_ULID" ]; then buildkite-agent meta-data set probara-run "$$PROBARA_RUN_ULID"; fi
- label: Playwright
depends_on: create-run
parallelism: 4
command: |
npm ci
export PROBARA_RUN_ULID="$(buildkite-agent meta-data get probara-run --default '')"
npx playwright test --shard=$((BUILDKITE_PARALLEL_JOB + 1))/$$BUILDKITE_PARALLEL_JOB_COUNT
- wait: ~
continue_on_failure: true
- label: Close the Probara run
command: PROBARA_RUN_ULID="$(buildkite-agent meta-data get probara-run --default '')" npx --yes @probara/cli@0.2 run close
  • buildkite-agent pipeline upload sustituye $NAME y ${NAME} al subir el pipeline, y una variable que todavía no conoce, como BUILDKITE_PARALLEL_JOB_COUNT, que solo reciben los jobs en paralelo, queda vacía. Un $$ se deja como un solo $ para el shell del job, mientras que $(...) y $((...)) se dejan tal cual.
  • La ejecución se crea en una línea propia, así que un run create fallido hace fallar el primer paso en lugar de guardar un ULID vacío. Con el envío desactivado (un build de un fork en un agente que define PROBARA_ENABLED=false, como se explica abajo), run create no imprime ningún ULID y termina con código 0: no se guarda nada, meta-data get --default '' da a los demás pasos un ULID vacío en lugar de un error, y se ejecutan también con el envío desactivado.
  • BUILDKITE_PARALLEL_JOB empieza en 0; los shards de Playwright, en 1.
  • Con BUILDKITE=true, la ejecución se llama BUILDKITE_PIPELINE_SLUG #BUILDKITE_BUILD_NUMBER, con la rama BUILDKITE_BRANCH, el commit BUILDKITE_COMMIT y la URL del build BUILDKITE_BUILD_URL.

Buildkite construye pull requests desde forks solo si el pipeline lo permite (desactivado por defecto). Esos builds se ejecutan en tus agentes, y pipeline upload lee el pipeline.yml del propio fork, que también elige la cola de los agentes. El código del fork se ejecuta en el shell del job con el usuario del agente, así que en un agente que puede leer el token de tu almacén de secretos, el código del fork también puede leerlo: ningún hook ni paso lo impide.

Mantén desactivados los builds de forks, o mantenlos lejos de esos agentes: su hook environment rechaza un build de un fork (BUILDKITE_PULL_REQUEST_REPO lo nombra), y los forks solo se construyen en una cola o un clúster aparte cuyos agentes no pueden leer el almacén de secretos:

Ventana de terminal
# The environment hook of the agents that can read the token: no build of a fork runs there.
if [ -n "$BUILDKITE_PULL_REQUEST_REPO" ]; then
case "${BUILDKITE_PULL_REQUEST_REPO#*://}" in
github.com/acme/shop.git) ;;
*)
echo "Refusing a build of a fork: $BUILDKITE_PULL_REQUEST_REPO" >&2
exit 1
;;
esac
fi
export PROBARA_API_TOKEN="$(your-secrets-manager get probara-api-token)"

BUILDKITE_PULL_REQUEST_REPO está vacía fuera de los pull requests, y es una URL https:// desde 2022 (antes, git://), así que el hook la compara sin el esquema: pon tu repositorio en lugar de github.com/acme/shop.git. Si no coincide, tus propios pull requests también fallan en estos agentes, algo que el primero pondrá en evidencia. En los agentes que construyen forks, define PROBARA_ENABLED=false en su hook environment: las pruebas del fork se ejecutan, npx playwright test no envía nada, y run create y run close terminan con código 0 sin token.

Siguiente: solución de problemas, o conserva lo que un informe fallido no pudo enviar con un archivo de resultados.