// @vitest-environment node /** * presence-cursor (TASK-0093, spec R4) — the PURE remote-cursor geometry * resolution. The `lookup` is a fake point provider (no DOM); stale blocks * → null → dropped. */ import { describe, expect, it } from "vitest"; import { resolvePresenceCursors, selectionRanges } from "../presence-cursor"; import type { Presence } from "../types"; function peer(userId: string, cursor: Presence["cursor"], viewing?: boolean): Presence { return { userId, name: `peer-${userId}`, color: "blue", cursor, ...(viewing !== undefined ? { viewing } : {}), lastSeen: 1, }; } /** A lookup that knows blocks b1..b3, each resolving offset→ a point. */ function fakeLookup(missing: string[] = []) { const known = new Set(["b1", "b2", "b3"]); const seen: string[] = []; const lookup = (blockId: string, offset: number | "end") => { if (!known.has(blockId) || missing.includes(blockId)) return null; seen.push(`${blockId}:${offset}`); const y = blockId === "b1" ? 10 : blockId === "b2" ? 20 : 30; return { x: 100, y: y + (offset === "end" ? 0 : offset) }; }; return { lookup, seen }; } describe("resolvePresenceCursors", () => { it("a peer with a resolvable caret gets a caret at the resolved point", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors( [peer("u1", { blockId: "b1", offset: 2 })], ["b1", "b2", "b3"], lookup, ); expect(r.dropped).toBe(0); expect(r.cursors).toHaveLength(1); expect(r.cursors[0]?.caret).toEqual({ x: 100, y: 12 }); }); it("null cursor (cleared / 50-peer avatars-only cap) gets NO caret", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors([peer("u1", null)], ["b1"], lookup); expect(r.cursors).toHaveLength(0); expect(r.dropped).toBe(0); // not "stale" — just cleared }); it("viewing:false (a background tab) gets NO caret (the avatar stays elsewhere)", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors( [peer("u1", { blockId: "b1", offset: 1 }, false)], ["b1"], lookup, ); expect(r.cursors).toHaveLength(0); expect(r.dropped).toBe(0); }); it("a stale cursor (block gone) is DROPPED silently and counted", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors( [peer("u1", { blockId: "gone", offset: 1 }), peer("u2", { blockId: "b1", offset: 1 })], ["b1"], lookup, ); expect(r.dropped).toBe(1); expect(r.cursors.map((c) => c.peer.userId)).toEqual(["u2"]); }); it("a single-block selection renders one rect spanning both endpoints", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors( [ peer("u1", { blockId: "b1", offset: 4, selection: { fromBlockId: "b1", fromOffset: 1, toBlockId: "b1", toOffset: 4 }, }), ], ["b1", "b2"], lookup, ); expect(r.cursors[0]?.rects).toEqual([{ x1: 100, y1: 11, x2: 100, y2: 14 }]); }); it("a multi-block selection = head partial + full middles + tail partial", () => { const { lookup } = fakeLookup(); const r = resolvePresenceCursors( [ peer("u1", { blockId: "b1", offset: 1, selection: { fromBlockId: "b1", fromOffset: 1, toBlockId: "b3", toOffset: 2 }, }), ], ["b1", "b2", "b3"], lookup, ); // b1: 1→end, b2: 0→end, b3: 0→2 expect(r.cursors[0]?.rects).toHaveLength(3); }); it("a selection whose endpoint block is gone drops only that range (the caret stays)", () => { const { lookup } = fakeLookup(["b3"]); const r = resolvePresenceCursors( [ peer("u1", { blockId: "b1", offset: 1, selection: { fromBlockId: "b1", fromOffset: 1, toBlockId: "b3", toOffset: 2 }, }), ], ["b1", "b2", "b3"], lookup, ); expect(r.cursors).toHaveLength(1); // the caret (b1) is fine // b1 head + b2 middle resolve; the b3 tail range drops. expect(r.cursors[0]?.rects).toHaveLength(2); }); it("a reversed selection (to before from) still resolves (order is normalized)", () => { // Anchor b3:2, focus b1:1 → the range covers b1[1..end] + b2[full] + // b3[0..2] (the anchor block keeps [0..2], the focus block keeps // [1..end]). const ranges = selectionRanges(["b1", "b2", "b3"], { fromBlockId: "b3", fromOffset: 2, toBlockId: "b1", toOffset: 1, }); expect(ranges).toEqual([ { blockId: "b1", from: 1, to: "end" }, { blockId: "b2", from: 0, to: "end" }, { blockId: "b3", from: 0, to: 2 }, ]); }); it("a selection referencing a missing block resolves to NO ranges (stale)", () => { expect( selectionRanges(["b1"], { fromBlockId: "b1", fromOffset: 0, toBlockId: "ghost", toOffset: 0, }), ).toEqual([]); }); });