Files
stack/agents/dewey/work/wui/checks/final-checks.mjs
T

42 lines
3.0 KiB
JavaScript

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;