chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,87 @@
|
||||
// Only YCode is automated. Original measurements are fixed REA artifacts, never direct original CDP.
|
||||
const fs = require('node:fs'), path = require('node:path'), assert = require('node:assert/strict')
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
app.disableHardwareAcceleration()
|
||||
app.commandLine.appendSwitch('use-angle', 'swiftshader'); app.commandLine.appendSwitch('disable-gpu-compositing')
|
||||
const root = path.resolve(__dirname, '..'), output = path.join(root, '.tmp/source-select-native-20261016')
|
||||
assert.ok(output.startsWith(path.join(root, '.tmp') + path.sep)); fs.mkdirSync(output, { recursive: true })
|
||||
app.setPath('userData', path.join(output, 'user-data'))
|
||||
const modulePath = name => JSON.stringify(path.join(root, name).replaceAll('\\', '/'))
|
||||
fs.writeFileSync(path.join(output, 'fixture.tsx'), `import React,{useState} from 'react';import{createRoot}from'react-dom/client';
|
||||
import Panel from ${modulePath('src/renderer/src/components/ModelSettingsPanel')};
|
||||
import Select from ${modulePath('src/renderer/src/components/source-select/SourceSelect')};
|
||||
import ${modulePath('src/renderer/src/styles/tokens.css')};
|
||||
import ${modulePath('src/renderer/src/styles/app.css')};
|
||||
function Fixture(){const[models,setModels]=useState([]),[standalone,setStandalone]=useState(false),[value,setValue]=useState('first');window.__selectQA??={tests:[],saves:[],deletes:[]};return <><Panel models={models} onTest={async input=>{window.__selectQA.tests.push(input);return{success:true,message:'Local fixture test completed'}}} onSave={async input=>{window.__selectQA.saves.push(input);setModels(list=>[...list.filter(m=>m.id!==input.id),{...input,hasApiKey:!!input.apiKey}])}} onDelete={async id=>{window.__selectQA.deletes.push(id);setModels(list=>list.filter(m=>m.id!==id))}}/><button id="legacy" style={{position:'fixed',bottom:0,left:0}} onClick={()=>setModels([{id:'legacy',name:'Existing noncandidate',providerId:'tencent-token-plan',model:'legacy-model-id',baseUrl:'https://example.invalid/v1',authMode:'api-key',hasApiKey:true}])}>load existing profile</button><button id="standalone" style={{position:'fixed',bottom:0,right:0}} onClick={()=>setStandalone(v=>!v)}>standalone</button>{standalone&&<div id="portal-host" style={{position:'fixed',left:700,top:620,width:180}}><Select ariaLabel="portal test" value={value} onChange={setValue} portalRoot="body" fullWidth options={[{value:'first',label:'First'},{value:'disabled',label:'Disabled',disabled:true},{value:'last',label:'Last'}]}/></div>}</>};createRoot(document.getElementById('root')).render(<Fixture/>);`)
|
||||
const checks = [], snapshots = {}, errors = []
|
||||
function check(name, actual, expected) { assert.deepEqual(actual, expected, name); checks.push({ name, pass: true }) }
|
||||
app.whenReady().then(async () => {
|
||||
await require('esbuild').build({ entryPoints: [path.join(output, 'fixture.tsx')], bundle: true, outfile: path.join(output, 'fixture.js'), jsx: 'automatic', platform: 'browser', format: 'iife', loader: { '.png': 'file', '.svg': 'file' }, 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{margin:0;width:100vw;height:100vh;background:white}</style></head><body><div id="root"></div><script src="fixture.js"></script></body></html>')
|
||||
const win = new BrowserWindow({ width: 900, height: 680, useContentSize: true, frame: false, show: false, webPreferences: { contextIsolation: true, nodeIntegration: false, backgroundThrottling: false } })
|
||||
win.webContents.on('console-message', (_e, level, message) => { if (level === 3) errors.push(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)
|
||||
const capture = async name => fs.writeFileSync(path.join(output, name + '.png'), Buffer.from((await cdp('Page.captureScreenshot', { format: 'png', fromSurface: true })).data, 'base64'))
|
||||
const settle = async () => { await cdp('Page.captureScreenshot', { format: 'png', fromSurface: true }); await new Promise(resolve => setTimeout(resolve, 180)) }
|
||||
const click = async selector => { const point = await read(`(()=>{const e=document.querySelector(${JSON.stringify(selector)});if(!e)throw Error('Missing '+${JSON.stringify(selector)});const r=e.getBoundingClientRect();return{x:r.x+r.width/2,y:r.y+r.height/2}})()`); for (const type of ['mousePressed', 'mouseReleased']) await cdp('Input.dispatchMouseEvent', { type, ...point, button: 'left', clickCount: 1 }); await settle() }
|
||||
const key = async key => { for (const type of ['keyDown', 'keyUp']) await cdp('Input.dispatchKeyEvent', { type, key, code: key === ' ' ? 'Space' : key, windowsVirtualKeyCode: key === 'Escape' ? 27 : key === 'Enter' ? 13 : key === 'Tab' ? 9 : 65 }) }
|
||||
const type = async (selector, text) => { await click(selector); await read(`document.querySelector(${JSON.stringify(selector)}).select()`); await cdp('Input.insertText', { text }); await settle() }
|
||||
const bounds = selector => read(`(()=>{const r=document.querySelector(${JSON.stringify(selector)}).getBoundingClientRect();return[r.x,r.y,r.width,r.height]})()`)
|
||||
const measure = () => read(`Array.from(document.querySelectorAll('.wb-select,.wb-select-popover,.wb-select__panel,.wb-select__search,.wb-select__list,.wb-select__group,.wb-select__group-label,.wb-select__option,.wb-input-wrapper,.wb-input__prefix,.wb-input__inner,.wb-input__clear,.wb-select__option-check,.models-settings-panel__editor-body'),e=>{const r=e.getBoundingClientRect(),s=getComputedStyle(e);return{class:e.className,text:e.textContent,bounds:[r.x,r.y,r.width,r.height],styles:{font:s.font,fontFamily:s.fontFamily,color:s.color,background:s.backgroundColor,padding:s.padding,gap:s.gap}}})`)
|
||||
try {
|
||||
await settle(); await click('.models-settings-panel__add-button'); await click('.models-settings-panel__provider-select')
|
||||
snapshots.provider = await measure(); await capture('provider')
|
||||
check('provider popup geometry matches fixed REA', await bounds('.wb-select-popover'), [154,176,592,306])
|
||||
check('provider trigger geometry matches fixed REA', await bounds('.models-settings-panel__provider-select'), [154,136,592,32])
|
||||
check('provider inline popup belongs to editor body', await read('!!document.querySelector(".models-settings-panel__editor-body .wb-select-popover")'), true)
|
||||
check('provider groups have source DOM', await read('document.querySelectorAll(".wb-select__group-label").length'), 4)
|
||||
check('all provider labels have real source vendor mark', await read('Array.from(document.querySelectorAll(".models-settings-panel__provider-option"),el=>!!el.querySelector("svg,img")).every(Boolean)'), true)
|
||||
await type('.wb-select__search input', 'CUSTOM'); await capture('provider-custom-query'); check('source filter matches value and case-insensitive search', await read('document.querySelectorAll(".wb-select__option").length'), 1)
|
||||
check('source filtered provider is custom', await read('document.querySelector(".wb-select__option").textContent'), '自定义')
|
||||
await click('.wb-input__clear'); check('source input clear resets option list', await read('document.querySelectorAll(".wb-select__option").length'), 13)
|
||||
await type('.wb-select__search input', 'NO_SUCH_PROVIDER_1'); await capture('provider-empty-query'); check('source empty label', await read('document.querySelector(".wb-select__empty").textContent'), 'No options')
|
||||
await key('Enter'); check('empty search cannot invent a provider', await read('document.querySelector(".models-settings-panel__provider-select").textContent.includes("NO_SUCH")'), false)
|
||||
await key('Escape'); await settle(); check('first Escape closes popup only', await read('!!document.querySelector(".models-settings-panel__editor")&&!document.querySelector(".wb-select-popover")'), true)
|
||||
await click('.models-settings-panel__model-select'); snapshots.model = await measure(); await capture('model')
|
||||
check('model popup geometry matches fixed REA clip boundary', await bounds('.wb-select-popover'), [154,344,592,216])
|
||||
check('model popup list clipped to source height', (await bounds('.wb-select__list'))[3], 166)
|
||||
await type('.wb-select__search input', 'NO_SUCH_MODEL_1'); await key('Enter'); check('empty model search has no allowCreate branch', await read('document.querySelector(".wb-select__empty").textContent'), 'No options')
|
||||
await key('Escape'); await settle(); await click('.models-settings-panel__provider-select'); await type('.wb-select__search input', 'custom')
|
||||
await read('document.querySelector(".wb-select__option").focus()'); await key('Enter'); await settle()
|
||||
check('keyboard Enter selects real custom provider', await read('!!document.querySelector("input[aria-label=模型名称]")'), true)
|
||||
await type('input[aria-label="接口地址"]', 'http://127.0.0.1:9999/v1'); await type('input[aria-label="API Key"]', 'local-key'); await type('input[aria-label="模型名称"]', 'ui-source-model')
|
||||
await click('.models-settings-panel__editor-test-connection'); check('test callback receives free custom input without saving', await read('[window.__selectQA.tests.at(-1).model,window.__selectQA.saves.length]'), ['ui-source-model',0])
|
||||
await click('.models-settings-panel__editor-save'); check('save callback receives custom model', await read('window.__selectQA.saves.at(-1).model'), 'ui-source-model')
|
||||
await click('button[aria-label="编辑"]'); await type('input[aria-label="模型名称"]', 'ui-source-model-edited'); await click('.models-settings-panel__editor-save')
|
||||
check('edit-save callback is real component state', await read('window.__selectQA.saves.at(-1).model'), 'ui-source-model-edited')
|
||||
await click('button[aria-label="删除"]'); await click('.models-settings-panel__delete-action'); check('delete callback removes own model', await read('window.__selectQA.deletes.length'), 1)
|
||||
await click('#legacy'); await click('button[aria-label="编辑"]')
|
||||
check('current noncandidate model remains trigger label', await read('document.querySelector(".models-settings-panel__model-select").textContent'), 'legacy-model-id')
|
||||
await click('.models-settings-panel__model-select')
|
||||
check('current noncandidate is injected once', await read('Array.from(document.querySelectorAll(".wb-select__option"),e=>e.textContent).filter(t=>t==="legacy-model-id").length'), 1)
|
||||
check('injected model is selected', await read('document.querySelector(".wb-select__option--single-selected").textContent'), 'legacy-model-id')
|
||||
await key('Escape'); await settle(); await click('.models-settings-panel__provider-select'); await type('.wb-select__search input','glm')
|
||||
await read('Array.from(document.querySelectorAll(".wb-select__option")).at(-1).focus()'); await key('Enter'); await settle()
|
||||
await click('.models-settings-panel__model-select')
|
||||
check('six or fewer source candidate models hide search', await read('document.querySelector(".wb-select__search")===null'), true)
|
||||
await key('Escape'); await settle(); await click('.models-settings-panel__editor-cancel')
|
||||
await click('#standalone'); await click('#portal-host .wb-select'); snapshots.portal = await measure(); await capture('portal')
|
||||
check('explicit body portal escapes host', await read('!document.querySelector("#portal-host .wb-select-popover")&&!!document.querySelector("body>.source-select-host .wb-select-popover")'), true)
|
||||
check('edge popup flips above trigger', await read('document.querySelector(".wb-select-popover").classList.contains("wb-popover--placement-top")'), true)
|
||||
check('disabled option is skipped by tab index', await read('document.querySelector(".wb-select__option--disabled").tabIndex'), -1)
|
||||
await read('document.querySelectorAll(".wb-select__option")[2].focus()'); await key(' '); await settle()
|
||||
check('keyboard Space selects option', await read('document.querySelector("#portal-host .wb-select").textContent'), 'Last')
|
||||
await click('#portal-host .wb-select')
|
||||
await read('document.querySelectorAll(".wb-select__option")[0].querySelector("span").style.maxWidth="16px"')
|
||||
await settle()
|
||||
const hoverPoint = await read('(()=>{const r=document.querySelectorAll(".wb-select__option")[0].querySelector("span").getBoundingClientRect();return{x:r.x+8,y:r.y+r.height/2}})()')
|
||||
await cdp('Input.dispatchMouseEvent',{type:'mouseMoved',...hoverPoint}); await settle()
|
||||
check('truncated source label mounts tooltip and arrow',await read('!!document.querySelector(".wb-tooltip__content")&&!!document.querySelector(".wb-tooltip__arrow")'),true)
|
||||
check('source tooltip z-index token is restored',await read('getComputedStyle(document.querySelector(".wb-tooltip")).zIndex'),'10200')
|
||||
await key('Escape'); await settle()
|
||||
check('renderer has no errors', errors, [])
|
||||
fs.writeFileSync(path.join(output,'report.json'),JSON.stringify({result:'PASS',scope:'YCode component native Electron fixture; controlled callbacks for CRUD, no backend or original cloud claim',checks,snapshots},null,2))
|
||||
console.log('SOURCE_SELECT_PASS '+checks.length); win.destroy(); app.quit()
|
||||
} catch(error) { await capture('failure').catch(()=>{}); fs.writeFileSync(path.join(output,'report.json'),JSON.stringify({result:'FAIL',error:error.stack,checks,snapshots,errors},null,2)); throw error }
|
||||
}).catch(error=>{console.error(error);app.exit(1)})
|
||||
Reference in new issue
Block a user