pike/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files 4fd585251d7e85f906bd34583a5e32b0a1736c1b
1
/**
2
 * The banner: what the view says big when a round turns, and when it says nothing.
3
 */
4
5
import { describe, expect, test } from "bun:test";
6
import { banner, drawBanner } from "../static/banner.js";
7
8
const snap = (over: Record<string, unknown> = {}, me: Record<string, unknown> | null = { team: "red", side: "atk" }) => ({
9
  phase: "live",
10
  clock: 40,
11
  round: 5,
12
  result: null,
13
  last: { round: 4, winner: "blue", why: "Spike defused" },
14
  me,
15
  ...over,
16
});
17
18
describe("what the banner says", () => {
19
  test("a live round says nothing: the view is the game", () => {
20
    expect(banner(snap())).toBe(null);
21
    expect(banner(null)).toBe(null);
22
  });
23
24
  test("a round over says whether you won, why, and when the next one starts", () => {
25
    const lost = banner(snap({ phase: "end", clock: 3.2 }));
26
    expect(lost).toEqual({ title: "Round lost", sub: "spike defused · next round in 4s", alpha: 1 });
27
    const won = banner(snap({ phase: "end", clock: 3.2 }, { team: "blue", side: "def" }));
28
    expect(won?.title).toBe("Round won");
29
  });
30
31
  test("a round over with no outcome yet still says the round is over", () => {
32
    expect(banner(snap({ phase: "end", clock: 2, last: null }))).toEqual({ title: "Round over", sub: "next round in 2s", alpha: 1 });
33
  });
34
35
  test("a match over names the winner and the countdown to the next match", () => {
36
    const words = banner(snap({ phase: "over", clock: 9.9, result: "red win 13–7" }));
37
    expect(words?.title).toBe("red win 13–7");
38
    expect(words?.sub).toBe("match over · a new match starts in 10s");
39
  });
40
41
  test("a round starting shows its number and your side for three seconds, then fades out", () => {
42
    // The buy phase is fourteen seconds; the card is for its first three.
43
    const fresh = banner(snap({ phase: "buy", clock: 14 }));
44
    expect(fresh).toEqual({ title: "Round 5", sub: "you attack — plant the spike on A or B", alpha: 1 });
45
    expect(banner(snap({ phase: "buy", clock: 13 }, { team: "blue", side: "def" }))?.sub).toBe("you defend — hold A and B");
46
    // Half a second before it goes it is half gone: it leaves by fading, never by blinking off.
47
    const leaving = banner(snap({ phase: "buy", clock: 11.25 }));
48
    expect(leaving?.alpha).toBeCloseTo(0.5, 5);
49
    expect(banner(snap({ phase: "buy", clock: 11 }))).toBe(null);
50
    expect(banner(snap({ phase: "buy", clock: 3 }))).toBe(null);
51
  });
52
53
  test("a watcher with no side is told the team that took it", () => {
54
    expect(banner(snap({ phase: "end", clock: 3 }, null))?.title).toBe("blue take the round");
55
  });
56
});
57
58
test("drawing it is one band and two lines of text, and nothing when there is nothing to say", () => {
59
  const ops: string[] = [];
60
  const ctx = new Proxy({} as Record<string, unknown>, {
61
    get: (_, prop: string) => (...args: unknown[]) => ops.push(`${prop}(${args.map(String).join(",")})`),
62
    set: () => true,
63
  });
64
  drawBanner(ctx as never, banner(snap({ phase: "end", clock: 3 })), 900, 570);
65
  expect(ops.filter((op) => op.startsWith("fillRect")).length).toBe(1);
66
  expect(ops.some((op) => op.startsWith("fillText(Round lost"))).toBe(true);
67
  expect(ops.some((op) => op.startsWith("fillText(spike defused"))).toBe(true);
68
  ops.length = 0;
69
  drawBanner(ctx as never, null, 900, 570);
70
  drawBanner(ctx as never, { title: "x", sub: "y", alpha: 0 }, 900, 570);
71
  expect(ops.length).toBe(0);
72
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts