37 lines
5.9 KiB
JavaScript
37 lines
5.9 KiB
JavaScript
const {readFile,mkdir,writeFile}=require('node:fs/promises');
|
|
const {createHmac,randomBytes}=require('node:crypto');
|
|
const {chromium}=require('playwright-core');
|
|
const assert=require('node:assert/strict');
|
|
(async()=>{
|
|
const base='http://8.211.154.134:18131',origin=new URL(base).origin,dir='/tmp/studio-library/visual';await mkdir(dir,{recursive:true});
|
|
const config=JSON.parse(await readFile(process.env.AUTH_CONFIG_FILE,'utf8'));
|
|
const now=Math.floor(Date.now()/1000),part=o=>Buffer.from(JSON.stringify(o)).toString('base64url');
|
|
const content=part({alg:'HS256',typ:'JWT'})+'.'+part({iss:'prompt',aud:'prompt-web',sub:'shared',iat:now,exp:now+1200,jti:randomBytes(16).toString('hex')});
|
|
const token=content+'.'+createHmac('sha256',Buffer.from(config.jwtKey,'hex')).update(content).digest('base64url');
|
|
const browser=await chromium.launch({executablePath:'/usr/bin/chromium',headless:true,args:['--no-sandbox','--disable-dev-shm-usage']});
|
|
const context=await browser.newContext({viewport:{width:1100,height:1000},deviceScaleFactor:1});
|
|
await context.route('**/*',async route=>{const u=new URL(route.request().url());if(u.origin===origin)return route.continue({headers:{...route.request().headers(),Authorization:'Bearer '+token}});if(['data:','blob:'].includes(u.protocol))return route.continue();return route.abort()});
|
|
const page=await context.newPage(),errors=[],images=[];
|
|
page.on('pageerror',error=>errors.push({url:page.url(),message:error.message}));
|
|
page.on('response',async response=>{if(response.request().resourceType()==='image'){const url=new URL(response.url());images.push({path:url.pathname+url.search,status:response.status(),type:response.headers()['content-type'],bytes:(await response.body().catch(()=>Buffer.alloc(0))).length})}});
|
|
const metrics=process.argv.includes('--after-layouts')?JSON.parse(await readFile(dir+'/metrics.json','utf8')).metrics:[];
|
|
for(let i=1;i<=27&&!process.argv.includes('--after-layouts');i++){
|
|
const id='ref-'+String(i).padStart(2,'0');await page.goto(base+'/studio/layouts/'+id,{waitUntil:'networkidle'});await page.locator('article.native-layout').screenshot({path:dir+'/'+id+'.png',style:'.page-tools{visibility:hidden}'});
|
|
const m=await page.locator('article.native-layout').evaluate(el=>({id:el.dataset.layoutId,elements:el.querySelectorAll('[data-element]').length,width:el.clientWidth,height:el.clientHeight,overflow:[...el.querySelectorAll('.native-copy')].filter(e=>e.scrollHeight>e.clientHeight+2||e.scrollWidth>e.clientWidth+2).map(e=>e.closest('[data-element]').dataset.element)}));metrics.push(m);await writeFile(dir+'/metrics.json',JSON.stringify({metrics,images,errors},null,2));
|
|
}
|
|
if(!process.argv.includes('--after-layouts')&&!process.argv.includes('--diagnose'))assert.deepEqual(metrics.filter(m=>m.overflow.length),[],'Default copy must fit every native slot');
|
|
await page.goto(base+'/studio',{waitUntil:'networkidle'});assert.equal(await page.locator('.library-item').count(),27);await page.screenshot({path:dir+'/catalog-desktop.png',fullPage:true});
|
|
const verify=JSON.parse(await readFile('/tmp/studio-library/verification.json','utf8'));
|
|
for(const d of verify.results){await page.goto(base+d.url,{waitUntil:'networkidle'});assert.equal(await page.locator('img').count(),1);const current=await page.locator('img').evaluate(img=>({src:img.currentSrc,complete:img.complete,width:img.naturalWidth}));assert.ok(current.complete&¤t.width>0);assert.equal(new URL(current.src).pathname,'/api/preview');await page.locator('article.native-layout').screenshot({path:dir+'/'+d.layoutId+'-saved.png',style:'.page-tools{visibility:hidden}'});
|
|
await page.getByRole('button',{name:'编辑页面',exact:true}).click();assert.equal(await page.locator('.workbench-top').count(),1);await page.screenshot({path:dir+'/'+d.layoutId+'-editor.png'});
|
|
}
|
|
for(const slug of ['learning-skills-template','layout-selection']){await page.goto(base+'/studio/ideas/'+slug,{waitUntil:'networkidle'});await page.screenshot({path:dir+'/tool-'+slug+'.png',fullPage:true})}
|
|
await page.setViewportSize({width:390,height:844});await page.goto(base+'/studio',{waitUntil:'networkidle'});assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));await page.screenshot({path:dir+'/catalog-mobile.png',fullPage:true});
|
|
await page.goto(base+verify.results[0].url,{waitUntil:'networkidle'});await page.screenshot({path:dir+'/standard-mobile.png',fullPage:true});
|
|
await page.goto(base+'/categories?category='+encodeURIComponent('文档入口'),{waitUntil:'domcontentloaded',timeout:60000});await page.getByRole('navigation',{name:'系统标签分类',exact:true}).waitFor({state:'visible'});assert.equal(await page.locator('.discovery-card').count(),3);await page.waitForFunction(()=>Array.from(document.querySelectorAll('.discovery-card img')).every(img=>img.complete&&img.naturalWidth>0),null,{timeout:60000});await page.screenshot({path:dir+'/document-entries.png',fullPage:true});
|
|
await writeFile(dir+'/metrics.json',JSON.stringify({metrics,images,errors},null,2));
|
|
assert.equal(images.filter(r=>new URL(r.path,base).pathname.startsWith('/api/media/')).length,0,'Display must never fetch originals');if(!process.argv.includes('--diagnose'))assert.equal(errors.length,0,JSON.stringify(errors));for(const image of images){if(new URL(image.path,base).pathname==='/brand-mark.svg'){assert.equal(image.status,200);assert.match(image.type,/image\/svg\+xml/);continue;}assert.equal(new URL(image.path,base).pathname,'/api/preview');assert.equal(image.status,200);assert.equal(image.type,'image/webp');assert.ok(image.bytes>0&&image.bytes<=220000)}
|
|
await writeFile(dir+'/metrics.json',JSON.stringify({metrics,images,errors},null,2));await browser.close();
|
|
console.log(JSON.stringify({diagnostic:process.argv.includes('--diagnose'),pages:27,errors:errors.length,displayImages:images.length,originalImageRequests:0,overflow:metrics.filter(m=>m.overflow.length)}));
|
|
})().catch(e=>{console.error(e.message);process.exit(1)});
|