import assert from 'node:assert/strict'; import { mkdir, writeFile } from 'node:fs/promises'; import { fileURLToPath, pathToFileURL } from 'node:url'; const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href); const directory = new URL('../docs/teaching/rtcp-beginners/', import.meta.url); await mkdir(new URL('verification/',directory),{recursive:true}); const browser = await chromium.launch({executablePath:'/usr/bin/google-chrome',headless:true,args:['--no-sandbox']}); try { const page = await browser.newPage({viewport:{width:794,height:1123}}); const errors=[];page.on('pageerror',(e)=>errors.push(e.message)); await page.goto(new URL('handbook.html',directory).href);await page.emulateMedia({media:'print'}); await page.evaluate(async()=>{await document.fonts.ready;await Promise.all([...document.images].map((im)=>im.decode()));}); const checks=await page.locator('.sheet').evaluateAll((pages)=>pages.map((sheet)=>{ const outer=sheet.getBoundingClientRect(),body=sheet.querySelector('.body'),key=sheet.querySelector('.key').getBoundingClientRect(); const violations=[...body.querySelectorAll('p,li,table,figure,pre,.equation,aside,h2')].filter((e)=>{ const r=e.getBoundingClientRect();return r.bottom>key.top-6||r.right>outer.right-32||r.left({tag:e.tagName,text:e.textContent.slice(0,70),bottom:e.getBoundingClientRect().bottom-outer.top})); const title=sheet.querySelector('h1');const range=document.createRange();range.selectNodeContents(title); return {page:Number(sheet.dataset.page),title:title.textContent,violations,overflow:body.scrollHeight>body.clientHeight+1,title_right:range.getBoundingClientRect().right-outer.left,images:[...sheet.querySelectorAll('img')].map((im)=>({src:im.getAttribute('src'),loaded:im.complete&&im.naturalWidth>0})),characters:body.innerText.length}; })); await writeFile(new URL('verification/layout.json',directory),JSON.stringify({report_id:'rtcp-beginners-layout-v1',pages:checks,errors},null,2)+'\n'); for(const check of checks) await page.locator(`.sheet[data-page="${check.page}"]`).screenshot({path:fileURLToPath(new URL(`verification/page-${String(check.page).padStart(2,'0')}.png`,directory))}); const bad=checks.filter((c)=>c.violations.length||c.overflow||c.title_right>760||c.images.some((im)=>!im.loaded)); assert.deepEqual(errors,[]);assert.deepEqual(bad,[],'Handbook page overflow'); await page.pdf({path:fileURLToPath(new URL('五轴RTCP与工件坐标-初学者完整讲义.pdf',directory)),preferCSSPageSize:true,printBackground:true,tagged:true,outline:true}); console.log(JSON.stringify({report_id:'rtcp-beginners-layout-v1',status:'verified',pages:checks.length})); } finally {await browser.close();}