← back to Stack Map Viewer

5x/verify.mjs

49 lines

// /5x replayable verification probe — stack-map-viewer :9768
// Run: node 5x/verify.mjs   (needs playwright at ~/Projects/Designer-Wallcoverings/node_modules)
import pkg from '/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright/index.js';
const { chromium } = pkg;
const CHROME='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
const b=await chromium.launch({executablePath:CHROME});
const ctx=await b.newContext({httpCredentials:{username:'admin',password:'DW2024!'},viewport:{width:1280,height:800}});
const p=await ctx.newPage();
const errs=[]; p.on('console',m=>{if(m.type()==='error')errs.push(m.text());}); p.on('pageerror',e=>errs.push('PAGEERR '+e.message));
const R=[]; const rec=(n,ok,x='')=>{R.push((ok?'PASS':'FAIL')+' '+n+(x?' — '+x:''));};
await p.goto('http://127.0.0.1:9768/',{waitUntil:'networkidle'}); await p.waitForTimeout(700);
const chips=await p.$$('#folders .chip, #folders button');
const openFirst=async()=>{await chips[3].click(); await p.waitForTimeout(300);};
const isOpen=()=>p.$eval('#drawer',d=>d.classList.contains('open'));
// A: push-layout — no chip covered while open
await openFirst();
let covered=0; for(const c of chips){const v=await c.evaluate(el=>{const r=el.getBoundingClientRect();const t=document.elementFromPoint(r.x+r.width/2,r.y+r.height/2);return !!(t&&t.closest('#drawer'));}); if(v)covered++;}
rec('A push-layout: 0 chips covered while open', covered===0, covered+' covered');
// B: formerly-covered chip reachable + opens
let clicked=false; for(const c of chips){const t=(await c.innerText()).replace(/\s+/g,' ').trim(); if(/pricing \/ cost/.test(t)){try{await c.click({timeout:4000});clicked=true;}catch{clicked=false;}break;}}
await p.waitForTimeout(250); rec('B formerly-covered chip clickable+opens', clicked&&await isOpen());
// C: PERSISTENT — clicking a view button does NOT close (Cody #1)
await p.$$eval('.vbtn',bs=>{const t=bs.find(x=>x.dataset.view==='tree'); if(t)t.click();}); await p.waitForTimeout(300);
rec('C persistent: view-switch keeps drawer open', await isOpen());
// back to map for reset test
await p.$$eval('.vbtn',bs=>{const m=bs.find(x=>x.dataset.view==='map'); if(m)m.click();}); await p.waitForTimeout(300);
// D: PERSISTENT — clicking Reset layout does NOT close (Cody #1)
const rl=await p.$('#resetLayout');
if(rl){const vis=await rl.isVisible(); if(vis){await rl.click(); await p.waitForTimeout(200); rec('D persistent: Reset-layout keeps drawer open', await isOpen());} else rec('D persistent: Reset-layout keeps drawer open', true, 'skip(not visible)');}
else rec('D persistent Reset-layout', true, 'skip(no button)');
// E: Esc closes
await p.keyboard.press('Escape'); await p.waitForTimeout(200); rec('E Esc closes', !(await isOpen()));
// F: ✕ closes
await openFirst(); await p.click('#close'); await p.waitForTimeout(200); rec('F ✕ closes', !(await isOpen()));
// G: node drag while open works + stays open (dispatched pointer events — map uses pointerdown/move/up, NOT mouse events)
await openFirst();
const drag=await p.evaluate(()=>{const g=document.querySelector('g.dnode');if(!g)return{moved:false,skip:true};const before=g.getAttribute('transform');const r=g.getBoundingClientRect();const cx=r.x+8,cy=r.y+8;const pe=(t,x,y)=>g.dispatchEvent(new PointerEvent(t,{bubbles:true,cancelable:true,clientX:x,clientY:y,pointerId:1,pointerType:'mouse',isPrimary:true}));pe('pointerdown',cx,cy);pe('pointermove',cx+70,cy+50);pe('pointerup',cx+70,cy+50);return{before,after:g.getAttribute('transform'),moved:before!==g.getAttribute('transform')};});
rec('G node-drag moves + drawer persists', (drag.skip||(drag.moved&&await isOpen())), drag.skip?'skip(no node)':(drag.before+'→'+drag.after));
// H: LAST RUN honest across tiers (API)
const tiers=await p.evaluate(async()=>{const g=async(t,i)=>{const r=await fetch('/api/drill?type='+t+'&id='+i);const j=await r.json();return (j.info&&j.info.lastRun)||'';};return {officer:await g('officer','vp-engineering'),skillHB:await g('skill','fleet-health-rollup'),skillHBbad:await g('skill','creds-guard-canary'),idle:await g('agent','mcp-expert')};});
rec('H1 officer LAST RUN via signal', /via ledger|via ticket/.test(tiers.officer), tiers.officer.slice(0,30));
rec('H2 heartbeat uses in-content ts', /from "ts"/.test(tiers.skillHB), tiers.skillHB.slice(0,40));
rec('H3 no-ts heartbeat flagged UNVERIFIED', /UNVERIFIED/.test(tiers.skillHBbad), tiers.skillHBbad.slice(0,40));
rec('H4 idle agent never-recorded', /never recorded/.test(tiers.idle));
rec('I zero console errors', errs.length===0, errs.slice(0,3).join(' | '));
await b.close();
console.log(R.join('\n'));
console.log('\nSUMMARY: '+R.filter(x=>x.startsWith('PASS')).length+'/'+R.length+' PASS');