38 lines
2.8 KiB
JavaScript
38 lines
2.8 KiB
JavaScript
const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),vm=require('node:vm');
|
|
function fixture(){
|
|
const space={style:{}},surface={style:{}},label={},frames=new Map();let next=0,observer;
|
|
const viewport={clientWidth:1550,clientHeight:601,rect:{width:1550,height:600.5234375},scrollLeft:0,scrollTop:0,
|
|
addEventListener(){},getBoundingClientRect(){return this.rect;},
|
|
querySelector(s){return s==='.family-map-space'?space:surface;},replaceChildren(){},
|
|
scrollTo(x,y){this.scrollLeft=x;this.scrollTop=y;}};
|
|
const scope=vm.createContext({$:()=>label,ResizeObserver:class{constructor(fn){observer=fn;}observe(){}},
|
|
requestAnimationFrame(fn){frames.set(++next,fn);return next;},cancelAnimationFrame(id){frames.delete(id);}});
|
|
vm.runInContext(fs.readFileSync('web/family-map.js','utf8')+';this.MapClass=FamilyMap',scope);
|
|
const map=new scope.MapClass(viewport,()=>{});map.graph={width:400,height:264,nodes:[]};
|
|
return {map,viewport,space,surface,frames,resize:()=>observer(),flush(){const jobs=[...frames.values()];frames.clear();jobs.forEach(fn=>fn());}};
|
|
}
|
|
test('fractional fullscreen size never creates a too-tall scroll surface',()=>{
|
|
const f=fixture();for(let i=0;i<20;i++){f.map.fit();assert.equal(f.space.style.height,'600px');assert.ok(parseFloat(f.space.style.height)<=f.viewport.rect.height);}
|
|
assert.equal(f.surface.style.transform,'translate(575px,168px) scale(1)');
|
|
});
|
|
test('resize notifications coalesce and unchanged dimensions do not refit',()=>{
|
|
const f=fixture();let calls=0;const fit=f.map.fit.bind(f.map);f.map.fit=()=>{calls++;fit();};
|
|
f.resize();f.resize();assert.equal(f.frames.size,1);f.flush();assert.equal(calls,1);
|
|
f.resize();f.flush();assert.equal(calls,1);
|
|
f.viewport.rect.height=700.7;f.viewport.clientHeight=701;f.resize();f.flush();assert.equal(calls,2);
|
|
});
|
|
test('manual zoom and scroll survive a resize notification',()=>{
|
|
const f=fixture();f.map.autoFit=false;f.map.scale=1.5;f.viewport.scrollLeft=15;f.viewport.scrollTop=30;
|
|
f.resize();f.flush();assert.equal(f.map.scale,1.5);assert.equal(f.map.autoFit,false);
|
|
assert.equal(f.viewport.scrollLeft,15);assert.equal(f.viewport.scrollTop,30);
|
|
});
|
|
test('hidden canvas does not resize and pending work is cancelled on reset',()=>{
|
|
const f=fixture();f.viewport.clientWidth=0;f.resize();f.flush();assert.equal(f.space.style.width,undefined);
|
|
f.resize();f.map.reset();assert.equal(f.frames.size,0);assert.equal(f.map.graph,null);
|
|
});
|
|
test('scaled map clips unscaled overflow and fullscreen fallback clears the sidebar',()=>{
|
|
const css=fs.readFileSync('web/family-map.css','utf8');
|
|
assert.match(css,/\.family-map-space\{min-width:0;min-height:0;overflow:clip\}/);
|
|
assert.match(css,/scrollbar-gutter:stable/);assert.match(css,/\.family-workspace\.family-expanded\{z-index:1100\}/);
|
|
});
|