main
John Lauer Publish 0.2.50 01ab14c
// Render the dashboard's route card and step badges (issue #141) with stub elements: no browser, no dashboard.
import fs from 'node:fs';
import vm from 'node:vm';
import assert from 'node:assert/strict';
const html=fs.readFileSync(new URL('../tools/timeline/index.html',import.meta.url),'utf8');
const start=html.indexOf('  // issue #141: a step whose last command');
const end=html.indexOf('  // ---------------- build',start);
assert(start>0 && end>start);
assert(html.includes('id="route-card"') && html.includes('class="look-chip"'),'the card and the step chip are in the page');
const elements=new Map();
const node=()=>({hidden:true,textContent:'',innerHTML:'',classList:{s:new Set(),toggle(c,on){on?this.s.add(c):this.s.delete(c)}}});
const $=id=>{if(!elements.has(id))elements.set(id,node());return elements.get(id)};
const esc=s=>String(s==null?'':s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const fmtClock=iso=>String(iso||'');
const titleOf=(m,n)=>({routing:'Routing'}[n]||n);
const ctx={$,esc,fmtClock,titleOf};
vm.createContext(ctx);vm.runInContext(html.slice(start,end)+'\nObject.assign(globalThis,{renderRouteCard,lookOf,lookText});',ctx);
ctx.renderRouteCard({route:null,checks:[]});
assert.equal($('route-card').hidden,true,'no route and no checks, no section');
assert.equal(ctx.lookOf(null),null);assert.equal(ctx.lookOf({kind:'ok'}),null,'only findings and errors make a badge');
assert.equal(ctx.lookText('findings',3),'needs a look · 3');assert.equal(ctx.lookText('error',1),'error');
const route={route:{at:'t1',measuredOn:'measured on the previous board (board-0.kicad_pcb, revision 1; the board is now revision 2)',onCurrentBoard:false,
  routed:2,connections:3,unrouted:1,netsUnrouted:['C'],vias:4,warnings:['<w>'],detail:['C at J7.1 (no path)'],routeQuality:'2 segments'},
  gate:{at:'t2',measuredOn:'x',onCurrentBoard:false,newErrors:0,unconnected:1}};
const checks=[{key:'route',step:'routing',kind:'findings',count:1,lines:['routing closed 1 connection(s) short']},{key:'gate',step:'routing',kind:'error',count:1,lines:['no plan <b>']}];
ctx.renderRouteCard({route,checks});
assert.equal($('route-card').hidden,false);
assert.equal($('rt-title').textContent,'Last route');
assert.match($('rt-on').textContent,/previous board/);assert($('rt-on').classList.s.has('prev'),'previous board is flagged');
assert.match($('rt-facts').innerHTML,/routed <b>2 \/ 3<\/b>/);assert.match($('rt-facts').innerHTML,/unrouted <b>1<\/b> <span>in C<\/span>/);
assert.match($('rt-facts').innerHTML,/vias <b>4<\/b>/);assert.match($('rt-facts').innerHTML,/gate <b>0<\/b> new DRC, <b>1<\/b> unconnected/);
assert($('rt-lines').innerHTML.includes('warning: &lt;w&gt;'),'warnings escaped');
assert($('rt-checks').innerHTML.includes('look-chip error') && $('rt-checks').innerHTML.includes('Routing · route') && $('rt-checks').innerHTML.includes('no plan &lt;b&gt;'));
ctx.renderRouteCard({route:null,checks});
assert.equal($('route-card').hidden,false);assert.equal($('rt-title').textContent,'Checks that need a look');
console.log('PASS: route card hidden without data, numbers and previous-board label shown, checks listed with error/findings chips, text escaped');