From 833a345b85fca17cc4ddb16d6f2e57f33ab81c20 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Wed, 28 Oct 2026 15:42:10 +0800 Subject: [PATCH] Make production acceptance checks retain evidence and clean test records --- scripts/verify-studio-library.mjs | 16 ++++++++++++++++ scripts/verify-studio-visual.cjs | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/verify-studio-library.mjs b/scripts/verify-studio-library.mjs index e14d76f..c0f2d5d 100644 --- a/scripts/verify-studio-library.mjs +++ b/scripts/verify-studio-library.mjs @@ -2,6 +2,20 @@ import assert from 'node:assert/strict'; import {writeFile} from 'node:fs/promises'; const base='http://8.211.154.134:18131'; async function api(path,body,method=body?'POST':'GET',status=200){const r=await fetch(base+path,{method,...(body?{headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}:{})});assert.equal(r.status,status,path);return r.json()} +if(process.argv.includes('--finalize')){ + const {results}=JSON.parse(await (await import('node:fs/promises')).readFile('/tmp/studio-library/verification.json','utf8')); + const canonical='8eb8c76500580f7a4b56ba3c',legacy='9ebd8f78d54b4b9db741b4e8'; + const entry=(await api('/api/notes/'+canonical)).entry; + for(const needle of ['standard-01','DELETE','revision','cropX','兼容工具原说明'])assert.ok(entry.editableMarkdown.includes(needle),needle); + for(const id of [canonical,legacy])assert.equal((await fetch(base+'/explore/'+id)).status,200); + const old=(await api('/api/notes/'+legacy)).entry;assert.ok(old.archived&&old.editableMarkdown.includes(canonical)); + const category=await api('/api/explore-feed?category='+encodeURIComponent('文档入口')); + assert.equal(category.total,3);assert.equal(category.nextCursor,null); + assert.deepEqual(category.entries.map(e=>e.id).sort(),['3c98946754c33309d73d5284','4ce3283fd98bb03a8fb39a19',canonical].sort()); + for(const item of results){const d=(await api('/api/studio/designs/'+item.id)).design;assert.ok(d.name.startsWith('API 验收'));assert.equal(d.postId,item.postId);if(!d.archived)await api('/api/studio/designs/'+d.id,{revision:d.revision},'DELETE');assert.equal((await api('/api/studio/designs/'+d.id)).design.archived,true)} + const final={canonical:base+'/explore/'+canonical,documentEntries:3,legacyCompatible:true,testRecordsArchived:results.length}; + await writeFile('/tmp/studio-library/final-verification.json',JSON.stringify(final,null,2));console.log(JSON.stringify(final)); +}else{ const catalog=await api('/studio/api/layouts');assert.equal(catalog.layouts.length,28); const note=(await api('/api/notes/8eb8c76500580f7a4b56ba3c')).entry; const asset=note.coverAssetId||note.assets.find(a=>a.kind==='image').id; @@ -29,3 +43,5 @@ for(const l of catalog.layouts){const r=await fetch(base+l.url);assert.equal(r.s for(const slug of ['learning-skills-template','layout-selection']){assert.equal((await fetch(base+'/studio/ideas/'+slug)).status,200);const r=await fetch(base+'/studio/api/export/'+slug+'?format=svg');assert.equal(r.status,200);assert.match(r.headers.get('content-type'),/svg/);} await writeFile('/tmp/studio-library/verification.json',JSON.stringify({results,preview:{asset,bytes,type:'image/webp'},pages:28},null,2)); console.log(JSON.stringify({crudFamilies:2,pages:28,conflicts:2,archiveRestore:2,compressedPreviewBytes:bytes,retainedToolbox:2,results:results.map(({parameters,...r})=>r)})); + +} diff --git a/scripts/verify-studio-visual.cjs b/scripts/verify-studio-visual.cjs index 324fb79..fcb7772 100644 --- a/scripts/verify-studio-visual.cjs +++ b/scripts/verify-studio-visual.cjs @@ -17,7 +17,7 @@ const assert=require('node:assert/strict'); const metrics=[]; for(let i=1;i<=27;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'}); - 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); + 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)); } 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')); @@ -27,8 +27,8 @@ const assert=require('node:assert/strict'); 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:'networkidle'});await page.screenshot({path:dir+'/document-entries.png',fullPage:true}); - assert.equal(images.filter(r=>new URL(r.path,base).pathname.startsWith('/api/media/')).length,0,'Display must never fetch originals');assert.equal(errors.length,0,JSON.stringify(errors)); + await page.goto(base+'/categories?category='+encodeURIComponent('文档入口'),{waitUntil:'domcontentloaded',timeout:60000});await page.getByRole('heading',{name:'分类',exact:true}).waitFor({state:'visible'});await page.screenshot({path:dir+'/document-entries.png',fullPage:true}); + assert.equal(images.filter(r=>new URL(r.path,base).pathname.startsWith('/api/media/')).length,0,'Display must never fetch originals');assert.equal(errors.length,0,JSON.stringify(errors));for(const image of images){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({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)});