From 00ee563311d55222f5923c526cb14465dd52e8f0 Mon Sep 17 00:00:00 2001
From: th
Date: Sun, 7 Jun 2026 11:09:39 +0200
Subject: [PATCH] Add Electron E2E harness launcher
---
electron/main.cts | 21 ++++++++++++++++++---
package.json | 2 +-
scripts/run-electron-harness.cjs | 21 +++++++++++++++++++++
scripts/verify-desktop-render.cjs | 9 +++++++--
4 files changed, 47 insertions(+), 6 deletions(-)
create mode 100644 scripts/run-electron-harness.cjs
diff --git a/electron/main.cts b/electron/main.cts
index cb9e22c..e5b2b4e 100644
--- a/electron/main.cts
+++ b/electron/main.cts
@@ -10,6 +10,7 @@ const { validateRenderRequest } = require('../shared/renderValidation.cjs') as {
}
const isDev = !app.isPackaged
+const isE2E = process.env.RUWIO_E2E === '1'
const devServerUrl = process.env.VITE_DEV_SERVER_URL ?? 'http://localhost:5200'
const devServerOrigin = new URL(devServerUrl).origin
const appIconPath = isDev
@@ -43,6 +44,12 @@ function sendAppCommand(command: string): void {
mainWindow.webContents.send('ruwio:menu-command', command)
}
+declare global {
+ // Playwright's Electron runner can evaluate in the main process and call this
+ // env-gated hook without expanding the preload bridge surface.
+ var __ruwioTestMenuCommand: ((command: string) => void) | undefined
+}
+
function sendRenderProgress(
target: Electron.WebContents,
event: { jobId: string; progress: number; done: boolean; error?: string },
@@ -101,9 +108,14 @@ async function runRenderMp4(event: IpcMainInvokeEvent, payload: RenderMp4Payload
defaultPath: sanitizeMp4Name(payload?.suggestedName),
filters: [{ name: 'MP4 Video', extensions: ['mp4'] }],
}
- const saveResult = window
- ? await dialog.showSaveDialog(window, saveOptions)
- : await dialog.showSaveDialog(saveOptions)
+ const e2eSavePath = isE2E
+ ? process.env.RUWIO_E2E_SAVE_PATH || path.join(app.getPath('temp'), saveOptions.defaultPath)
+ : ''
+ const saveResult = e2eSavePath
+ ? { canceled: false, filePath: e2eSavePath }
+ : window
+ ? await dialog.showSaveDialog(window, saveOptions)
+ : await dialog.showSaveDialog(saveOptions)
if (saveResult.canceled || !saveResult.filePath) return null
const jobId = randomUUID()
@@ -379,6 +391,9 @@ app.setName('RUWIO')
app.whenReady().then(() => {
ipcMain.handle('ruwio:render-mp4', handleRenderMp4)
ipcMain.handle('ruwio:render-cancel', handleRenderCancel)
+ if (isE2E) {
+ globalThis.__ruwioTestMenuCommand = (command: string) => sendAppCommand(command)
+ }
createMenu()
createMainWindow()
diff --git a/package.json b/package.json
index 971fc0c..3f29cfa 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
"test:render-server": "node --test server/server.smoke.test.cjs",
"test:render-server:e2e": "node --test server/render.e2e.test.cjs",
"test:render-desktop": "tsc -p tsconfig.electron.json && node scripts/verify-desktop-render.cjs",
- "test:capture-fidelity": "tsc -p tsconfig.electron.json && electron scripts/verify-capture-fidelity.cjs",
+ "test:capture-fidelity": "tsc -p tsconfig.electron.json && node scripts/run-electron-harness.cjs scripts/verify-capture-fidelity.cjs",
"stage:ffmpeg": "node scripts/stage-ffmpeg.cjs"
},
"dependencies": {
diff --git a/scripts/run-electron-harness.cjs b/scripts/run-electron-harness.cjs
new file mode 100644
index 0000000..1ad5645
--- /dev/null
+++ b/scripts/run-electron-harness.cjs
@@ -0,0 +1,21 @@
+const { spawnSync } = require('node:child_process')
+
+const electron = require('electron')
+const args = process.argv.slice(2)
+
+if (!args.length) {
+ console.error('usage: node scripts/run-electron-harness.cjs |