/** * The watching view: what a snapshot with no seat in it turns into on screen. */ import { describe, expect, test } from "bun:test"; import { drawWatching, watchLines, watching, wrap } from "../static/watch.js"; const spectator = (over: Record = {}) => ({ t: "s", spectator: true, why: "address", phase: "live", clock: 42.4, round: 7, score: { red: 3, blue: 3 }, attackers: "red", result: null, events: ["red won the round"], feed: [], ...over, }); const seated = () => ({ t: "s", phase: "live", me: { id: "p1", alive: true }, players: [] }); describe("telling a seat from a watcher", () => { test("a spectator snapshot is watching, a seat's is not", () => { expect(watching(spectator())).toBe(true); expect(watching(seated())).toBe(false); }); test("a snapshot with no `me` is watching even if nobody flagged it", () => { expect(watching({ t: "s", phase: "live" })).toBe(true); expect(watching(null)).toBe(false); expect(watching(undefined)).toBe(false); }); }); describe("what the watching view says", () => { test("the score, the round and the clock, from the snapshot it was given", () => { const lines = watchLines(spectator()); expect(lines.score).toBe("red 3 — 3 blue"); expect(lines.clock).toBe("43s"); expect(lines.phase).toBe("round 7 · live · red attacking"); }); test("the reason is named: the seat is held at this address", () => { expect(watchLines(spectator()).why).toContain("address"); expect(watchLines(spectator({ why: "cookie" })).why).toContain("cookie"); // A snapshot that does not say still gets a sentence, not `undefined`. expect(watchLines(spectator({ why: undefined })).why).toContain("watching"); }); test("it never claims to know where anybody is", () => { const lines = watchLines(spectator()); expect(lines.note).toContain("No map"); expect(JSON.stringify(lines)).not.toMatch(/"x"|"y"/); }); test("a half-built snapshot is drawn, not thrown on", () => { const lines = watchLines({ spectator: true }); expect(lines.score).toBe("red 0 — 0 blue"); expect(lines.clock).toBe("0s"); expect(lines.phase).toContain("round 1"); }); }); test("long sentences are broken to fit the canvas", () => { const ctx = { measureText: (text: string) => ({ width: text.length * 10 }) }; const lines = wrap(ctx, "one two three four five six", 100); expect(lines.length).toBeGreaterThan(1); for (const line of lines) expect(line.length).toBeLessThanOrEqual(10); expect(lines.join(" ")).toBe("one two three four five six"); }); test("drawing it fills a flat panel and writes the score, and draws no world", () => { const ops: string[] = []; const ctx = new Proxy( { measureText: (text: string) => ({ width: text.length * 6 }) } as Record, { get: (target, prop: string) => prop in target ? target[prop] : (...args: unknown[]) => ops.push(`${prop}(${args.map(String).join(",")})`), set: () => true, }, ); drawWatching(ctx as never, spectator(), 900, 570); // One background rectangle and nothing else with an edge: no tiles, no bodies. expect(ops.filter((op) => op.startsWith("fillRect")).length).toBe(1); expect(ops.some((op) => op.startsWith("fillText(red 3 — 3 blue"))).toBe(true); expect(ops.some((op) => op.startsWith("fillText(watching"))).toBe(true); });