Skip to content
This repository was archived by the owner on Jul 24, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: e2e

# Runs the Playwright-Electron setup-wizard suite against a real build of the app.
# Electron is a GUI process, so it needs an X server - we give it a virtual one
# via xvfb-run and launch with --no-sandbox/--disable-gpu (set in e2e/fixtures.ts).
# EDISON_DRY_RUN=1 (also in fixtures.ts) short-circuits the stdiod/detectord
# daemons so the app boots straight to the wizard with no native subprocesses.

on:
workflow_dispatch:
pull_request:
branches: [main]
push:
branches: [main]

concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
setup-wizard:
name: Setup wizard (Playwright-Electron)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- run: npm ci

# System libraries the Electron/Chromium runtime needs (libnss3, libgbm,
# libasound, ...). Playwright ships the exact set for its Chromium, which is
# a superset of what Electron needs, so reuse it instead of a manual apt list.
- name: Install headless runtime deps
run: npx playwright install-deps chromium

- name: Build the app (main + preload + renderer)
run: npm run build

# xvfb-run -a provides the virtual X server on an auto-selected display.
- name: Run e2e under Xvfb
run: xvfb-run -a npm run test:e2e
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

# On failure, upload the HTML report + per-test traces/screenshots so the
# failure is debuggable beyond the raw log (playwright.config.ts retains
# trace + screenshot on failure).
- name: Upload Playwright artifacts
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 7
if-no-files-found: ignore
9 changes: 6 additions & 3 deletions e2e/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { join } from "path";
* the ElectronApplication and first window Page.
*
* Expects the app to be built first via `npm run build` (electron-vite build).
* In CI, set EDISON_TEST_MODE=1 to skip real auth and backend calls.
* EDISON_DRY_RUN=1 short-circuits stdiod/detectord subprocess spawning so the
* app boots to the wizard without native daemons (see src/main/stdiod/controller.ts,
* src/main/detectord/controller.ts). --no-sandbox/--disable-gpu let Electron run
* headless under Xvfb (e.g. CI, or as root in a sandbox).
*/
export const test = base.extend<{
electronApp: ElectronApplication;
Expand All @@ -17,11 +20,11 @@ export const test = base.extend<{
const mainPath = join(__dirname, "../out/main/index.js");

const app = await electron.launch({
args: [mainPath],
args: [mainPath, "--no-sandbox", "--disable-gpu"],
env: {
...process.env,
NODE_ENV: "test",
EDISON_TEST_MODE: "1",
EDISON_DRY_RUN: "1",
},
});

Expand Down
10 changes: 5 additions & 5 deletions e2e/setup-wizard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ test.describe("Setup Wizard", () => {
// The wizard should show the welcome step with Edison branding
await expect(firstWindow.locator("text=Edison Watch")).toBeVisible({ timeout: 10000 });

// Step indicator should show step 1 as active
await expect(firstWindow.locator("text=Sign In")).toBeVisible();

// Sign in form should be present
await expect(firstWindow.locator('input[type="email"], input[placeholder*="email" i]')).toBeVisible();
// Step indicator should show step 1 ("Welcome") as active (see StepIndicator.tsx)
await expect(firstWindow.locator("text=Welcome")).toBeVisible();
});

test("Step 1 shows sign-in options", async ({ firstWindow }) => {
// Wait for the welcome step to render
await expect(firstWindow.locator("text=Edison Watch")).toBeVisible({ timeout: 10000 });

// The email form is behind the provider-select view - open it first (WelcomeStep.tsx)
await firstWindow.getByText("Continue with Email", { exact: false }).click();

// Should show email input
const emailInput = firstWindow.locator('input[type="email"], input[placeholder*="email" i]');
await expect(emailInput).toBeVisible();
Expand Down
7 changes: 6 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ export default defineConfig({
timeout: 30000,
retries: 0,
workers: 1, // Electron tests must run serially
// HTML report + list output; the report is uploaded as a CI artifact on failure.
reporter: [["list"], ["html", { open: "never" }]],
use: {
trace: "on-first-retry",
// retries: 0, so "on-first-retry" would never capture anything - retain on
// failure instead so CI has a trace + screenshot to inspect (see e2e.yml).
trace: "retain-on-failure",
screenshot: "only-on-failure",
},
});
Loading