| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
import { describe, expect, test } from "bun:test"; |
| 6 |
import { drawWatching, watchLines, watching, wrap } from "../static/watch.js"; |
| 7 |
|
| 8 |
const spectator = (over: Record<string, unknown> = {}) => ({ |
| 9 |
t: "s", |
| 10 |
spectator: true, |
| 11 |
why: "address", |
| 12 |
phase: "live", |
| 13 |
clock: 42.4, |
| 14 |
round: 7, |
| 15 |
score: { red: 3, blue: 3 }, |
| 16 |
attackers: "red", |
| 17 |
result: null, |
| 18 |
events: ["red won the round"], |
| 19 |
feed: [], |
| 20 |
...over, |
| 21 |
}); |
| 22 |
|
| 23 |
const seated = () => ({ t: "s", phase: "live", me: { id: "p1", alive: true }, players: [] }); |
| 24 |
|
| 25 |
describe("telling a seat from a watcher", () => { |
| 26 |
test("a spectator snapshot is watching, a seat's is not", () => { |
| 27 |
expect(watching(spectator())).toBe(true); |
| 28 |
expect(watching(seated())).toBe(false); |
| 29 |
}); |
| 30 |
|
| 31 |
test("a snapshot with no `me` is watching even if nobody flagged it", () => { |
| 32 |
expect(watching({ t: "s", phase: "live" })).toBe(true); |
| 33 |
expect(watching(null)).toBe(false); |
| 34 |
expect(watching(undefined)).toBe(false); |
| 35 |
}); |
| 36 |
}); |
| 37 |
|
| 38 |
describe("what the watching view says", () => { |
| 39 |
test("the score, the round and the clock, from the snapshot it was given", () => { |
| 40 |
const lines = watchLines(spectator()); |
| 41 |
expect(lines.score).toBe("red 3 — 3 blue"); |
| 42 |
expect(lines.clock).toBe("43s"); |
| 43 |
expect(lines.phase).toBe("round 7 · live · red attacking"); |
| 44 |
}); |
| 45 |
|
| 46 |
test("the reason is named: the seat is held at this address", () => { |
| 47 |
expect(watchLines(spectator()).why).toContain("address"); |
| 48 |
expect(watchLines(spectator({ why: "cookie" })).why).toContain("cookie"); |
| 49 |
|
| 50 |
expect(watchLines(spectator({ why: undefined })).why).toContain("watching"); |
| 51 |
}); |
| 52 |
|
| 53 |
test("it never claims to know where anybody is", () => { |
| 54 |
const lines = watchLines(spectator()); |
| 55 |
expect(lines.note).toContain("No map"); |
| 56 |
expect(JSON.stringify(lines)).not.toMatch(/"x"|"y"/); |
| 57 |
}); |
| 58 |
|
| 59 |
test("a half-built snapshot is drawn, not thrown on", () => { |
| 60 |
const lines = watchLines({ spectator: true }); |
| 61 |
expect(lines.score).toBe("red 0 — 0 blue"); |
| 62 |
expect(lines.clock).toBe("0s"); |
| 63 |
expect(lines.phase).toContain("round 1"); |
| 64 |
}); |
| 65 |
}); |
| 66 |
|
| 67 |
test("long sentences are broken to fit the canvas", () => { |
| 68 |
const ctx = { measureText: (text: string) => ({ width: text.length * 10 }) }; |
| 69 |
const lines = wrap(ctx, "one two three four five six", 100); |
| 70 |
expect(lines.length).toBeGreaterThan(1); |
| 71 |
for (const line of lines) expect(line.length).toBeLessThanOrEqual(10); |
| 72 |
expect(lines.join(" ")).toBe("one two three four five six"); |
| 73 |
}); |
| 74 |
|
| 75 |
test("drawing it fills a flat panel and writes the score, and draws no world", () => { |
| 76 |
const ops: string[] = []; |
| 77 |
const ctx = new Proxy( |
| 78 |
{ measureText: (text: string) => ({ width: text.length * 6 }) } as Record<string, unknown>, |
| 79 |
{ |
| 80 |
get: (target, prop: string) => |
| 81 |
prop in target |
| 82 |
? target[prop] |
| 83 |
: (...args: unknown[]) => ops.push(`${prop}(${args.map(String).join(",")})`), |
| 84 |
set: () => true, |
| 85 |
}, |
| 86 |
); |
| 87 |
drawWatching(ctx as never, spectator(), 900, 570); |
| 88 |
|
| 89 |
expect(ops.filter((op) => op.startsWith("fillRect")).length).toBe(1); |
| 90 |
expect(ops.some((op) => op.startsWith("fillText(red 3 — 3 blue"))).toBe(true); |
| 91 |
expect(ops.some((op) => op.startsWith("fillText(watching"))).toBe(true); |
| 92 |
}); |