End-to-end tests
Run the desktop Playwright harness that drives the built Electron app against a local backend, and the services and system libraries it needs.
The desktop app ships its own Playwright suite in apps/desktop/e2e/. One command builds it against a local backend, launches the real Electron binary, and drives the app through its own UI.
Running it
pnpm infra.apps/desktop/playwright-report/ (pnpm --filter desktop exec playwright show-report).pnpm infra # Postgres, Redis, Mailpit, Inngest, MinIO
pnpm --filter desktop e2e:desktop:ci # build against the harness origin, then run every default spec
pnpm --filter desktop e2e:live # the opt-in specs that drive a real coding CLIpnpm --filter desktop desktop:build:e2e runs that build on its own.
Ports and build output
| Fact | Value |
|---|---|
| Origin | http://127.0.0.1:<5100 + (checkout hash % 900)>; DESKTOP_E2E_ORIGIN overrides it |
| Web build directory | apps/web/.next-e2e-desktop, never the .next your pnpm dev uses |
| Database | A throwaway one per run, dropped on exit |
| Concurrency | Separate ports and build directories from the web suite, so both can run at once |
The app bakes its backend origin in at build time, so an override has to be set before the build, never after.
The harness targets the fullstack architecture. With a separate backend the API answers on another port, so repoint the origin at it or the seed 404s.
What the specs prove
| Spec | Gate | What it drives |
|---|---|---|
login.spec.ts | default | the real device-authorization grant, from the app's own login screen |
dashboard.spec.ts | default | a profile holding a token boots signed in, not into onboarding |
settings.spec.ts | default | the user menu opens General settings, and sign-out returns to login |
terminal-panel.spec.ts | default | the terminal surface renders per your config flags |
chat-composer.spec.ts, automations.spec.ts | default | the chat and automation surfaces against the local fake provider |
chat-composer.live.spec.ts, automations.live.spec.ts, terminal-session.live.spec.ts | live | a real coding CLI completing a real turn |
How they drive the app:
- Real Electron, programmatically. Playwright launches the app's own binary and drives its DOM over the debugging bridge - it never moves the OS pointer, so a local run steals no focus.
- Isolation without parallelism. One worker, a fresh
--user-data-dirper launch, a uniquely seeded user per spec. - Gated on your flags. Specs read
@repo/configand register conditionally, so the summary reports 0 skipped - see Writing tests. - Offline. The renderer's outbound requests are blocked at the Electron context, so the packaged support widget never reaches the network during a run.
Linux and CI
Electron needs a display and its GTK/NSS system libraries. That is an apt install, not playwright install - the suite launches the app's own Electron, so a downloaded Chromium would go unused.
sudo apt-get install -y xvfb libgtk-3-0t64 libnss3 libasound2t64 libgbm1 libatk1.0-0t64 \
libatk-bridge2.0-0t64 libatspi2.0-0t64 libcups2t64 libdrm2 libxkbcommon0 libxcomposite1 \
libxdamage1 libxfixes3 libxrandr2 libxtst6 libnotify4 libsecret-1-0 libpango-1.0-0 libcairo2
xvfb-run --auto-servernum pnpm --filter desktop e2e:desktop:ciYour generated .github/workflows/ci.yml already carries this as a desktop-e2e job on a manual dispatch: an Electron build on top of a Next production build is too slow for every push.
Ubuntu 24.04 gave several of these a t64 suffix. On an older release, install the unsuffixed name (libgtk-3-0, libasound2, …).