// Exercise the repaired REA public CLI against an isolated, owned Electron fixture. // This proves capture capability, never visual parity with the WorkBuddy application. import assert from 'node:assert/strict' import { createRequire } from 'node:module' import { spawn } from 'node:child_process' import { createServer } from 'node:net' import fs from 'node:fs/promises' import path from 'node:path' import { setTimeout as delay } from 'node:timers/promises' const require = createRequire(import.meta.url) const root=path.resolve('.tmp/rea-visual-probe-self-test') await fs.mkdir(root,{recursive:true}) const portServer=createServer() await new Promise(resolve=>portServer.listen(0,'127.0.0.1',resolve)) const port=portServer.address().port await new Promise(resolve=>portServer.close(resolve)) const fixture=path.join(root,'index.html'),ready=path.join(root,'ready.json') await fs.rm(ready,{force:true}) await fs.writeFile(fixture,`
WorkBuddy

字体字形:中文测试

English glyph parity sample.

const value = 42;
用户气泡 Mixed 123
Native input animation
Scroll fixture
`) const main=path.join(root,'main.cjs') await fs.writeFile(main,`const {app,BrowserWindow}=require('electron');const fs=require('node:fs');let w; app.setPath('userData',${JSON.stringify(path.join(root,'user-data'))});app.disableHardwareAcceleration(); app.whenReady().then(async()=>{w=new BrowserWindow({show:false,useContentSize:true,width:1000,height:800,webPreferences:{offscreen:true,contextIsolation:true}});await w.loadFile(${JSON.stringify(fixture)});await w.webContents.executeJavaScript('document.fonts.ready.then(()=>true)');fs.writeFileSync(${JSON.stringify(ready)},JSON.stringify({ready:true}));});`) const env={...process.env};delete env.ELECTRON_RUN_AS_NODE const child=spawn(require('electron'),[main,`--remote-debugging-port=${port}`],{env,windowsHide:true,stdio:'ignore'}) async function cli(args){ return await new Promise((resolve,reject)=>{ const process=spawn(globalThis.process.execPath,['.tmp/rea-css-runtime/scripts/rea.mjs',...args,'--json'],{env:{...env,REA_VISUAL_TRACE:'1'},windowsHide:true,stdio:['ignore','pipe','pipe']}) let out='',err='';process.stdout.on('data',b=>out+=b);process.stderr.on('data',b=>err+=b) const timer=setTimeout(()=>{process.kill();reject(new Error('REA fixture CLI timeout'))},45000) process.once('error',reject);process.once('close',code=>{clearTimeout(timer);try{const result=JSON.parse(out);if(code||result.error)reject(new Error(JSON.stringify(result)+err));else resolve(result)}catch(error){reject(error)}}) }) } function normalized(value){return value.normalized_result??value.result?.normalized_result??value.result??value} try{ for(let n=0;n<80;n++){try{await fs.access(ready);break}catch{if(child.exitCode!==null)throw new Error('Owned fixture exited');await delay(100)}} await fs.access(ready) const endpoint=`http://127.0.0.1:${port}` const targets=await cli(['list-electron-targets',endpoint]) await fs.writeFile(path.join(root,'targets.json'),JSON.stringify(targets,null,2)) const target=normalized(targets).targets.find(t=>path.resolve(t.file_path)===fixture) assert.ok(target,'REA must discover the owned file fixture') const probe={include_fonts:true,track_selectors:['#panel'],actions:[ {kind:'click',step_id:'collapse',selector:'#toggle',restore:true,frames:8,interval_ms:40}, {kind:'hover',step_id:'hover',selector:'#toggle',frames:1}, {kind:'viewport',step_id:'resize',width:900,height:700,frames:1} ]} const captured=await cli(['inspect-electron-page',endpoint,target.target_id,'--include-visuals','--visual-probe',JSON.stringify(probe)]) await fs.writeFile(path.join(root,'capture.json'),JSON.stringify(captured,null,2)) const result=normalized(captured),visual=result.visuals assert.equal(visual.probe.status,'completed') assert.deepEqual(visual.font_missing,[]) assert.equal(new Set(visual.font_samples.map(s=>s.category)).size,5) for(const sample of visual.font_samples)assert.ok(sample.platformFonts.some(f=>f.glyphCount>0)) assert.equal(visual.probe.frames.length,10) const collapsing=visual.probe.frames.filter(f=>f.step_id==='collapse') const heights=collapsing.map(f=>f.regions[0].bounds?.[3]) assert.ok(heights.some(h=>h>0),'Must sample an intermediate animation frame') assert.equal(heights.at(-1),0) assert.ok(collapsing.every((f,i)=>i===0||f.at_ms>collapsing[i-1].at_ms)) assert.ok(visual.probe.restoration.some(r=>r.kind==='click'&&r.status==='completed')) assert.ok(visual.probe.restoration.some(r=>r.kind==='viewport'&&r.status==='completed')) for(const [i,frame] of visual.probe.frames.entries())await fs.writeFile(path.join(root,`frame-${String(i).padStart(3,'0')}.png`),Buffer.from(frame.artifact.data_base64,'base64')) await delay(300) const after=normalized(await cli(['inspect-electron-page',endpoint,target.target_id,'--include-visuals'])) assert.equal(after.visuals.css_viewport.width,1000) const panel=after.visuals.layout.documents[0].nodes.find(n=>n.attributes.id==='panel') assert.equal(panel.bounds[3],120) const typed=normalized(await cli(['inspect-electron-page',endpoint,target.target_id,'--include-visuals','--visual-probe',JSON.stringify({actions:[{kind:'text',step_id:'type-query',selector:'#editable',text:'腾讯 Tencent',frames:1}]})])) assert.equal(typed.visuals.probe.status,'completed') const typedAfter=normalized(await cli(['inspect-electron-page',endpoint,target.target_id,'--include-visuals','--visual-probe',JSON.stringify({include_message_content:true})])) await fs.writeFile(path.join(root,'text-input-observation.json'),JSON.stringify({typed,typedAfter},null,2)) assert.ok(typedAfter.visuals.message_content.nodes.some(node=>node.node_name==='#text'&&node.text==='腾讯 Tencent'),'Native text input fires input handler and renders the query') const cleared=normalized(await cli(['inspect-electron-page',endpoint,target.target_id,'--include-visuals','--visual-probe',JSON.stringify({actions:[{kind:'text',step_id:'clear-query',selector:'#editable',text:'',frames:1}]})])) assert.equal(cleared.visuals.probe.status,'completed') await fs.writeFile(path.join(root,'text-input-capture.json'),JSON.stringify({typed,typedAfter,cleared},null,2)) await fs.writeFile(path.join(root,'verification.json'),JSON.stringify({kind:'ycode-self-test',passed:true,originalApplicationParity:false,fontCategories:visual.font_samples.map(s=>s.category),fonts:visual.font_samples.map(s=>({category:s.category,platformFonts:s.platformFonts})),heights,frameTimes:collapsing.map(f=>f.at_ms),restoration:visual.probe.restoration,after:{width:after.visuals.css_viewport.width,panelHeight:panel.bounds[3]}},null,2)) console.log('REA live public CLI passed: 5 actual glyph samples, 10 PNG frames, measured collapse motion, toggle and viewport restoration (owned fixture only)') }finally{child.kill()}