test(electron): smoke test as the regression gate for the desktop shell
Phase 1 step 2 of VNCprodbuild. e2e/electron-smoke.spec.ts uses Playwright's
_electron.launch() to boot the real skeleton (dist-electron/main.js from
step 1) and asserts:
- the login screen renders (same input[type="text"]/[type="password"]
selectors as e2e/login.spec.ts's browser-based check)
- zero uncaught page errors fire during load
Sets JMAP_SERVER_URL (any non-empty value) so the app reaches
lib/setup/state.ts's "env-managed" state and serves the normal login screen
instead of 302ing to the first-run /setup wizard - no live mail server or
mock JMAP build flag needed just to prove the shell renders.
playwright.electron.config.ts is deliberately separate from
playwright.config.ts: it has no `webServer` block, since this suite's app
boots its own server and would otherwise race pointlessly with `npm run dev`
starting on :3000 for the browser-based e2e/*.spec.ts suite.
Wired as `npm run test:electron`. Verified green locally (2 passed) after
`npm run build:standalone && npm run build:electron`; every later step in
the Electron rollout must keep this passing before moving on.
This commit is contained in:
@@ -42,6 +42,10 @@ yarn-error.log*
|
|||||||
/dist-electron/
|
/dist-electron/
|
||||||
/dist-electron-builds/
|
/dist-electron-builds/
|
||||||
|
|
||||||
|
# playwright output
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { test, expect, _electron as electron } from '@playwright/test';
|
||||||
|
import type { ElectronApplication, Page } from '@playwright/test';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
// Regression gate for the Electron desktop shell (electron/main.ts +
|
||||||
|
// electron/preload.ts). Launches the real skeleton - the same standalone
|
||||||
|
// Next.js server artifact the Dockerfile produces, booted as a child
|
||||||
|
// process by main.ts, with a real BrowserWindow on top - and asserts the
|
||||||
|
// login screen renders with zero uncaught page errors. Every later step in
|
||||||
|
// the Electron rollout (notification bridge, realtime sync, packaging) must
|
||||||
|
// keep this green; run it before touching anything else.
|
||||||
|
//
|
||||||
|
// Requires `npm run build:standalone && npm run build:electron` to have run
|
||||||
|
// first (see package.json's `electron:dev`/`test:electron` scripts, which
|
||||||
|
// this suite assumes but does not itself trigger, matching how
|
||||||
|
// playwright.config.ts's browser suite assumes `npm run build` for its own
|
||||||
|
// prod-mode runs).
|
||||||
|
const projectRoot = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
test.describe('Electron desktop shell', () => {
|
||||||
|
let electronApp: ElectronApplication;
|
||||||
|
let window: Page;
|
||||||
|
const pageErrors: Error[] = [];
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
electronApp = await electron.launch({
|
||||||
|
args: [projectRoot],
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
// Bypass the first-run setup wizard (lib/setup/state.ts's
|
||||||
|
// "bootstrap" state, which 302s everything to /setup) without
|
||||||
|
// needing a reachable JMAP server just to prove the login screen
|
||||||
|
// renders - any non-empty JMAP_SERVER_URL is enough to reach
|
||||||
|
// "env-managed" state and serve the normal app shell.
|
||||||
|
JMAP_SERVER_URL: 'https://stalwart.sandbox.vnc.de',
|
||||||
|
SESSION_SECRET: 'electron-smoke-test-not-for-production',
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
window = await electronApp.firstWindow();
|
||||||
|
window.on('pageerror', (error) => {
|
||||||
|
pageErrors.push(error);
|
||||||
|
});
|
||||||
|
await window.waitForLoadState('domcontentloaded');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async () => {
|
||||||
|
await electronApp?.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('boots the standalone server and renders the login screen', async () => {
|
||||||
|
// Same selectors as e2e/login.spec.ts's browser-based check - the
|
||||||
|
// shell should render the identical login form, not a different view.
|
||||||
|
await expect(window.locator('input[type="text"]')).toBeVisible({ timeout: 20000 });
|
||||||
|
await expect(window.locator('input[type="password"]')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('produces zero uncaught page errors', () => {
|
||||||
|
expect(pageErrors).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
// Separate from playwright.config.ts on purpose: the Electron smoke suite
|
||||||
|
// launches its own app (which boots its own standalone Next.js server via
|
||||||
|
// electron/main.ts - see scripts/build-electron.mjs), so it must NOT inherit
|
||||||
|
// the main config's `webServer` (which starts `npm run dev` on :3000 for the
|
||||||
|
// browser-based e2e/*.spec.ts suite) - the two would fight over nothing but
|
||||||
|
// still waste time starting a server this suite never touches.
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
testMatch: 'electron-smoke.spec.ts',
|
||||||
|
timeout: 60000,
|
||||||
|
retries: 0,
|
||||||
|
use: {
|
||||||
|
trace: 'retain-on-failure',
|
||||||
|
},
|
||||||
|
// Electron tests drive their own app windows via the `_electron` fixture,
|
||||||
|
// not a browser project - one worker keeps main-process/server startup
|
||||||
|
// logs and any zombie processes easy to reason about.
|
||||||
|
workers: 1,
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user