feat: graceful missing-payment-config UX for forkers #550
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
| name: E2E Tests | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| - cron: '0 6 * * 1' # Monday 6 AM UTC — full 3-browser run | |
| workflow_dispatch: | |
| concurrency: | |
| group: e2e-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| NEXT_PUBLIC_SUPABASE_URL: ${{ vars.NEXT_PUBLIC_SUPABASE_URL }} | |
| NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ vars.NEXT_PUBLIC_SUPABASE_ANON_KEY }} | |
| jobs: | |
| # ============================================ | |
| # STEP 1: Build once, cache for all shards | |
| # ============================================ | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build application | |
| run: pnpm build | |
| env: | |
| GITHUB_ACTIONS: false | |
| NEXT_PUBLIC_EMAILJS_PUBLIC_KEY: ${{ secrets.EMAILJS_PUBLIC_KEY }} | |
| NEXT_PUBLIC_EMAILJS_SERVICE_ID: ${{ vars.NEXT_PUBLIC_EMAILJS_SERVICE_ID }} | |
| NEXT_PUBLIC_EMAILJS_TEMPLATE_ID: ${{ vars.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID }} | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| retention-days: 1 | |
| # ============================================ | |
| # STEP 2: Smoke tests (~2 min) - FAST FEEDBACK | |
| # ============================================ | |
| smoke: | |
| name: Smoke Tests | |
| runs-on: ubuntu-latest | |
| needs: build | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright (chromium only) | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps chromium && break | |
| echo "Retry $i: playwright install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Run smoke tests (sign-up only - auth tests run after auth-setup) | |
| run: | | |
| pnpm exec playwright test \ | |
| tests/e2e/auth/sign-up.spec.ts \ | |
| --project=signup \ | |
| --no-deps \ | |
| --reporter=list \ | |
| --timeout=30000 | |
| env: | |
| CI: true | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| - name: Smoke test summary | |
| if: always() | |
| run: echo "## Smoke Tests Complete" >> "$GITHUB_STEP_SUMMARY" | |
| # ============================================ | |
| # STEP 3: Rate-limiting tests (ordered, clean IP) | |
| # ============================================ | |
| rate-limiting: | |
| name: Rate-Limiting Tests | |
| runs-on: ubuntu-latest | |
| needs: smoke | |
| timeout-minutes: 10 | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright (chromium only) | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps chromium && break | |
| echo "Retry $i: playwright install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Run rate-limiting tests (ordered) | |
| run: pnpm test:e2e --project=rate-limiting --project=brute-force --project=signup --reporter=list --trace=on-first-retry | |
| env: | |
| CI: true | |
| PLAYWRIGHT_BROWSER: chromium | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| # ============================================ | |
| # STEP 4: Auth Setup (runs ONCE, shares state) | |
| # ============================================ | |
| auth-setup: | |
| name: Auth Setup | |
| runs-on: ubuntu-latest | |
| needs: smoke | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright (chromium only for auth) | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps chromium && break | |
| echo "Retry $i: playwright install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| - name: Run auth setup | |
| run: pnpm exec playwright test --project=setup --reporter=list --timeout=180000 | |
| env: | |
| CI: true | |
| AUTH_SETUP_JOB: true | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| - name: Upload auth state | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: auth-state | |
| path: tests/e2e/fixtures/storage-state-auth.json | |
| retention-days: 1 | |
| # ============================================ | |
| # STEP 5: Full E2E tests (sharded, parallel) | |
| # ============================================ | |
| e2e: | |
| name: E2E (${{ matrix.project }} ${{ matrix.shard }}) | |
| runs-on: ubuntu-latest | |
| needs: [smoke, rate-limiting, auth-setup] | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # ===== CHROMIUM (8 shards) ===== | |
| - { project: chromium-msg, browser: chromium, shard: 1/2 } | |
| - { project: chromium-msg, browser: chromium, shard: 2/2 } | |
| - { project: chromium-gen, browser: chromium, shard: 1/6 } | |
| - { project: chromium-gen, browser: chromium, shard: 2/6 } | |
| - { project: chromium-gen, browser: chromium, shard: 3/6 } | |
| - { project: chromium-gen, browser: chromium, shard: 4/6 } | |
| - { project: chromium-gen, browser: chromium, shard: 5/6 } | |
| - { project: chromium-gen, browser: chromium, shard: 6/6 } | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright browsers | |
| env: | |
| BROWSER: ${{ matrix.browser }} | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps "$BROWSER" && break | |
| echo "Retry $i: $BROWSER install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Download auth state | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: auth-state | |
| path: tests/e2e/fixtures/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| env: | |
| CI: true | |
| - name: Prime Supabase connection pool | |
| env: | |
| SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} | |
| SUPABASE_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} | |
| run: | | |
| # Wake Supabase free tier and warm the connection pool. | |
| # Without this, the first query from each shard takes 10-30s. | |
| curl -sf "${SUPABASE_URL}/rest/v1/user_profiles?select=id&limit=1" \ | |
| -H "apikey: ${SUPABASE_KEY}" \ | |
| -H "Authorization: Bearer ${SUPABASE_KEY}" \ | |
| > /dev/null 2>&1 || true | |
| echo "✓ Supabase connection pool primed" | |
| - name: Stagger shards within batch to reduce Supabase contention | |
| env: | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| SHARD_NUM=$(echo "$SHARD" | cut -d/ -f1) | |
| # max-parallel: 8 already serializes browsers. Within each batch | |
| # of 8, stagger by 5-10 seconds to spread the initial connection | |
| # burst on Supabase. | |
| if echo "$PROJECT" | grep -q "msg"; then | |
| DELAY=$(((SHARD_NUM - 1) * 10)) | |
| else | |
| DELAY=$(((SHARD_NUM - 1) * 5)) | |
| fi | |
| echo "$PROJECT shard $SHARD_NUM: waiting ${DELAY}s" | |
| sleep $DELAY | |
| - name: Run E2E tests (${{ matrix.project }} ${{ matrix.shard }}) | |
| env: | |
| CI: true | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| DEBUG: pw:api | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| # --no-deps: skip the 'setup' project dependency. The auth-setup | |
| # job already ran setup once (with chromium) and uploaded the | |
| # auth-state artifact, which we downloaded above. Without --no-deps, | |
| # Playwright tries to re-run setup with the matrix browser, which | |
| # fails because firefox/webkit shards don't install chromium. | |
| pnpm exec playwright test \ | |
| --project="$PROJECT" \ | |
| --shard="$SHARD" \ | |
| --reporter=blob \ | |
| --trace=on-first-retry \ | |
| --no-deps | |
| - name: Upload blob report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: blob-report-${{ matrix.project }}-${{ strategy.job-index }} | |
| path: blob-report/ | |
| retention-days: 1 | |
| # ============================================ | |
| # STEP 5b: Firefox E2E (waits for chromium to release Supabase) | |
| # ============================================ | |
| e2e-firefox: | |
| name: E2E (${{ matrix.project }} ${{ matrix.shard }}) | |
| runs-on: ubuntu-latest | |
| needs: e2e | |
| if: always() && !cancelled() | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { project: firefox-msg, browser: firefox, shard: 1/2 } | |
| - { project: firefox-msg, browser: firefox, shard: 2/2 } | |
| - { project: firefox-gen, browser: firefox, shard: 1/6 } | |
| - { project: firefox-gen, browser: firefox, shard: 2/6 } | |
| - { project: firefox-gen, browser: firefox, shard: 3/6 } | |
| - { project: firefox-gen, browser: firefox, shard: 4/6 } | |
| - { project: firefox-gen, browser: firefox, shard: 5/6 } | |
| - { project: firefox-gen, browser: firefox, shard: 6/6 } | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright browsers | |
| env: | |
| BROWSER: ${{ matrix.browser }} | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps "$BROWSER" && break | |
| echo "Retry $i: $BROWSER install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Download auth state | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: auth-state | |
| path: tests/e2e/fixtures/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| env: | |
| CI: true | |
| - name: Prime Supabase connection pool | |
| env: | |
| SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} | |
| SUPABASE_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} | |
| run: | | |
| curl -sf "${SUPABASE_URL}/rest/v1/user_profiles?select=id&limit=1" \ | |
| -H "apikey: ${SUPABASE_KEY}" \ | |
| -H "Authorization: Bearer ${SUPABASE_KEY}" \ | |
| > /dev/null 2>&1 || true | |
| - name: Stagger shards within batch | |
| env: | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| SHARD_NUM=$(echo "$SHARD" | cut -d/ -f1) | |
| if echo "$PROJECT" | grep -q "msg"; then | |
| DELAY=$(((SHARD_NUM - 1) * 10)) | |
| else | |
| DELAY=$(((SHARD_NUM - 1) * 5)) | |
| fi | |
| sleep $DELAY | |
| - name: Run E2E tests | |
| env: | |
| CI: true | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| DEBUG: pw:api | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| pnpm exec playwright test \ | |
| --project="$PROJECT" \ | |
| --shard="$SHARD" \ | |
| --reporter=blob \ | |
| --trace=on-first-retry \ | |
| --no-deps | |
| - name: Upload blob report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: blob-report-${{ matrix.project }}-${{ strategy.job-index }} | |
| path: blob-report/ | |
| retention-days: 1 | |
| # ============================================ | |
| # STEP 5c: WebKit E2E (waits for firefox to release Supabase) | |
| # ============================================ | |
| e2e-webkit: | |
| name: E2E (${{ matrix.project }} ${{ matrix.shard }}) | |
| runs-on: ubuntu-latest | |
| needs: e2e-firefox | |
| if: always() && !cancelled() | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { project: webkit-msg, browser: webkit, shard: 1/2 } | |
| - { project: webkit-msg, browser: webkit, shard: 2/2 } | |
| - { project: webkit-gen, browser: webkit, shard: 1/6 } | |
| - { project: webkit-gen, browser: webkit, shard: 2/6 } | |
| - { project: webkit-gen, browser: webkit, shard: 3/6 } | |
| - { project: webkit-gen, browser: webkit, shard: 4/6 } | |
| - { project: webkit-gen, browser: webkit, shard: 5/6 } | |
| - { project: webkit-gen, browser: webkit, shard: 6/6 } | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install Playwright browsers | |
| env: | |
| BROWSER: ${{ matrix.browser }} | |
| run: | | |
| for i in 1 2 3; do | |
| pnpm exec playwright install --with-deps "$BROWSER" && break | |
| echo "Retry $i: $BROWSER install failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| - name: Download build | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: build-output | |
| path: out/ | |
| - name: Download auth state | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: auth-state | |
| path: tests/e2e/fixtures/ | |
| - name: Start server | |
| run: | | |
| npx serve out -l 3000 & | |
| sleep 5 | |
| npx wait-on http://localhost:3000 --timeout 60000 | |
| env: | |
| CI: true | |
| - name: Prime Supabase connection pool | |
| env: | |
| SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} | |
| SUPABASE_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} | |
| run: | | |
| curl -sf "${SUPABASE_URL}/rest/v1/user_profiles?select=id&limit=1" \ | |
| -H "apikey: ${SUPABASE_KEY}" \ | |
| -H "Authorization: Bearer ${SUPABASE_KEY}" \ | |
| > /dev/null 2>&1 || true | |
| - name: Stagger shards within batch | |
| env: | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| SHARD_NUM=$(echo "$SHARD" | cut -d/ -f1) | |
| if echo "$PROJECT" | grep -q "msg"; then | |
| DELAY=$(((SHARD_NUM - 1) * 10)) | |
| else | |
| DELAY=$(((SHARD_NUM - 1) * 5)) | |
| fi | |
| sleep $DELAY | |
| - name: Run E2E tests | |
| env: | |
| CI: true | |
| SKIP_WEBSERVER: true | |
| BASE_URL: http://localhost:3000 | |
| DEBUG: pw:api | |
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | |
| TEST_USER_PRIMARY_EMAIL: ${{ vars.TEST_USER_PRIMARY_EMAIL }} | |
| TEST_USER_PRIMARY_PASSWORD: ${{ secrets.TEST_USER_PRIMARY_PASSWORD }} | |
| TEST_USER_SECONDARY_EMAIL: ${{ vars.TEST_USER_SECONDARY_EMAIL }} | |
| TEST_USER_SECONDARY_PASSWORD: ${{ secrets.TEST_USER_SECONDARY_PASSWORD }} | |
| TEST_USER_TERTIARY_EMAIL: ${{ vars.TEST_USER_TERTIARY_EMAIL }} | |
| TEST_USER_TERTIARY_PASSWORD: ${{ secrets.TEST_USER_TERTIARY_PASSWORD }} | |
| PROJECT: ${{ matrix.project }} | |
| SHARD: ${{ matrix.shard }} | |
| run: | | |
| pnpm exec playwright test \ | |
| --project="$PROJECT" \ | |
| --shard="$SHARD" \ | |
| --reporter=blob \ | |
| --trace=on-first-retry \ | |
| --no-deps | |
| - name: Upload blob report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: blob-report-${{ matrix.project }}-${{ strategy.job-index }} | |
| path: blob-report/ | |
| retention-days: 1 | |
| # ============================================ | |
| # STEP 6: Merge reports and generate summary | |
| # ============================================ | |
| report: | |
| name: Test Report | |
| runs-on: ubuntu-latest | |
| needs: [e2e, e2e-firefox, e2e-webkit] | |
| if: always() | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.16.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Download blob reports | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: blob-report-* | |
| path: all-blob-reports | |
| merge-multiple: true | |
| - name: Merge reports | |
| run: pnpm exec playwright merge-reports --reporter=html,github ./all-blob-reports | |
| - name: Upload HTML report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 7 | |
| - name: Generate summary | |
| if: always() | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| echo "## E2E Test Results" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| if [ "$EVENT_NAME" = "pull_request" ]; then | |
| echo "**PR Mode**: Chromium only (4 shards)" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "**Full Mode**: Chromium + Firefox + WebKit (12 shards total)" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Download the **playwright-report** artifact for detailed results." >> "$GITHUB_STEP_SUMMARY" |