import { test } from 'node:test'; import assert from 'node:assert/strict'; import { bindCamera } from './interactions.js'; const close=(a,b)=>assert.ok(Math.abs(a-b)<1e-8,`${a} != ${b}`); function setup(t) { const saved={raf:globalThis.requestAnimationFrame,caf:globalThis.cancelAnimationFrame,observer:globalThis.ResizeObserver}; const frames=[]; globalThis.requestAnimationFrame=fn=>{frames.push(fn);return frames.length;};globalThis.cancelAnimationFrame=()=>{}; globalThis.ResizeObserver=class {observe(){}disconnect(){}}; t.after(()=>{globalThis.requestAnimationFrame=saved.raf;globalThis.cancelAnimationFrame=saved.caf;globalThis.ResizeObserver=saved.observer;}); function surface(left=100) { const surface=new EventTarget();Object.assign(surface,{clientWidth:800,clientHeight:600, getBoundingClientRect:()=>({left,top:20}),focus(){},setPointerCapture(){}}); const attributes={};return {surface,svg:{setAttribute:(k,v)=>attributes[k]=v},attributes}; } return {surface,flush:()=>{for(const fn of frames.splice(0))fn();}}; } function send(surface,type,values){const event=new Event(type,{cancelable:true});Object.assign(event,values);surface.dispatchEvent(event);return event;} test('wheel zoom uses the pane offset and synchronizes world cameras',t=>{ const env=setup(t),a=env.surface(),b=env.surface(1000),bounds={x:0,y:-760,width:1150,height:760}; let second;const first=bindCamera(a.surface,a.svg,bounds,source=>second.apply(source.pose()));second=bindCamera(b.surface,b.svg,bounds); const anchor=first.camera.world(250,200); const event=send(a.surface,'wheel',{deltaY:-120,deltaMode:0,clientX:350,clientY:220}); assert.ok(event.defaultPrevented); close(first.camera.world(250,200).x,anchor.x);close(first.camera.world(250,200).y,anchor.y); close(first.pose().center.x,second.pose().center.x);close(first.camera.scale,second.camera.scale); env.flush();assert.equal(a.attributes.viewBox,b.attributes.viewBox);first.destroy();second.destroy(); }); test('two-pointer pinch preserves the moving center without rasterizing',t=>{ const env=setup(t),{surface,svg}=env.surface();const view=bindCamera(surface,svg,{x:0,y:0,width:100,height:100}); send(surface,'pointerdown',{pointerId:1,button:0,clientX:200,clientY:200}); send(surface,'pointerdown',{pointerId:2,button:0,clientX:300,clientY:200}); const anchor=view.camera.world(150,180),scale=view.camera.scale; send(surface,'pointermove',{pointerId:2,clientX:400,clientY:200}); close(view.camera.scale,scale*2);close(view.camera.world(200,180).x,anchor.x);close(view.camera.world(200,180).y,anchor.y); view.destroy(); }); test('focus fits change bounds and detaching stops interaction',t=>{ const env=setup(t),{surface,svg}=env.surface();const view=bindCamera(surface,svg,{x:0,y:0,width:1000,height:1000}); view.focus({x:100,y:200,width:20,height:40});const center=view.pose().center; close(center.x,110);close(center.y,220);view.destroy();const scale=view.camera.scale; send(surface,'wheel',{deltaY:-120,deltaMode:0,clientX:200,clientY:200});close(view.camera.scale,scale); });