Skip to content

E2E — interactive #429

E2E — interactive

E2E — interactive #429

Workflow file for this run

name: Hub-Client E2E Tests
# Label runs by mode in the Actions UI. `run-name` is evaluated at workflow
# top level with only the `github`/`inputs` contexts available, so it branches
# purely on the trigger. Order matters — first truthy arm wins:
# - recreate-all-snapshots dispatch → baseline regeneration
# - schedule → nightly (interactive + smoke-all)
# - run-smoke-all dispatch → on-demand interactive + smoke-all
# - everything else (push/PR) → the fast interactive gate
run-name: >-
${{
inputs.recreate-all-snapshots && 'E2E — recreate baselines'
|| github.event_name == 'schedule' && 'E2E — interactive + smoke-all (nightly)'
|| inputs.run-smoke-all && 'E2E — interactive + smoke-all'
|| 'E2E — interactive'
}}
on:
workflow_dispatch:
inputs:
recreate-all-snapshots:
description: 'Delete and recreate ALL visual regression baselines'
type: boolean
default: false
run-smoke-all:
description: 'Also run smoke-all E2E tests (slow, ~80 extra tests, opt-in only)'
type: boolean
default: false
schedule:
- cron: '0 7 * * *' # 2am EST / 3am EDT, runs on main
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
e2e-tests:
runs-on: ubuntu-latest
name: Hub-Client E2E Tests
if: github.repository == 'quarto-dev/q2'
# Needed for the "Commit new baselines" step to push back to the
# workflow branch. Default GITHUB_TOKEN is read-only.
permissions:
contents: write
steps:
- name: Checkout Repo
uses: actions/checkout@v6
# Fix mtimes for build caching
- name: Restore file modification times
shell: bash
run: |
git ls-files | while read file; do
time=$(git log -1 --format='@%ct' -- "$file" 2>/dev/null || echo '@0')
[ "$time" != "@0" ] && touch -d "$time" "$file" 2>/dev/null || true
done
# Rust toolchain for WASM build. Use the named `@nightly` ref
# (not `@master`, which hard-errors without an explicit `toolchain:`
# input) — it installs the nightly toolchain; cargo then reads the
# pinned dated nightly from rust-toolchain.toml (bd-at72) at build
# time. Matches the pattern Carlos established for ts-test-suite.yml
# on main.
- name: Set up Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: wasm32-unknown-unknown
components: rust-src
- name: Set up Clang
uses: egor-tensin/setup-clang@v2
with:
version: latest
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Install wasm-pack
run: cargo install wasm-pack
# build-wasm.js verifies wasm-bindgen CLI matches the version
# pinned in Cargo.lock; install that exact version.
- name: Install wasm-bindgen-cli
run: |
VERSION=$(awk '/^name = "wasm-bindgen"$/{getline; gsub(/version = |"/, ""); print; exit}' Cargo.lock)
echo "Installing wasm-bindgen-cli $VERSION"
cargo install -f wasm-bindgen-cli --version "$VERSION"
# tree-sitter for grammar builds
- name: Set up tree-sitter CLI
run: |
curl -LO https://github.com/tree-sitter/tree-sitter/releases/download/v0.25.8/tree-sitter-linux-x86.gz
gunzip tree-sitter-linux-x86.gz
chmod +x tree-sitter-linux-x86
sudo mv tree-sitter-linux-x86 /usr/local/bin/tree-sitter
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- name: Install npm dependencies
run: npm ci
# WASM artifacts must exist before any package's `vite build` runs
# (hub-client and q2-demos/* all import the WASM module).
- name: Build WASM
run: |
cd hub-client
npm run build:wasm
# Build only the workspaces the e2e tests actually exercise.
# q2-demos/* currently fail their vite build (they import an
# absolute path "/src/wasm-js-bridge/cache.js" that only exists
# in hub-client/), and trace-viewer is not under test here.
#
# VITE_E2E=1 on the hub-client build pulls in src/test-hooks.ts so
# the Playwright suite can reach internal services via
# `window.__quartoTest` (production bundles don't expose source
# paths the way `vite dev` did). The flag is consumed only by the
# `if (import.meta.env.VITE_E2E === '1')` branch in src/main.tsx;
# production-user builds leave it unset and tree-shake the hooks out.
- name: Build TypeScript packages (ts-packages + hub-client)
env:
VITE_E2E: '1'
run: |
for pkg in ts-packages/*/; do
if [ -f "$pkg/package.json" ]; then
npm run build --workspace "$pkg" --if-present
fi
done
npm run build --workspace hub-client --if-present
# globalSetup launches the hub via `cargo run --bin hub` with a 120s
# readiness timeout. On a cold CI runner the compile alone exceeds
# that, so pre-build it here.
- name: Pre-build hub binary
run: cargo build --bin hub
# Install Playwright browsers
- name: Install Playwright
run: |
cd hub-client
npx playwright install --with-deps chromium
# Delete all snapshots if recreating from scratch
- name: Delete all snapshots (recreate mode)
if: inputs.recreate-all-snapshots == true
run: find hub-client/e2e -type d -name '*-snapshots' -exec rm -rf {} + || true
# Run E2E tests — custom specs only (smoke-all excluded by testIgnore)
- name: Run E2E tests
run: |
cd hub-client
npx playwright test
# Smoke-all: full render pipeline matrix (~80 tests). Runs nightly
# (schedule trigger) or on demand (workflow_dispatch with run-smoke-all=true).
# Skipped for every push/PR. Use this to verify render correctness after
# changes to the WASM pipeline or theme resolution.
- name: Run smoke-all E2E tests (nightly + opt-in)
if: inputs.run-smoke-all == true || github.event_name == 'schedule'
run: |
cd hub-client
npx playwright test --config playwright.smoke-all.config.ts
# Run visual regression tests
- name: Run visual tests
id: visual
continue-on-error: true
run: |
cd hub-client
npx playwright test --config playwright.visual.config.ts
# If visual tests failed, retry with --update-snapshots=missing
# This only creates baselines for NEW tests; existing mismatches still fail
- name: Retry visual tests (add missing baselines)
id: visual-retry
if: steps.visual.outcome == 'failure'
run: |
cd hub-client
npx playwright test --config playwright.visual.config.ts --update-snapshots=missing
# If retry passed (only missing baselines), commit them back.
# Use `find` instead of a `**` glob — bash globstar isn't enabled
# by default and visual specs sit directly under hub-client/e2e/,
# not one directory deeper.
- name: Commit new baselines
if: steps.visual.outcome == 'failure' && steps.visual-retry.outcome == 'success'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
find hub-client/e2e -type d -name '*-snapshots' -exec git add -f {} +
git diff --cached --quiet || git commit -m "Add missing Playwright visual regression baselines"
git push
# Fail the workflow if visual retry also failed (real regression)
- name: Fail on visual regression
if: steps.visual-retry.outcome == 'failure'
run: exit 1
# Upload test artifacts on failure
- name: Upload Playwright report
uses: actions/upload-artifact@v7
if: failure()
with:
name: playwright-report
path: hub-client/playwright-report/
retention-days: 7