import {browser} from './browser.mjs'; import {writeFile,readFile,stat} from 'node:fs/promises'; import assert from 'node:assert/strict'; const b=await browser(), report={observedAt:new Date().toISOString(),checks:[]}; const url=new URL('../index.html',import.meta.url); try{ await b.viewport(1440,900);await b.navigate(url.href); const resources=(await b.call('Page.getResourceTree')).frameTree.resources.map(x=>x.url); assert(resources.length>5);assert(resources.every(u=>u.startsWith('file:'))); report.checks.push({name:'Entry page loads only local resources',result:'pass',resourceCount:resources.length}); const links=await b.evaluate('[...document.querySelectorAll("a[href],link[href],script[src]")].map(x=>x.getAttribute("href")||x.getAttribute("src"))'); for(const link of links){ if(link.startsWith('#'))assert(await b.evaluate(`!!document.getElementById(${JSON.stringify(link.slice(1))})`),link); else assert((await stat(new URL(link,url))).isFile(),link); } const css=await readFile(new URL('../styles.css',import.meta.url),'utf8'); for(const match of css.matchAll(/url\('([^']+)'\)/g))assert((await stat(new URL(match[1],url))).isFile()); report.checks.push({name:'HTML and CSS asset links and in-page anchors resolve',result:'pass'}); await b.evaluate('document.querySelector(".skip").focus()');await b.key('Enter'); assert.equal(await b.evaluate('document.activeElement.id'),'main'); report.checks.push({name:'Keyboard skip link moves focus to main',result:'pass'}); for(const width of [699,700,959,960,1199,1200,1699,1700,2399,2400]){ await b.viewport(width,900); assert(await b.evaluate('document.documentElement.scrollWidth<=innerWidth'),String(width)); } report.checks.push({name:'Both sides of five responsive breakpoints have no page overflow',result:'pass',widths:[699,700,959,960,1199,1200,1699,1700,2399,2400]}); for(const width of [320,1440]){ await b.viewport(width,900); const small=await b.evaluate(`([...document.querySelectorAll('button,input,select,textarea,.controls nav a,.brand-home,.review-link')].filter(e=>!e.disabled).map(e=>{const r=e.getBoundingClientRect();return {id:e.id||e.textContent.trim().slice(0,30),width:r.width,height:r.height}}).filter(r=>r.width<24||r.height<24))`); assert.deepEqual(small,[]); } report.checks.push({name:'Interactive control targets meet 24 px at 320 and 1440 widths',result:'pass',qualification:'Inline prose/footer links use the WCAG inline-text exception.'}); await b.evaluate('localStorage.setItem("mosaic-brand-preview-v1", "{broken JSON");'); await b.navigate(url.href); assert.equal(await b.evaluate('document.documentElement.dataset.palette'),'harbor'); report.checks.push({name:'Malformed preference storage recovers to a usable default',result:'pass'}); }catch(error){report.failure=error.stack;} finally{await b.close();} await writeFile(new URL('../evidence/final-verification.json',import.meta.url),JSON.stringify(report,null,2)+'\n'); console.log(JSON.stringify(report,null,2)); if(report.failure)process.exitCode=1;