import {createRequire} from 'node:module'; import {writeFileSync,mkdirSync} from 'node:fs'; const require=createRequire('C:/kefu/admin-web/package.json'); const {chromium}=require('playwright'); const root='C:/kefu/deploy/ui-performance-20260916';mkdirSync(`${root}/qa`,{recursive:true}); const browser=await chromium.launch({executablePath:'C:/Program Files/Google/Chrome/Application/chrome.exe',headless:true}); const cases=[['1080',1630,888,1],['4k-200',1630,888,2],['6k-300',1630,888,3],['4k-300',1280,680,3],['1080-200',960,500,2],['1080-300',640,330,3],['fractional',1200,700,1.25]]; const pages=['auto','queue','archive','ai','automation','logs','wechatdb','system']; const results=[],errors=[]; try{ for(const [name,width,height,dpr] of cases){ const context=await browser.newContext({viewport:{width,height},deviceScaleFactor:dpr}); await context.route('**/*',route=>route.request().url().startsWith('file:')?route.continue():route.abort()); const page=await context.newPage();page.on('pageerror',e=>errors.push({name,error:e.message})); await page.goto(`file:///${root}/payload/assets/ui/zhenyang-ai-console.html`); await page.waitForTimeout(500); for(const view of pages){ await page.evaluate(view=>{STATE.ready=true;STATE.demo=false;STATE.view=view;render(view);fit();},view); await page.waitForTimeout(90); const result=await page.evaluate(()=>{ const main=document.getElementById('main'),stage=document.getElementById('stage'); const nav=document.querySelector('[data-view="auto"]'),rect=nav.getBoundingClientRect(); return {view:STATE.view,layout:stage.dataset.layout,zoom:Number(stage.style.zoom),dpr:devicePixelRatio, mainWidth:main.clientWidth,contentWidth:main.scrollWidth,font:getComputedStyle(main).fontSize, navWidth:rect.width,navHeight:rect.height,mainHeight:main.clientHeight,contentHeight:main.scrollHeight, overflow:[...main.querySelectorAll('*')].filter(e=>e.getBoundingClientRect().right>main.getBoundingClientRect().right+2&&e.clientWidth>0).slice(0,8).map(e=>({tag:e.tagName,cls:e.className,text:e.textContent.slice(0,45)}))}; }); results.push({name,...result}); if(view==='auto'||(name==='1080-300'&&['ai','system','automation'].includes(view)))await page.screenshot({path:`${root}/qa/${name}-${view}.png`}); if(result.contentWidth>result.mainWidth+2)console.log(JSON.stringify({overflow:name,...result})); } if(name==='1080'){ for(const pref of ['200%','300%','自动']){ await page.evaluate(pref=>{STATE.system={...(STATE.system||{}),scale:pref};render('system');fit();},pref); const value=await page.locator('#stage').evaluate(e=>Number(e.style.zoom)); if(Math.abs(value-({'200%':1.7,'300%':2.55,'自动':.85}[pref]))>.001)throw Error('Manual scaling failed'); } await page.setViewportSize({width:780,height:400});await page.waitForTimeout(150); if(await page.locator('#stage').evaluate(e=>Number(e.style.zoom))!==.85)throw Error('Resize changed font density'); const settings=page.locator('[data-view="system"]');await settings.click(); await page.getByRole('heading',{name:'系统设置',exact:true}).waitFor(); } await context.close(); } writeFileSync(`${root}/qa/results.json`,JSON.stringify({results,errors},null,2)); const bad=results.filter(r=>r.contentWidth>r.mainWidth+2); console.log(JSON.stringify({checks:results.length,overflow:bad.length,pageErrors:errors})); if(bad.length||errors.length)process.exitCode=1; }finally{await browser.close();}