77 lines
4.5 KiB
JavaScript
77 lines
4.5 KiB
JavaScript
/** 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<any>('plan'),[disabled,setDisabled]=useState(false);Object.assign(q,{setMode,setDisabled});return <div className="chat-pane cr-theme" style={{padding:40}}><div style={{display:"flex",alignItems:"center",gap:8}}><button id="before">Before</button><ModeChip mode={mode} disabled={disabled} onClear={()=>{q.clears++;setMode('craft')}}/><button id="after">After</button></div></div>};createRoot(document.getElementById('root')!).render(<Fixture/>);`)
|
|
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'), '<html data-theme="light"><head><meta charset="utf-8"><link rel="stylesheet" href="fixture.css"></head><body><div id="root"></div><script src="fixture.js"></script></body></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) }
|