Files
game-scoreboard/src/components/admin/share-panel.test.ts
T
joshuaandClaude Opus 5.5 bbcf84bbc3 Phase 10: team buzzers
Each team gets a buzzer link (/b/[token]); a game opts in with a
"Teams buzz in" checkbox. The database's clock orders the buzzes, so
the result doesn't depend on phone clocks. The control room lists the
order and clears it per question (B); the scoreboard shows who buzzed
first with a ding.

Buzzes follow the existing rules: token-gated route handlers with the
service-role client, RLS on the new table, and data-free realtime pings
on a new buzzer:{eventId} channel.

Needs supabase/migrations/0003_buzzer.sql applied before deploying.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:33:44 +02:00

33 lines
1.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
vi.mock("@/lib/env.public", () => ({ publicEnv: { NEXT_PUBLIC_SITE_URL: "https://gamenight.example" } }));
const { buzzerLinksText, judgeLinksText } = await import("./share-panel");
const judge = (name: string, token: string, revoked = false) =>
({ id: token, game_id: "g", name, token, revoked, source: "nominated", created_at: "" }) as const;
describe("judgeLinksText", () => {
it("lists one line per active judge, skipping revoked ones", () => {
const text = judgeLinksText({
title: "Karaoke",
judges: [judge("Eben", "aaa"), judge("Gone", "bbb", true), judge("Melissa", "ccc")],
});
expect(text).toBe(
"Karaoke — judge links\nEben: https://gamenight.example/j/aaa\nMelissa: https://gamenight.example/j/ccc",
);
});
});
describe("buzzerLinksText", () => {
it("lists one line per team", () => {
const text = buzzerLinksText([
{ name: "Lions", buzzer_token: "aaa" },
{ name: "Eagles", buzzer_token: "bbb" },
]);
expect(text).toBe(
"Buzzer links\nLions: https://gamenight.example/b/aaa\nEagles: https://gamenight.example/b/bbb",
);
});
});