CODE · 2.3 KB
src/verify-output.mjs
Workspace snapshot · 09/04 13:45
import fs from 'node:fs';
import path from 'node:path';
import AdmZip from 'adm-zip';
import sharp from 'sharp';
const out=path.join(process.cwd(),'artifacts','sales-pilot');
const pptxPath=path.join(out,'ai-slide-lab-sales-pilot.pptx');
const manifest=JSON.parse(fs.readFileSync(path.join(out,'manifest.json'),'utf8'));
const zip=new AdmZip(pptxPath);
const slideXml=zip.getEntries().filter(e=>/^ppt\/slides\/slide\d+\.xml$/.test(e.entryName));
const notesXml=zip.getEntries().filter(e=>/^ppt\/notesSlides\/notesSlide\d+\.xml$/.test(e.entryName));
const checks=[];
checks.push({name:'slide-count',pass:slideXml.length===manifest.slides.length,actual:slideXml.length});
checks.push({name:'source-notes',pass:notesXml.length===manifest.slides.length,actual:notesXml.length});
let totalText=0,totalShapes=0;
for(const e of slideXml){const x=e.getData().toString('utf8');totalText+=(x.match(/<a:t>/g)||[]).length;totalShapes+=(x.match(/<p:sp>/g)||[]).length;}
checks.push({name:'editable-text-elements',pass:totalText>=manifest.slides.length*4,actual:totalText});
checks.push({name:'editable-shapes',pass:totalShapes>=manifest.slides.length*3,actual:totalShapes});
const previews=fs.readdirSync(path.join(out,'preview')).filter(f=>f.endsWith('.png')).sort();
checks.push({name:'preview-count',pass:previews.length===manifest.slides.length,actual:previews.length});
for(const f of previews){
const image=sharp(path.join(out,'preview',f));
const m=await image.metadata();
checks.push({name:`render-${f}`,pass:m.width===1600&&m.height===900,actual:`${m.width}x${m.height}`});
const stats=await image.stats();
const entropy=stats.entropy;
checks.push({name:`visual-density-${f}`,pass:entropy>0.35,actual:Number(entropy.toFixed(3))});
}
checks.push({name:'pdf-present',pass:fs.statSync(path.join(out,'ai-slide-lab-sales-pilot.pdf')).size>10000,actual:fs.statSync(path.join(out,'ai-slide-lab-sales-pilot.pdf')).size});
const report={generatedAt:new Date().toISOString(),passed:checks.every(c=>c.pass),checks,limitations:['Preview is rendered from the shared scene content, not by Microsoft PowerPoint. Open-and-render compatibility in PowerPoint remains a manual gate.']};
fs.writeFileSync(path.join(out,'qa-report.json'),JSON.stringify(report,null,2));
console.log(JSON.stringify(report,null,2));
if(!report.passed)process.exit(1);