E2E reports
Send each repository's end-to-end test results to PRLane from GitHub Actions. See the latest run on every pull request and the tests that keep failing or flaking.
Send E2E results from GitHub Actions
Works with any repository that runs Playwright in GitHub Actions and writes a JSON report. No secret or token to manage.
Set it up with a coding agent
Paste this into Claude Code, Cursor or another coding agent working in your repository. It covers every step below.
Set up this repository to send its Playwright end-to-end test results to PRLane from GitHub Actions. Do every step below, then show me the changes (open a pull request if you can).
Before you start, ask me for my PRLane organization alias and replace <your-org-alias> below with it.
1. Add the sender script. First search the repository for an existing copy of send-playwright-report.mjs. If there is one, use it instead of adding a second copy, and change the path in step 5 to point at it. Otherwise, from the repository root, run this and commit the downloaded file unchanged (it is one dependency-free file that needs Node 20 or later on the runner):
curl -fsSL https://prlane.com/e2e-reports/send-playwright-report.mjs -o scripts/send-playwright-report.mjs
2. Find the GitHub Actions job that runs Playwright. If the tests are sharded across several jobs, use the job that merges the shard reports, or add one that downloads the shards' blob reports and runs `npx playwright merge-reports`. Results must come from one combined report.
3. Make sure that job writes a Playwright JSON report to test-results.json. Add the JSON reporter next to the existing reporters (for example `["json", { outputFile: "test-results.json" }]` in playwright.config) or, when merging shards, run `npx playwright merge-reports --reporter json <blob-report-dir> > test-results.json`. Keep the existing reporters working. A relative `outputFile` resolves against the folder that holds the Playwright config, not the repository root: if the config lives in a subfolder such as e2e/, the report lands there, so point `--report` in step 5 at that path (for example e2e/test-results.json), or anchor `outputFile` to the repository root. Check where the file actually ends up and make `--report` match it.
4. Give that job permission to request a GitHub Actions OIDC identity token. Add these job-level permissions, merging them with any the job already has (a job-level block replaces the workflow-level one, so keep anything else the job needs):
permissions:
contents: read
id-token: write
5. Add this step to the same job, after the tests (and after the merge, for sharded runs):
- name: Send E2E results to PRLane
if: ${{ !cancelled() }}
continue-on-error: true
timeout-minutes: 3
run: >-
node scripts/send-playwright-report.mjs
--report test-results.json
--org <your-org-alias>
Keep `if: ${{ !cancelled() }}` so failed test runs still report, and `continue-on-error: true` so the step never fails the workflow. If more than one job or workflow sends results for the same pull request, give each one its own `--suite <name>` (for example the workflow or job name). PRLane shows the newest run per suite, so sends without distinct suites replace each other.
Do not add any secret or token: PRLane authenticates the step with the job's OIDC token. When you're done, remind me that the repository must be connected to the PRLane organization, or its results are refused.Or set it up yourself
Connect the repository
The repository must be connected to your PRLane organization. Results from any other repository are refused.
Add the sender script to the repository
One dependency-free file that runs on Node 20 or later. Download it or run this once, then commit it:
Download commandcurl -fsSL https://prlane.com/e2e-reports/send-playwright-report.mjs -o scripts/send-playwright-report.mjsLet the job request a GitHub Actions identity token
Add this to the job that has the Playwright JSON report (merge sharded reports first). It grants no other access.
Job permissionspermissions: contents: read id-token: writeAdd the step after your tests
Workflow step- name: Send E2E results to PRLane if: ${{ !cancelled() }} continue-on-error: true timeout-minutes: 3 run: >- node scripts/send-playwright-report.mjs --report test-results.json --org <your-org-alias>Optional:
--suite namefor each job or workflow when several send results for the same pull request,--report-url https://…to link an HTML report you host, and--pr 123for events whose ref is not a pull request.
How it authenticates
The step asks GitHub for a short-lived OIDC token for the audience below. PRLane checks GitHub's signature, that the token names a repository connected to the org, and that the token is used only once. The run, commit and branch come from the token, not from the report.
prlane-e2e-reports:<your-org-alias>Results are informational and never block a merge: keep merge gates on GitHub checks.
API reference
Any test framework can send results with one request. The bundled script does exactly this for Playwright.
POST /api/e2e-reports/<org alias>/runsAuthorization: Bearer <GitHub Actions OIDC token>for the audienceprlane-e2e-reports:<org alias>, at most 10 minutes old, used once.- JSON body, optionally
Content-Encoding: gzip: at most 4 MB sent and 32 MB unpacked. - Repository, run, attempt, commit, branch and workflow come from the token. Sending the same run, attempt and suite again replaces its results.
Payload
{
"version": 1,
"suite": "full",
"framework": "playwright",
"reportUrl": "https://example.com/report/index.html",
"headSha": "<40-hex PR head commit>",
"startedAt": "2026-10-24T07:00:00.000Z",
"durationMs": 371000,
"tests": [
{
"file": "e2e/auth/login.spec.ts",
"titlePath": ["Login"],
"title": "signs in",
"project": "chromium",
"outcome": "flaky",
"durationMs": 5120,
"retries": 1,
"error": "Timed out 5000ms waiting for …"
}
]
}outcome is the result after retries: passed, failed, flaky (passed on a retry) or skipped. Send setup or teardown errors as failed tests so the run never shows as passed. headSha is the pull request commit that was tested; a pull_request run's token names GitHub's merge commit instead.
Errors
| Code | What to do |
|---|---|
e2e_reports_disabled | Receiving results is switched off on this deployment. |
org_not_found | No org with that alias: check --org and --base-url. |
missing_token | No bearer token: give the job id-token: write. |
bad_audience | The token's audience is not prlane-e2e-reports:<org>. |
repository_not_connected | Connect the repository to the org. |
token_replayed | Each token is single use; mint a new one to retry. |
token_expired | Re-run the job; check the runner's clock. |
body_too_large | Over 4 MB on the wire; gzip it or split suites. |
invalid_body | The JSON does not match the payload below. |