diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 00000000..af8eb7ac --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,59 @@ +name: E2E + +on: + workflow_call: + inputs: + ref: + description: Ref to check out (defaults to the calling workflow's ref) + required: false + type: string + +jobs: + e2e: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} + + # Why: electron-vite's globalSetup invokes a full app build, which + # compiles native modules via node-gyp. Mirrors the install step in + # pr.yml's verify job so E2E doesn't hit missing-toolchain errors. + - name: Install native build tools + run: sudo apt-get update && sudo apt-get install -y build-essential python3 + + # Why: Electron on Linux needs an X display even when the app + # suppresses mainWindow.show() via ORCA_E2E_HEADLESS. xvfb provides a + # virtual framebuffer so Chromium can initialize without a real display. + - name: Install xvfb + run: sudo apt-get install -y xvfb + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run E2E tests + run: xvfb-run --auto-servernum pnpm run test:e2e + + # Why: Playwright retains traces/screenshots only on failure. Uploading + # them as an artifact makes post-mortem debugging on CI possible without + # re-running locally. + - name: Upload Playwright traces + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-traces + path: test-results/ + retention-days: 7 + if-no-files-found: ignore diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9508e3d7..e91c89c9 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -43,3 +43,6 @@ jobs: - name: Build run: pnpm build + + e2e: + uses: ./.github/workflows/e2e.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64de5cbe..35646f5e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,6 +63,18 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Why: E2E runs alongside the release for visibility (failures surface as a + # red check on the tag), but it is NOT in `release`'s needs list. Releases + # already take a while and the suite is already a required check on PRs, so + # gating here would mostly just delay shipping without adding much signal. + # Matches the pattern used by noqa's app deploy, which runs E2E with + # continue-on-error so failures are visible but don't block the deploy. + e2e: + needs: resolve-release + uses: ./.github/workflows/e2e.yml + with: + ref: ${{ needs.resolve-release.outputs.ref }} + release: needs: - resolve-release diff --git a/tests/playwright.config.ts b/tests/playwright.config.ts index 75a3533f..ecaf4ea5 100644 --- a/tests/playwright.config.ts +++ b/tests/playwright.config.ts @@ -26,6 +26,11 @@ export default defineConfig({ // substantially. The few visible-window tests that still rely on real // pointer interaction are marked serial in their spec file instead. fullyParallel: true, + // Why: Playwright defaults to workers=1 on CI, which would serialize all + // specs on the ubuntu-latest runner (4 vCPUs) and waste headroom. Each test + // launches an isolated Electron instance with its own userData dir, so they + // don't share state — we can safely fan out to match the runner's vCPU count. + workers: process.env.CI ? 4 : undefined, forbidOnly: !!process.env.CI, retries: process.env.CI ? 1 : 0, reporter: 'list',