name: Frontend CI on: push: pull_request: env: NODE_VERSION: '22.18.0' E2E_BACKEND_PORT: '3010' E2E_FRONTEND_PORT: '5190' jobs: unit: name: unit (test, type-check, build) runs-on: ubuntu-latest timeout-minutes: 25 steps: - name: Checkout frontend uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: package-lock.json - name: Install dependencies (npm ci) run: npm ci - name: Unit tests run: npm run test - name: Type check run: npm run type-check - name: Production build run: npm run build e2e: name: browser E2E (frontend + pinned backend) runs-on: ubuntu-latest needs: unit timeout-minutes: 45 env: CI: 'true' E2E_BACKEND_ROOT: backend-nest E2E_BACKEND_REF: ${{ vars.E2E_BACKEND_REF }} E2E_BACKEND_CHECKOUT_TOKEN: ${{ secrets.E2E_BACKEND_CHECKOUT_TOKEN }} steps: - name: Checkout frontend uses: actions/checkout@v4 - name: Require pinned backend ref (SHA or tag) run: | if [ -z "${E2E_BACKEND_REF}" ]; then E2E_BACKEND_REF="$(node -e "const c=require('./e2e/backend.contract.json'); const r=String(c.pinnedRef||''); if(!/^[0-9a-f]{40}$/i.test(r)){console.error('pinnedRef must be a full SHA'); process.exit(1)} process.stdout.write(r)")" echo "E2E_BACKEND_REF=${E2E_BACKEND_REF}" >> "$GITHUB_ENV" echo "Repository variable unset; using committed pinnedRef" fi echo "E2E_BACKEND_REF=${E2E_BACKEND_REF}" - name: Checkout backend (pinned ref) run: node scripts/ci-checkout-backend.mjs - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Install frontend dependencies (npm ci) run: npm ci - name: Install backend dependencies (npm ci) working-directory: backend-nest run: npm ci - name: Build backend for E2E run: npm run e2e:prepare - name: Install Playwright Chromium (OS deps) run: npx playwright install chromium --with-deps - name: Run Playwright E2E run: npm run test:e2e:ci - name: Upload failure artifacts if: failure() uses: actions/upload-artifact@v4 with: name: playwright-failure-${{ gitea.run_id }} path: | playwright-report/ test-results/ if-no-files-found: ignore - name: E2E cleanup if: always() run: npm run e2e:cleanup