import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { DEFAULT_SCENE_VIEW_CAMERA, sceneViewPanBy, sceneViewZoomAt, } from '../../shared/types/sceneView'; import { SceneViewStore } from './sceneViewStore'; describe('SceneViewStore', () => { it('resets to default camera', () => { const store = new SceneViewStore(); store.dispatch({ kind: 'set', camera: { scale: 2, ox: 0.2, oy: 0.8 } }); const next = store.dispatch({ kind: 'reset' }); assert.equal(next.scale, 1); assert.equal(next.ox, 0.5); assert.equal(next.oy, 0.5); }); it('clamps scale and origin', () => { const store = new SceneViewStore(); const next = store.dispatch({ kind: 'set', camera: { scale: 99, ox: -1, oy: 2 } }); assert.equal(next.scale, 8); assert.equal(next.ox, 0); assert.equal(next.oy, 1); }); }); describe('sceneViewZoomAt / panBy', () => { it('zooms toward cursor and keeps that content point under cursor', () => { const hostW = 1000; const hostH = 500; const containW = 800; const containH = 400; const hostX = 700; const hostY = 250; const next = sceneViewZoomAt(DEFAULT_SCENE_VIEW_CAMERA, { hostW, hostH, containW, containH, hostX, hostY, factor: 2, }); assert.ok(next.scale > 1.5); const displayW = containW * next.scale; const displayH = containH * next.scale; const left = hostW / 2 - next.ox * displayW; const top = hostH / 2 - next.oy * displayH; const ix = (hostX - left) / displayW; const iy = (hostY - top) / displayH; // At scale=1 contain is centered; cursor was at content x=(700-100)/800=0.75 assert.ok(Math.abs(ix - 0.75) < 1e-6); assert.ok(Math.abs(iy - 0.5) < 1e-6); }); it('pans in host pixels', () => { const cam = { scale: 2, ox: 0.5, oy: 0.5 }; const next = sceneViewPanBy(cam, { containW: 400, containH: 200, dx: 80, dy: 0 }); // displayW=800; dx=80 → ox decreases by 0.1 assert.ok(Math.abs(next.ox - 0.4) < 1e-6); assert.equal(next.oy, 0.5); }); });