Skip to content

pr-preview-e2e

pr-preview-e2e #223

name: pr-preview-e2e
# Run the Playwright e2e suite against the PR preview URL once the
# preview deploy finishes. This is where Stage-10's parquet decoder
# (and every other UI feature) gets validated end-to-end on the actual
# bundle the reviewer will load — `pnpm preview` locally can't catch
# CORS, CDN, or Dropbox-fetch issues. Failures land as a failed check
# on the PR with the trace attached as a workflow artifact.
on:
workflow_run:
workflows: ['pr-preview']
types: [completed]
permissions:
contents: read
actions: read
checks: write
pull-requests: write
jobs:
e2e:
# Only run if the preview deploy itself succeeded — otherwise the URL
# is stale and the run is meaningless.
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Resolve PR number
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# The triggering workflow_run carries the head SHA; resolve the
# PR number from it. (`event.workflow_run.pull_requests` is
# often empty for fork PRs; using `gh pr list --search` is the
# robust path for in-repo PRs, which is what `pr-preview.yml`
# restricts to anyway.)
PR=$(gh pr list --repo "${{ github.repository }}" --search "${{ github.event.workflow_run.head_sha }}" --state open --json number --jq '.[0].number')
if [ -z "$PR" ] || [ "$PR" = "null" ]; then
echo "::error::Could not resolve PR number for SHA ${{ github.event.workflow_run.head_sha }}"
exit 1
fi
echo "number=$PR" >> $GITHUB_OUTPUT
echo "Resolved PR #$PR"
- name: Open PR check run
id: check
# workflow_run-triggered runs don't surface as PR checks
# automatically; the GitHub Actions UI only links the in-progress
# job to the workflow_run that fired it. Creating an explicit
# check_run on the PR head SHA gives reviewers a visible status
# entry under the PR's "Checks" tab while the job runs.
uses: actions/github-script@v7
with:
script: |
const { data: cr } = await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'pr-preview-e2e',
head_sha: '${{ github.event.workflow_run.head_sha }}',
status: 'in_progress',
details_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
output: {
title: 'Playwright e2e against PR preview',
summary: `Running the suite against the deployed PR preview URL. [Workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`,
},
});
core.setOutput('id', cr.id);
- name: Checkout PR head
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 1
- name: Set up Node 20 + pnpm
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Enable pnpm
run: npm install --global pnpm@10
- name: Install site deps
working-directory: site
run: pnpm install --frozen-lockfile
- name: Install Playwright browsers
working-directory: site
# webkit is needed for the Stage-24 iphone-webkit regression guard
# (specs/024-fix-ios-safari-load) — iPhone Safari is a WebKit engine
# that the chromium project can't reproduce.
run: pnpm exec playwright install chromium webkit --with-deps
- name: Wait for PR preview to be live
env:
PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
# The pr-preview workflow's gh-pages push can lag behind the
# workflow_run completion event by ~30 s. Poll until the preview
# responds 200 OR until we give up (5 min).
# NOTE: gh-pages serves a CNAME redirect to the custom domain
# (abstractatlas.brainkb.org), so probe the CNAME directly —
# `<owner>.github.io/<repo>/...` 301s to it and pollutes the
# probe output. `curl -L` would also work but the CNAME-direct
# path is what Playwright will actually hit below.
URL="https://abstractatlas.brainkb.org/pr-${PR_NUMBER}/ohbm2026/"
echo "Probing $URL"
for i in $(seq 1 30); do
code=$(curl -s -o /dev/null -w '%{http_code}' "$URL" || true)
if [ "$code" = "200" ]; then
echo "Preview live after ${i} polls."
break
fi
echo " [$i/30] HTTP $code — sleeping 10s"
sleep 10
done
- name: Run Playwright e2e against the preview URL
working-directory: site
env:
# `PLAYWRIGHT_BASE_URL` is the FULL URL of the conference home
# for the PR preview — origin + per-PR prefix + `/ohbm2026/`.
# workflow_run reads THIS file from main (not the PR head), so
# any future change to this env var needs to land via a
# bootstrap PR before the consuming spec refactor (see #25 →
# #24). Specs in the refactored suite resolve `./about/` and
# `./abstract/<id>/` against this baseURL, so the suite hits
# the PR deploy instead of silently falling through to prod.
PLAYWRIGHT_BASE_URL: https://abstractatlas.brainkb.org/pr-${{ steps.pr.outputs.number }}/ohbm2026/
UI_DATA_AVAILABLE: '1'
run: pnpm exec playwright test --project=chromium
- name: Run Stage-24 WebKit/iPhone regression guard against the preview
working-directory: site
# specs/024-fix-ios-safari-load (FR-006): the iPhone-Safari load +
# navigation-crash fix is WebKit-specific, so guard it on the REAL
# WebKit engine. Scoped to the four Stage-24 specs so the existing
# chromium suite is untouched. Runs against the same deployed preview
# (R2 data-host CORS resolves on this origin, not on localhost).
env:
PLAYWRIGHT_BASE_URL: https://abstractatlas.brainkb.org/pr-${{ steps.pr.outputs.number }}/ohbm2026/
UI_DATA_AVAILABLE: '1'
run: pnpm exec playwright test --project=iphone-webkit ios-ohbm-load ios-load-failure ios-navigation ios-siblings
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report-pr-${{ steps.pr.outputs.number }}
path: site/playwright-report/
retention-days: 14
- name: Close PR check run
if: always() && steps.check.outputs.id
uses: actions/github-script@v7
with:
script: |
const conclusion = '${{ job.status }}' === 'success' ? 'success' : 'failure';
await github.rest.checks.update({
owner: context.repo.owner,
repo: context.repo.repo,
check_run_id: ${{ steps.check.outputs.id }},
status: 'completed',
conclusion,
output: {
title: `Playwright e2e: ${conclusion}`,
summary: `[Workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}). On failure the Playwright report is attached as a workflow artifact.`,
},
});