Skip to content

fix(ci): add Cypress E2E workflow #32

fix(ci): add Cypress E2E workflow

fix(ci): add Cypress E2E workflow #32

Workflow file for this run

# Cypress E2E Tests
#
# Single job, three parallel workers (background processes).
# Mirrors `just cypress-parallel` locally — same split, same runner.
#
# Workers:
# worker-1 — general UI specs
# worker-2 — euclid rule-builder specs
# worker-3 — API specs
#
# Caching layers:
# - Docker BuildKit layers → GHA cache, scoped per branch.
# load: true puts the image directly into the local Docker daemon —
# no docker save / tar artifact / docker load round-trip needed.
# - npm global cache (~/.npm) → actions/setup-node, keyed on both lock files.
# - Cypress binary (~/.cache/Cypress, ~200 MB) → actions/cache, separate from ~/.npm.
name: Cypress E2E
on:
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
# Required for GHA cache writes (cache-to: type=gha) and artifact uploads.
actions: write
jobs:
cypress:
name: Cypress E2E
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
# ── Build Decision Engine ─────────────────────────────────────────────
# BuildKit GHA cache means only changed layers are rebuilt.
# load: true puts the image straight into the local Docker daemon —
# no tar.gz save/upload/download/load steps needed.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Decision Engine
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.postgres
tags: decision-engine-pg:local
load: true
build-args: BUILD_MODE=debug
cache-from: |
type=gha,scope=cypress-debug-${{ github.head_ref || github.ref_name }}
type=gha,scope=cypress-debug-${{ github.base_ref || 'main' }}
cache-to: type=gha,scope=cypress-debug-${{ github.head_ref || github.ref_name }},mode=min
# ── Start services ────────────────────────────────────────────────────
# Services start in the background while npm installs and UI build run.
- name: Start services
run: |
# host.docker.internal doesn't resolve on Linux CI runners.
sed -i 's/host = "host.docker.internal"/host = "redis"/' config/docker-configuration.toml
docker compose --profile postgres-local up -d --no-build
# ── npm + Cypress binary caches ───────────────────────────────────────
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: |
website/package-lock.json
package-lock.json
- name: Cache Cypress binary
uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-binary-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
cypress-binary-${{ runner.os }}-
# ── Install UI + Cypress deps (runs while services are coming up) ──────
- name: Install UI deps
run: npm ci
working-directory: website
- name: Install Cypress deps
run: npm ci
# ── Wait for API ──────────────────────────────────────────────────────
# The ready endpoint implies basic health — one check is enough.
# By this point services have had ~3 min to start while npm ran above.
- name: Wait for API
run: |
timeout 180 bash -c \
'until curl -sf http://localhost:8080/health/ready; do sleep 3; done' \
|| { echo "❌ API failed to become ready"; docker compose logs --tail=50; exit 1; }
echo "✓ API ready"
# ── Bash API tests ───────────────────────────────────────────────────
# Run before the UI is served — only need the API, not the browser.
# test_auth.sh covers the full auth surface: API key lifecycle (create,
# list, revoke, Redis cache) + user auth (signup, login, JWT, logout,
# invite, multi-merchant). test_api_key_auth.sh is a strict subset of
# test_auth.sh so only the comprehensive script runs here.
# ADMIN_SECRET matches config/development.toml [admin_secret] secret.
# test_euclid.sh is excluded: no exit-code-on-failure, developer tool.
- name: Auth integration tests
run: bash scripts/test_auth.sh
env:
BASE_URL: http://localhost:8080
ADMIN_SECRET: test_admin
# ── Build + serve UI ─────────────────────────────────────────────────
# Build first so import/compile errors (e.g. missing React hooks) fail
# fast with a clear message before Cypress even starts.
# vite preview has a proxy configured in vite.config.ts (same routes as
# the dev server), so API calls are forwarded to http://localhost:8080
# without CORS issues.
- name: Build UI
run: npx vite build
working-directory: website
- name: Start UI preview server
run: npx vite preview --port 5173 --host &
working-directory: website
- name: Wait for UI
run: |
timeout 60 bash -c \
'until curl -sf http://localhost:5173; do sleep 2; done'
- name: Install just
run: curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
# ── Run all Cypress specs across 3 parallel workers ───────────────────
# Delegates to `just cypress-parallel` — identical to local runs.
- name: Run Cypress
run: just cypress-parallel
env:
CYPRESS_API_BASE_URL: http://localhost:8080
CYPRESS_UI_BASE_URL: http://localhost:5173
CYPRESS_RUNTIME_MODE: ci
- name: Upload screenshots on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots/
retention-days: 7
- name: Cleanup services
if: always()
run: docker compose --profile postgres-local down --volumes