23 lines
7.9 KiB
JavaScript
23 lines
7.9 KiB
JavaScript
const fs=require('node:fs/promises'),path=require('node:path'),assert=require('node:assert/strict');
|
|
if(!process.versions.electron){(async()=>{
|
|
const dir=await fs.mkdtemp(path.resolve('.tmp/b04-app-'));for(const n of ['data','home','workspace'])await fs.mkdir(path.join(dir,n));const now=Date.now();const fixture=await fs.readFile('scripts/b04-image-lifecycle-fixture.tsx','utf8');const pixel=fixture.match(/const pixel = '([^']+)'/)[1];const source='data:image/png;base64,'+Buffer.concat([Buffer.from(pixel,'base64'),Buffer.alloc(18000)]).toString('base64');
|
|
const model={id:'b04-model',name:'B04 Offline',providerId:'custom',model:'fixture',protocol:'anthropic',authMode:'none',baseUrl:'http://127.0.0.1:1'};await fs.writeFile(path.join(dir,'data/models.json'),JSON.stringify({version:1,models:[model],selectedModelId:model.id}));await fs.writeFile(path.join(dir,'data/workspaces.json'),JSON.stringify({selectedWorkspaceId:'qa',workspaces:[{id:'qa',name:'QA Workspace',root:path.join(dir,'workspace'),createdAt:now}]}));
|
|
const sessions=['a','b'].map((x,i)=>({id:'b04-'+x,title:'Synthetic image '+x,cwd:path.join(dir,'workspace'),workspaceId:'qa',modelId:model.id,agentMode:'craft',permissionMode:'default-sandbox',createdAt:now-i,updatedAt:now-i,messages:[{id:x+'-u',role:'user',content:'Inspect synthetic image '+x,status:'done',createdAt:now-5000},{id:x+'-a',role:'assistant',status:'done',content:'',createdAt:now-4000,turnDurationMs:2000,parts:[{type:'reasoning',id:x+'-r',text:'Synthetic reasoning before image'},{type:'text',text:''}]}]}));
|
|
await fs.writeFile(path.join(dir,'data/sessions.json'),JSON.stringify({sessions}));const env={...process.env,YCODE_USER_DATA_DIR:path.join(dir,'data'),YCODE_QA_DIRECTORY:dir,YCODE_SKIP_KERNEL_DATA_RESET:'1',YCODE_DISABLE_GPU:'1',YCODE_DISABLE_SYSTEM_INTEGRATION:'1',B04_APP_DIR:dir,HOME:path.join(dir,'home'),USERPROFILE:path.join(dir,'home')};delete env.ELECTRON_RUN_AS_NODE;delete env.ELECTRON_RENDERER_URL;
|
|
const child=require('node:child_process').spawn(require('electron'),[__filename],{env,windowsHide:true,stdio:['ignore','pipe','pipe']});let log='';child.stdout.on('data',x=>log+=x);child.stderr.on('data',x=>log+=x);const timer=setTimeout(()=>child.kill(),70000);const exit=await new Promise(r=>child.on('exit',r));clearTimeout(timer);await fs.writeFile(path.join(dir,'process.txt'),log);console.log(JSON.stringify({directory:dir,exitCode:exit}));if(exit!==0){console.error(log.slice(-6000));process.exitCode=1}
|
|
})().catch(e=>{console.error(e);process.exitCode=1})}else{
|
|
const {app,BrowserWindow,dialog}=require('electron'),{pathToFileURL}=require('node:url');const dir=process.env.B04_APP_DIR;app.disableHardwareAcceleration();app.setPath('home',path.join(dir,'home'));app.setPath('downloads',dir);let saveDialogs=0;dialog.showSaveDialog=async()=>{saveDialogs++;return {canceled:false,filePath:path.join(dir,'downloaded.png')}};let win,stage='boot';const errors=[],checks=[],traces=[];const pause=ms=>new Promise(r=>setTimeout(r,ms));const read=s=>win.webContents.executeJavaScript(s);async function wait(s){for(let i=0;i<300;i++){const x=await read(s);if(x)return x;await pause(30)}throw Error('Timeout '+s)}function check(name,value){checks.push({name,pass:!!value});assert.ok(value,name)}async function shot(n){await pause(100);await fs.writeFile(path.join(dir,n+'.png'),(await win.webContents.capturePage()).toPNG())}
|
|
const timer=setTimeout(()=>{console.error('Watchdog',stage);app.exit(1)},60000);
|
|
;(async()=>{await import(pathToFileURL(path.resolve('out/main/index.mjs')).href);await app.whenReady();win=BrowserWindow.getAllWindows()[0];win.webContents.setBackgroundThrottling(false);win.setSize(1336,902);win.showInactive();win.webContents.on('console-message',(_e,l,m)=>{if(l>=3)errors.push(m)});await wait('!!document.querySelector(".cr-collapse--completed > .cr-collapse__header")');await read(`window.__b04Mutations=[];window.__b04Error=[];window.addEventListener('error',e=>window.__b04Error.push(e.message));window.__b04Obs=new MutationObserver(records=>{for(const r of records){for(const n of r.addedNodes){const t=n.textContent||'';if(t.includes('base64,')||t.includes('data:image/'))window.__b04Mutations.push({textLength:t.length})}}});window.__b04Obs.observe(document.querySelector('.chat-thread'),{childList:true,subtree:true,characterData:true});true`);
|
|
for(const [index,id]of ['b04-a','b04-b','b04-a'].entries()){
|
|
stage=id;const selector=JSON.stringify('[data-session-id="'+id+'"]');await read('document.querySelector('+selector+').click()');await wait('!document.querySelector(".yc-session-skeleton") && !!document.querySelector(".cr-collapse--completed > .cr-collapse__header")');await wait('[...document.querySelectorAll("img.markdown-content__image")].some(x=>x.complete&&x.naturalWidth===320)');
|
|
for(let cycle=0;cycle<4;cycle++){
|
|
const trace=await read(`new Promise(resolve=>{const samples=[];const take=label=>{const region=document.querySelector('.msg--assistant')||document.querySelector('.chat-thread');const text=region.textContent||'';const img=region.querySelector('img.markdown-content__image');samples.push({label,time:performance.now(),height:region.getBoundingClientRect().height,textLength:text.length,uriText:text.includes('base64,')||text.includes('data:image/'),imageHeight:img?.getBoundingClientRect().height||0,decoded:!!img?.complete&&img.naturalWidth===320})};take('before');document.querySelector('.cr-collapse--completed > .cr-collapse__header').click();take('click');let left=12;const frame=()=>{take('frame-'+(12-left));if(--left)requestAnimationFrame(frame);else resolve(samples)};requestAnimationFrame(frame)})`);traces.push({id,index,cycle,samples:trace});check(id+' cycle'+cycle+' no URI text',trace.every(x=>!x.uriText&&x.textLength<1000));check(id+' cycle'+cycle+' no giant height',trace.every(x=>x.height>0&&x.height<2000));check(id+' cycle'+cycle+' decoded image in frames',trace.some(x=>x.decoded&&x.imageHeight>0));if(cycle===0)await shot('session-'+index+'-expanded');
|
|
}
|
|
}
|
|
check('MutationObserver saw no URI text nodes',!(await read('window.__b04Mutations.length')));check('zero captured renderer errors',errors.length===0&&!(await read('window.__b04Error.length')));
|
|
stage='preview';await read('document.querySelector("img.markdown-content__image").click()');await wait('!!document.querySelector(".wb-image-preview")');check('production image preview opens',true);await wait('document.querySelector(".wb-image-preview img")?.naturalWidth===320');check('preview image decodes',true);await shot('image-preview');await read('document.querySelector(".wb-image-preview [aria-label=下载图片]").click()');let downloaded;for(let i=0;i<200;i++){try{downloaded=await fs.readFile(path.join(dir,'downloaded.png'));if(downloaded.length)break}catch{}await pause(20)}const fixtureText=await fs.readFile('scripts/b04-image-lifecycle-fixture.tsx','utf8');const pixel=fixtureText.match(/const pixel = '([^']+)'/)[1];const expected=Buffer.concat([Buffer.from(pixel,'base64'),Buffer.alloc(18000)]);check('actual image download bytes preserved',downloaded?.equals(expected));check('download traverses native save dialog adapter',saveDialogs===1);await read('document.querySelector(".wb-image-preview [aria-label=关闭预览]").click()');await wait('!document.querySelector(".wb-image-preview")');check('image preview closes',true);
|
|
const heights=traces.flatMap(t=>t.samples.map(x=>x.height));await fs.writeFile(path.join(dir,'report.json'),JSON.stringify({codeSha:'342820690f644fb1cb3b0a76b143dbcd41aac8c4',scope:'Unmodified built production main/preload/renderer with synthetic persisted history; 3 session selections, 12 toggles, consecutive real rAF frames; background throttling disabled only for QA window; no model requests',checks,traces,minHeight:Math.min(...heights),maxHeight:Math.max(...heights),errors},null,2));clearTimeout(timer);app.exit(0)
|
|
})().catch(async e=>{console.error(stage,e);if(win)await shot('failure').catch(()=>{});await fs.writeFile(path.join(dir,'failure.json'),JSON.stringify({stage,message:String(e),checks,errors},null,2));app.exit(1)})
|
|
}
|