Files
amosblomqvist 487f5d4cea wave 1 (platform stream): background-observability accuracy + owner-driven follow-ups
- observability: liveness-gated stale-job recovery, extraction run registry on globalThis with
  explicit 'embedding' phase, /api/jobs force-dynamic, client refresh nudges at every seam
- headless: providerSafePng canonicalizer (multi-IDAT Chrome PNGs → single-IDAT) fixing qwen 400s
- composer: Enter routes through pure enterAction — accepts a pending plan verdict (owner decision)
- markdown chrome: session-scoped mermaid overlay + md-blocks component set + pan-zoom math,
  corner-copy (clipboard grant in electron main), math/table/code block fixes, heading weight 900

543/543 tests; typecheck/lint/build clean
2026-09-19 13:16:34 +03:00

73 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Pure tests for the shared pan/zoom viewport math (components/pan-zoom.ts),
* harvested from the plan popup so the mermaid focus overlay runs the same
* fit-floor geometry: fully zoomed out IS the fit, panning locks at the
* floor (and re-centers), wheel zooms to the cursor, ceiling 4×. Pure
* function coverage — no DOM, no rendering (the overlays' live pointer
* wiring is exercised in the browser; these tests pin the math it runs).
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { FIT_ZOOM_MAX, ZOOM_MAX, fitGraphView, fitZoomFor, zoomedGraphView } from "../components/pan-zoom.ts";
describe("fitZoomFor", () => {
it("binds on the axis that runs out of room (wide box → width)", () => {
assert.equal(fitZoomFor({ w: 800, h: 400 }, { w: 400, h: 300 }), 384 / 800);
});
it("binds on height for a tall box", () => {
assert.equal(fitZoomFor({ w: 400, h: 800 }, { w: 400, h: 300 }), 284 / 800);
});
it("caps the fit at FIT_ZOOM_MAX — a tiny box must not blow up", () => {
assert.equal(fitZoomFor({ w: 50, h: 50 }, { w: 1000, h: 1000 }), FIT_ZOOM_MAX);
});
it("degenerate viewport (zero-sized) renders at 1×", () => {
assert.equal(fitZoomFor({ w: 400, h: 300 }, { w: 0, h: 0 }), 1);
});
});
describe("fitGraphView", () => {
it("centers the box at the given zoom", () => {
assert.deepEqual(fitGraphView({ w: 800, h: 400 }, { w: 400, h: 300 }, 0.5), {
x: (400 - 400) / 2,
y: (300 - 200) / 2,
zoom: 0.5,
});
});
});
describe("zoomedGraphView", () => {
const box = { w: 800, h: 400 };
const viewport = { w: 400, h: 300 };
const floor = fitZoomFor(box, viewport);
it("zooms to the focus point: the focus stays visually fixed", () => {
const out = zoomedGraphView({ x: 0, y: 0, zoom: 1 }, 1.25, floor, { x: 200, y: 150 }, box, viewport);
assert.equal(out.zoom, 1.25);
// focus.x - x_out = (focus.x - x_in) * k → the cursor anchors.
assert.equal(200 - out.x, 200 * 1.25);
assert.equal(150 - out.y, 150 * 1.25);
});
it("clamps at the floor and re-fits + RE-CENTERS (the fit is the floor)", () => {
const nearFloor = { x: -100, y: 40, zoom: floor * 1.1 };
const out = zoomedGraphView(nearFloor, 0.5, floor, { x: 200, y: 150 }, box, viewport);
assert.deepEqual(out, fitGraphView(box, viewport, floor));
});
it("clamps at ZOOM_MAX", () => {
const atMax = { x: 0, y: 0, zoom: ZOOM_MAX };
const out = zoomedGraphView(atMax, 2, floor, { x: 200, y: 150 }, box, viewport);
assert.deepEqual(out, atMax);
});
it("no-ops when the factor lands exactly on the current zoom", () => {
const view = { x: -37, y: 12, zoom: 1 };
const out = zoomedGraphView(view, 1, floor, { x: 0, y: 0 }, box, viewport);
assert.equal(out, view);
});
});