chore: establish YCode replication baseline and TODO

This commit is contained in:
xiaohan.yuan committed 2026-10-17 02:43:49 +08:00
commit 6dc59f89ba
1118 files changed
+196002

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
/** Actual Electron component fixture. Does not inspect or interact with original WorkBuddy. */
import fs from 'node:fs'
import path from 'node:path'
import assert from 'node:assert/strict'
import { createRequire } from 'node:module'
import { spawn } from 'node:child_process'
const require=createRequire(import.meta.url),module=require('electron'),electron=typeof module==='object'&&module.app?module:null
if(electron){electron.app.disableHardwareAcceleration();electron.app.commandLine.appendSwitch('use-angle','swiftshader');electron.app.commandLine.appendSwitch('disable-gpu-compositing')}
const root=path.resolve('.'),base=path.join(root,'.tmp')
if(!electron){const output=fs.mkdtempSync(path.join(base,'active-expert-native-'));const env={...process.env};delete env.ELECTRON_RUN_AS_NODE;delete env.ELECTRON_RENDERER_URL;const child=spawn(module,['--no-sandbox','--disable-gpu-sandbox',path.resolve('scripts/rea/test-active-expert-native.mjs'),output],{env,windowsHide:true,stdio:'inherit'});process.exitCode=await new Promise(resolve=>child.on('exit',code=>resolve(code??1)))}
else void(async()=>{
const {app,BrowserWindow}=electron,output=path.resolve(process.argv.at(-1));assert.ok(path.relative(base,output).startsWith('active-expert-native-')&&!path.relative(base,output).includes('..'));fs.mkdirSync(path.join(output,'data'));app.setPath('userData',path.join(output,'data'));await app.whenReady()
const location=file=>JSON.stringify(path.join(root,file).replaceAll('\\','/'))
fs.writeFileSync(path.join(output,'fixture.tsx'),`import React,{useState}from'react';import{createRoot}from'react-dom/client';import Active from ${location('src/renderer/src/capabilities/SourceActiveExpert')};import ${location('src/renderer/src/styles/tokens.css')};import ${location('src/renderer/src/styles/app.css')};import ${location('src/renderer/src/workbuddy/conversation/source-styles.css')};
window.qa={removed:0,bubbled:0,resolve:null,reject:null};const original={id:'algorithm',name:'小算',profession:'高级算法工程师',avatarUrl:'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><rect width="16" height="16" fill="red"/></svg>'};function App(){const[expert,setExpert]=useState(original),[disabled,setDisabled]=useState(false),[compact,setCompact]=useState(false),[preparing,setPreparing]=useState(undefined),[scope,setScope]=useState('A'),[mode,setMode]=useState('success');Object.assign(window.qa,{setExpert,setDisabled,setCompact,setPreparing,setScope,setMode,original});return<div className="chat-pane cr-theme" data-cr-theme="light" style={{height:'100vh'}}><button id="before" style={{position:'absolute',left:50,top:40}}>Before</button><div id="owner" onClick={()=>window.qa.bubbled++} style={{position:'absolute',left:100,top:100}}><Active expert={expert} disabled={disabled} compact={compact} preparingExpertId={preparing} scopeKey={scope} onRemove={async()=>{window.qa.removed++;if(mode==='defer')await new Promise((resolve,reject)=>{window.qa.resolve=resolve;window.qa.reject=reject});if(mode==='failure')throw Error('CONTROLLED_REMOVE_FAILURE');setExpert(null)}}/></div></div>}createRoot(document.getElementById('root')).render(<App/>);`)
await require('esbuild').build({entryPoints:[path.join(output,'fixture.tsx')],bundle:true,platform:'browser',format:'iife',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"><style>html,body,#root{margin:0;width:100%;height:100%}</style></head><body><div id="root"></div><script src="fixture.js"></script></body></html>')
const win=new BrowserWindow({width:1336,height:901,frame:false,useContentSize:true,show:false,webPreferences:{contextIsolation:true,nodeIntegration:false,backgroundThrottling:false}}),checks=[],errors=[],measurements={}
win.webContents.on('console-message',event=>{if(event.level==='error')errors.push(event.message)});await win.loadFile(path.join(output,'index.html'));win.webContents.debugger.attach('1.3');await win.webContents.debugger.sendCommand('Page.enable');win.showInactive()
const read=code=>win.webContents.executeJavaScript(code),cdp=(method,args)=>win.webContents.debugger.sendCommand(method,args),pause=ms=>new Promise(resolve=>setTimeout(resolve,ms))
const check=(name,actual,expected)=>{assert.deepEqual(actual,expected,name);checks.push(name)}
const wait=async code=>{for(let i=0;i<100;i++){if(await read(code))return;await pause(30)}throw Error('Timeout '+code)}
const bounds=selector=>read(`(()=>{const r=document.querySelector(${JSON.stringify(selector)}).getBoundingClientRect();return[r.x,r.y,r.width,r.height]})()`)
const point=async selector=>{const r=await bounds(selector);return{x:r[0]+r[2]/2,y:r[1]+r[3]/2}}
const move=async selector=>{await cdp('Input.dispatchMouseEvent',{type:'mouseMoved',...await point(selector)});await pause(250)}
const click=async selector=>{await move(selector);for(const type of ['mousePressed','mouseReleased'])await cdp('Input.dispatchMouseEvent',{type,...await point(selector),button:'left',clickCount:1});await pause(100)}
const key=async key=>{for(const type of ['keyDown','keyUp'])await cdp('Input.dispatchKeyEvent',{type,key,code:key,windowsVirtualKeyCode:key==='Enter'?13:9,...type==='keyDown'&&key==='Enter'?{text:'\r'}:{}});await pause(100)}
const update=async code=>{await read(code);await pause(100)}
const capture=async name=>fs.writeFileSync(path.join(output,name+'.png'),Buffer.from((await cdp('Page.captureScreenshot',{format:'png',fromSurface:true})).data,'base64'))
try{
await wait('!!document.querySelector(".cr-chip-base")')
check('source uses status role',await read('document.querySelector(".cr-chip-base").getAttribute("role")'),'status')
check('profession is selected label',await read('document.querySelector(".cr-chip-label").textContent'),'高级算法工程师')
check('no automatic host activation/removal on mount',await read('window.qa.removed'),0)
measurements.chip=await bounds('.cr-chip-base');check('source same-label chip geometry',measurements.chip,[100,100,127,32])
check('avatar16',(await bounds('.cr-expert-chip-avatar')).slice(2),[16,16]);check('label91x20',(await bounds('.cr-chip-label')).slice(2),[91,20])
const originalLayout=JSON.parse(fs.readFileSync('.tmp/rea-expert-usage-current-20261016/layout.json','utf8')).layout.documents.flatMap(document=>document.nodes)
for(const[selector,className,properties]of [['.cr-chip-base','cr-chip-base cr-chip-base--static',['gap','border-radius','padding-left','padding-right','transition-duration','background-color']],['.cr-expert-chip-avatar','cr-expert-chip-avatar',['font-size','font-weight','border-radius','overflow']],['.cr-chip-label','cr-chip-label',['font-size','font-weight','line-height','color','max-width','overflow']]]){
const source=originalLayout.find(node=>node.attributes?.class===className);assert(source,'Saved REA node '+className)
const actual=await read(`(()=>{const style=getComputedStyle(document.querySelector(${JSON.stringify(selector)}));return Object.fromEntries(${JSON.stringify(properties)}.map(key=>[key,style.getPropertyValue(key)]))})()`)
check('saved REA computed properties '+className,actual,Object.fromEntries(properties.map(key=>[key,source.styles[key]])))
}
check('close initially hidden',await read('getComputedStyle(document.querySelector(".cr-chip-close-btn")).display'),'none');await capture('normal')
await click('.cr-chip-label');check('label does not remove or summon',await read('window.qa.removed'),0)
check('hover swaps avatar for close',await read('[getComputedStyle(document.querySelector(".cr-chip-icon--default")).display,getComputedStyle(document.querySelector(".cr-chip-close-btn")).display]'),['none','flex']);await capture('hover')
const before=await read('window.qa.bubbled');await click('.cr-chip-close-btn');await wait('!document.querySelector(".cr-chip-base")');check('remove delegates once',await read('window.qa.removed'),1);check('remove stops event bubbling',await read('window.qa.bubbled'),before)
await update('window.qa.setExpert({...window.qa.original});window.qa.setMode("failure")');await move('.cr-chip-base');await click('.cr-chip-close-btn');await wait('!!document.querySelector(".cr-chip-label")');check('rejected real removal restores label',await read('document.querySelector(".cr-chip-label").textContent'),'高级算法工程师')
await update('window.qa.setDisabled(true)');check('host busy disables remove button',await read('document.querySelector(".cr-chip-close-btn").disabled'),true);await move('.cr-chip-base');check('disabled retains avatar',await read('getComputedStyle(document.querySelector(".cr-chip-icon--default")).display'),'flex');check('disabled cannot dispatch remove',await read('window.qa.removed'),2)
await update('window.qa.setDisabled(false);window.qa.setPreparing("algorithm")');check('preparing is exposed honestly',await read('document.querySelector(".cr-chip-base").getAttribute("aria-busy")'),'true');check('source spinner exists',await read('!!document.querySelector(".cr-expert-chip-avatar-spinner svg")'),true);await cdp('Input.dispatchMouseEvent',{type:'mouseMoved',x:10,y:10});await capture('preparing')
await update('window.qa.setPreparing(undefined);window.qa.setCompact(true)');check('compact source geometry',(await bounds('.cr-chip-base')).slice(2),[32,32]);check('compact hides label',await read('document.querySelector(".cr-chip-label")'),null);await move('.cr-chip-base');await wait('!!document.querySelector("[role=tooltip]")');check('compact tooltip retains profession',await read('document.querySelector("[role=tooltip]").textContent'),'高级算法工程师');await capture('compact-tooltip')
await update('window.qa.setCompact(false);window.qa.setExpert({id:"id",name:"Display name"})');check('no profession falls back to name',await read('document.querySelector(".cr-chip-label").textContent'),'Display name');check('missing avatar gets first seed character',await read('document.querySelector(".cr-expert-chip-avatar").textContent'),'D')
await update('window.qa.setExpert({id:"id",name:"id"})');check('incomplete hydration retains last display',await read('document.querySelector(".cr-chip-label").textContent'),'Display name');await update('window.qa.setExpert(null)');check('explicit null clears hydration cache',await read('!!document.querySelector(".cr-chip-base")'),false);await update('window.qa.setExpert({id:"unknown",name:"unknown"})');check('unknown rawid alone is hidden',await read('!!document.querySelector(".cr-chip-base")'),false)
await update('window.qa.setExpert({...window.qa.original,avatarUrl:"file:///missing-fixture-avatar.png"})');await wait('!document.querySelector(".cr-expert-chip-avatar img")');check('broken avatar falls back to profession seed',await read('document.querySelector(".cr-expert-chip-avatar").textContent'),'高')
await update('window.qa.setExpert({id:"team",name:"Team",profession:"团队",expertType:"team",assistantDisplayName:"Lead",assistantProfession:"Architect",assistantAvatarUrl:window.qa.original.avatarUrl})');check('team can use explicit assistant avatar',await read('document.querySelector(".cr-expert-chip-avatar img").getAttribute("src")'),await read('window.qa.original.avatarUrl'))
await update('window.qa.setExpert({...window.qa.original,profession:"这是一个很长很长很长很长的专家职业名称"})');check('long label is source capped140',(await bounds('.cr-chip-label'))[2],140);await move('.cr-chip-label');await wait('!!document.querySelector("[role=tooltip]")');check('truncation tooltip reveals full label',await read('document.querySelector("[role=tooltip]").textContent'),'这是一个很长很长很长很长的专家职业名称')
await update('window.qa.setExpert({...window.qa.original});window.qa.setMode("failure")');await move('.cr-chip-base');win.webContents.focus();await read('document.querySelector(".cr-chip-close-btn").focus()');check('keyboard target is real source close button',await read('document.activeElement.matches(".cr-chip-close-btn")'),true);await key('Enter');check('native keyboard removal invokes real host',await read('window.qa.removed'),3)
await update('window.qa.setMode("defer");window.qa.setExpert({...window.qa.original})');await move('.cr-chip-base');await click('.cr-chip-close-btn');check('pending clear projects null',await read('!!document.querySelector(".cr-chip-base")'),false);await update('window.qa.setExpert({id:"new",name:"New",profession:"New expert"})');await update('window.qa.reject(Error("STALE"))');check('stale rejected clear cannot restore old selection',await read('document.querySelector(".cr-chip-label").textContent'),'New expert')
await update('window.qa.setMode("defer");window.qa.setExpert({...window.qa.original})');await move('.cr-chip-base');await click('.cr-chip-close-btn');await update('window.qa.setScope("B");window.qa.setExpert({id:"B",name:"Owner B",profession:"Owner B"})');await update('window.qa.reject(Error("OLD_OWNER"))');check('old owner reject cannot mutate new owner',await read('document.querySelector(".cr-chip-label").textContent'),'Owner B')
check('no renderer exceptions',errors.filter(message=>!message.includes('ERR_FILE_NOT_FOUND')),[])
fs.writeFileSync(path.join(output,'report.json'),JSON.stringify({result:'PASS',checks,measurements,sourceEvidence:'ev_7e9604adb87a3d21570eb7a371001e48b3d316ee0388a03a97e2cb564d03aff7',scope:'Actual Electron component + typed host; source geometry independently matched, not whole-app pixel acceptance'},null,2));console.log(JSON.stringify({result:'PASS',checks:checks.length,output}));win.destroy();app.exit(0)
}catch(error){await capture('failure');fs.writeFileSync(path.join(output,'failure.json'),JSON.stringify({error:String(error),checks,errors,html:await read('document.body.innerHTML')},null,2));console.error(error);win.destroy();app.exit(1)}
})().catch(error=>{console.error(error);electron.app.exit(1)})