Files
mcm-26-b/scripts/verify_visualization.cjs
T

62 lines
4.2 KiB
JavaScript

/* Run only our generated local script in a minimal DOM, with no browser or network.
* This checks event handlers and serializes the produced SVG for static inspection.
* It is not a replacement for a native browser layout test.
*/
const fs=require('node:fs'),path=require('node:path'),vm=require('node:vm'),assert=require('node:assert');
const rootDir=path.resolve(__dirname,'..');
const source=fs.readFileSync(path.join(rootDir,'output/q1/question1_full.html'),'utf8');
const script=source.match(/<script>([\s\S]*?)<\/script>/)[1];
class Element {
constructor(tag) {this.tag=tag;this.attrs={};this.children=[];this.style={};this.events={};this.value='';this.max='3';this.textContent='';}
setAttribute(k,v){this.attrs[k]=String(v);}
appendChild(x){this.children.push(x);return x;}
replaceChildren(...x){this.children=x;this.textContent='';}
addEventListener(k,f){this.events[k]=f;}
add(o){this.children.push(o);}
getBoundingClientRect(){return {width:width};}
}
const esc=s=>String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
const palette={light:{'--foreground':'#24282e','--border':'#d9dfe5','--viz-series-1':'#146e9a','--viz-series-2':'#c6503a','--viz-series-3':'#198064'},dark:{'--foreground':'#eceff3','--border':'#424a55','--viz-series-1':'#6cc8ed','--viz-series-2':'#ff947d','--viz-series-3':'#64d6ac'}};
function serialize(el,theme){
if(el.tag==='text')Object.assign(el.attrs,{fill:palette[theme]['--foreground'],'font-size':12,'font-family':'Microsoft YaHei, sans-serif'});
const attrs=Object.entries(el.attrs).map(([k,v])=>`${k}="${esc(String(v).replace(/var\(([^)]+)\)/g,(_,n)=>palette[theme][n]||n))}"`).join(' ');
return `<${el.tag} ${attrs}>${esc(el.textContent)}${el.children.map(x=>serialize(x,theme)).join('')}</${el.tag}>`;
}
function text(el){return el.textContent+el.children.map(text).join(' ');}
const elements={};
for(const key of ['case','step','view','plot','step-label','stats','status','rows'])elements[key]=new Element(key==='plot'?'svg':'div');
elements.view.value='region';
const root=new Element('div');
root.querySelector=s=>{const m=s.match(/data-q1="([^"]+)"/);assert(m&&elements[m[1]],'missing selector '+s);return elements[m[1]];};
const document={getElementById:id=>{assert.equal(id,'q1-localization');return root;},createElement:tag=>new Element(tag),createElementNS:(_,tag)=>new Element(tag)};
let width=736;
vm.runInNewContext(script,{document,Option:function(label,value){this.label=label;this.value=String(value);},ResizeObserver:class{constructor(f){this.f=f;}observe(){}},Number,Math,String,Object,console},{timeout:5000});
const output=path.join(rootDir,'output/q1/figures');fs.mkdirSync(output,{recursive:true});
let checks=0;
for(let i=0;i<elements.case.children.length;i++) {
elements.case.value=String(i);elements.case.events.change();
for(let n=1;n<=+elements.step.max;n++) {
elements.step.value=String(n);elements.step.events.input();
assert.equal(elements['step-label'].textContent,`${n} / ${elements.step.max}`);
for(const v of ['global','region']) {
elements.view.value=v;elements.view.events.change();
const svg=serialize(elements.plot,'light');
assert(!/NaN|undefined|Infinity/.test(svg));
assert(svg.includes('data-axis="x"')&&svg.includes('data-axis="y"'));
checks++;
}
}
}
for(const w of [360,736])for(const theme of ['light','dark'])for(const index of [1,elements.case.children.length-1]) {
width=w;elements.case.value=String(index);elements.case.events.change();
elements.view.value='region';elements.view.events.change();
const height=elements.plot.attrs.viewBox.split(' ')[3];
const svg=serialize(elements.plot,theme).replace('<svg ',`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${height}" `);
const filename=(index===1?'counterexample':'practice')+`-${w}-${theme}.svg`;
fs.writeFileSync(path.join(output,filename),svg,'utf8');
}
const report={interaction_states_checked:checks,missing_selectors:0,nonfinite_svg_values:0,browser_layout_verified:false,
note:'Browser local-file navigation blocked by browser URL policy. Event handlers checked in minimal DOM; SVG statically inspected separately.'};
fs.writeFileSync(path.join(rootDir,'output/q1/visualization_checks.json'),JSON.stringify(report,null,2));
console.log(report);