/** Own-YCode isolated component fixture. Run on a permitted Electron desktop: * node scripts/test-mode-chip-native.mjs * No original app, model, SDK, user data, or production session access. */ import fs from 'node:fs' import path from 'node:path' import { createRequire } from 'node:module' import { spawn } from 'node:child_process' const require = createRequire(import.meta.url) if (process.versions.electron) throw new Error('Launch this fixture builder with Node, not Electron') const base = path.resolve('.tmp') fs.mkdirSync(base, { recursive: true }) const file = 'src/renderer/src/workbuddy/composer/ModeChip.tsx' async function prepareFixture(output) { const src = relative => JSON.stringify(path.resolve(relative).replaceAll('\\', '/')) fs.writeFileSync(path.join(output, 'fixture.tsx'), ` import React,{useState} from 'react'; import {createRoot} from 'react-dom/client'; import {ModeChip} from ${src(file)}; import ${src('src/renderer/src/workbuddy/typography/tokens.css')}; import ${src('src/renderer/src/styles/tokens.css')}; import ${src('src/renderer/src/ui/tokens.css')}; import ${src('src/renderer/src/styles/app.css')}; import ${src('src/renderer/src/workbuddy/conversation/source-styles.css')}; const q=(window as any).qa={clears:0}; function Fixture(){const [mode,setMode]=useState('plan'),[disabled,setDisabled]=useState(false);Object.assign(q,{setMode,setDisabled});return
{q.clears++;setMode('craft')}}/>
};createRoot(document.getElementById('root')!).render();`) await require('esbuild').build({ entryPoints: [path.join(output, 'fixture.tsx')], bundle: true, platform: 'browser', jsx: 'automatic', outfile: path.join(output, 'fixture.js'), logLevel: 'warning' }) fs.writeFileSync(path.join(output, 'index.html'), '
') } const output = fs.mkdtempSync(path.join(base, 'mode-chip-native-')) function phase(name) { const detail = { phase: name, time: new Date().toISOString(), output } fs.writeFileSync(path.join(output, 'runner-phase.json'), JSON.stringify(detail, null, 2)) console.log(JSON.stringify(detail)) } function fail(error) { const detail = String(error.stack || error) fs.writeFileSync(path.join(output, 'runner-failure.txt'), detail) console.error(detail); process.exitCode = 1 } phase('prepare-fixture') const buildTimeout = setTimeout(() => { fail(new Error('Fixture preparation timed out after 30000ms')) process.exit(1) }, 30000) try { // All esbuild work finishes under Node, before starting Electron's CJS entry. await prepareFixture(output) clearTimeout(buildTimeout) phase('fixture-built') if (process.argv.includes('--build-only')) { console.log(JSON.stringify({ output, fixtureBuilt: true, nativeExecuted: false })) } else { const env = { ...process.env } delete env.ELECTRON_RUN_AS_NODE; delete env.ELECTRON_RENDERER_URL phase('launch-electron') const child = spawn(require('electron'), [path.resolve('scripts/test-mode-chip-native-electron.cjs'), output], { env, windowsHide: true, stdio: 'inherit' }) let timedOut = false, spawnFailed = false const timeout = setTimeout(() => { timedOut = true let lastPhase = 'no Electron phase recorded' try { lastPhase = fs.readFileSync(path.join(output, 'phase.json'), 'utf8') } catch {} fail(new Error(`Electron timed out after 60000ms; last phase: ${lastPhase}`)) // Only terminate the child created by this runner, never an existing app/dev. child.kill('SIGKILL') }, 60000) child.once('error', error => { spawnFailed = true; clearTimeout(timeout); fail(error) }) child.once('close', (code, signal) => { clearTimeout(timeout) const passed = !timedOut && !spawnFailed && code === 0 && fs.existsSync(path.join(output, 'report.json')) process.exitCode = passed ? 0 : 1 if (!passed && !timedOut && !spawnFailed) fail(new Error(`Electron did not complete: exitCode=${code}, signal=${signal}; see phase.json / failure.txt`)) phase(passed ? 'complete' : 'failed') console.log(JSON.stringify({ output, exitCode: code, signal, timedOut, nativeExecuted: passed })) }) } } catch (error) { clearTimeout(buildTimeout); fail(error) }