Files
RUWIO/scripts/verify-desktop-render.cjs
T
thandClaude Opus 4.8 518d43855b Add desktop/server MP4 export pipeline, hardening, and render tests (v1.3.0)
Desktop MP4 export renders frames from a hidden BrowserWindow via
capturePage, pipes PNGs to a bundled ffmpeg (libx264/yuv420p/+faststart),
and writes the file through an atomic temp-file swap. It shares scene
validation (shared/renderValidation.cjs) and the render.html renderer with
the standalone Puppeteer server, which is hardened in parallel.

Render worker & main (electron/):
- abortable render loop with an inactivity watchdog and cancel plumbing
- atomic temp-file + rename so a cancel/failure never truncates a
  pre-existing target (#7); stdin EPIPE / early-exit / backpressure-hang
  handled (#8)
- HiDPI frames normalized to canvas size (#10); odd dimensions padded to
  even (#13, also server); backgroundThrottling disabled (#12)
- reject canvases larger than the display (#11); single-render guard;
  isolated render session that blocks network requests (#5)

Tests:
- Electron harnesses (scripts/verify-*.cjs) for capture, export, cancel,
  timeout, assets, background, capture-fidelity, resolution, odd-dims,
  aggregated by verify-desktop-render.cjs
- server smoke + e2e and shared validation unit tests
- headless hook (src/devtools/headless.ts) to run the in-app suite under
  browser/electron hosts; pixel-diff util + capture fixtures (test/)
- dev_docs plans: integration, fixes (#7-#13), and the e2e/capture strategy

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 20:41:25 +02:00

40 lines
2.0 KiB
JavaScript

// Runs the desktop-render verification harnesses (Phase A/B of
// electron-rendering-integration-plan_2.md) under Electron and aggregates the
// results. Each harness exits 0 on PASS, non-zero on FAIL.
//
// npm run test:render-desktop (compiles electron TS first, then runs this)
//
// Requires a display (DISPLAY set), ffmpeg + ffprobe on PATH, and a built
// dist-electron/ (the npm script's `tsc` step handles the latter).
const { spawnSync } = require('node:child_process')
const path = require('node:path')
const electron = require('electron') // resolves to the electron binary path
const harnesses = [
['capture', 'verify-capture.cjs', 'hidden-window capturePage produces advancing, non-blank frames (#1/#2)'],
['capture-fidelity', 'verify-capture-fidelity.cjs', 'capturePage matches render.html canvas pixels'],
['export', 'verify-export.cjs', 'real worker renders a playable H.264 MP4 with motion'],
['resolution', 'verify-resolution.cjs', 'MP4 dimensions match the requested canvas on HiDPI (#10)'],
['odd-dims', 'verify-odd-dims.cjs', 'odd canvas dimensions pad to an even MP4 instead of failing (#13)'],
['cancel', 'verify-cancel.cjs', 'cancel mid-export aborts cleanly, no partial file'],
['timeout', 'verify-timeout.cjs', 'inactivity watchdog aborts a wedged render (#3)'],
['assets', 'verify-assets.cjs', 'local data: assets load; remote http: blocked, no hang (#5)'],
['background', 'verify-bg.cjs', 'transparent regions flatten to white, not black (#4)'],
]
let failures = 0
for (const [name, file, desc] of harnesses) {
process.stdout.write(`\n=== ${name}: ${desc} ===\n`)
const res = spawnSync(electron, [path.join(__dirname, file)], {
stdio: ['ignore', 'inherit', 'ignore'], // drop Electron's chatty stderr
})
if (res.status !== 0) {
failures += 1
process.stdout.write(`*** ${name} FAILED (exit ${res.status})\n`)
}
}
process.stdout.write(`\n${failures === 0 ? 'ALL DESKTOP RENDER CHECKS PASSED' : failures + ' CHECK(S) FAILED'}\n`)
process.exit(failures === 0 ? 0 : 1)