fix(ci): add Cypress E2E workflow #32
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |