fable/val Public Public: anyone can read this; nobody can change it.
forked from benjivers/val
workbenchthis machine
Files 0da7ffcb37d59bc652a0ef6ea8768f5a4ee103ff
| 1 | /** |
| 2 | * The watching view: what a snapshot with no seat in it turns into on screen. |
| 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("a round that has just ended says who took it and why", () => { |
| 47 | const lines = watchLines(spectator({ phase: "end", last: { round: 7, winner: "blue", why: "Time out" } })); |
| 48 | expect(lines.phase).toBe("round 7 · round over — blue take it, time out · red attacking"); |
| 49 | // Once the next round is on, the phase line is the phase again. |
| 50 | expect(watchLines(spectator({ phase: "buy", last: { round: 7, winner: "blue", why: "Time out" } })).phase).not.toContain("take it"); |
| 51 | }); |
| 52 | |
| 53 | test("the reason is named: the seat is held at this address", () => { |
| 54 | expect(watchLines(spectator()).why).toContain("address"); |
| 55 | expect(watchLines(spectator({ why: "cookie" })).why).toContain("cookie"); |
| 56 | // A snapshot that does not say still gets a sentence, not `undefined`. |
| 57 | expect(watchLines(spectator({ why: undefined })).why).toContain("watching"); |
| 58 | }); |
| 59 | |
| 60 | test("it never claims to know where anybody is", () => { |
| 61 | const lines = watchLines(spectator()); |
| 62 | expect(lines.note).toContain("No map"); |
| 63 | expect(JSON.stringify(lines)).not.toMatch(/"x"|"y"/); |
| 64 | }); |
| 65 | |
| 66 | test("a half-built snapshot is drawn, not thrown on", () => { |
| 67 | const lines = watchLines({ spectator: true }); |
| 68 | expect(lines.score).toBe("red 0 — 0 blue"); |
| 69 | expect(lines.clock).toBe("0s"); |
| 70 | expect(lines.phase).toContain("round 1"); |
| 71 | }); |
| 72 | }); |
| 73 | |
| 74 | test("long sentences are broken to fit the canvas", () => { |
| 75 | const ctx = { measureText: (text: string) => ({ width: text.length * 10 }) }; |
| 76 | const lines = wrap(ctx, "one two three four five six", 100); |
| 77 | expect(lines.length).toBeGreaterThan(1); |
| 78 | for (const line of lines) expect(line.length).toBeLessThanOrEqual(10); |
| 79 | expect(lines.join(" ")).toBe("one two three four five six"); |
| 80 | }); |
| 81 | |
| 82 | test("drawing it fills a flat panel and writes the score, and draws no world", () => { |
| 83 | const ops: string[] = []; |
| 84 | const ctx = new Proxy( |
| 85 | { measureText: (text: string) => ({ width: text.length * 6 }) } as Record<string, unknown>, |
| 86 | { |
| 87 | get: (target, prop: string) => |
| 88 | prop in target |
| 89 | ? target[prop] |
| 90 | : (...args: unknown[]) => ops.push(`${prop}(${args.map(String).join(",")})`), |
| 91 | set: () => true, |
| 92 | }, |
| 93 | ); |
| 94 | drawWatching(ctx as never, spectator(), 900, 570); |
| 95 | // One background rectangle and nothing else with an edge: no tiles, no bodies. |
| 96 | expect(ops.filter((op) => op.startsWith("fillRect")).length).toBe(1); |
| 97 | expect(ops.some((op) => op.startsWith("fillText(red 3 — 3 blue"))).toBe(true); |
| 98 | expect(ops.some((op) => op.startsWith("fillText(watching"))).toBe(true); |
| 99 | }); |