import { describe, it, expect, beforeAll, afterAll } from "vitest"; import { startTestServer, createBoard, join, type TestServer } from "./helpers.js"; let server: TestServer; beforeAll(async () => { server = await startTestServer(); }); afterAll(async () => { await server.close(); }); describe("Socket.IO — flujo de retro", () => { it("oculta las notas de otros hasta revelar", async () => { const board = await createBoard(server.url); const alice = await join(server.url, board.boardId, "alice", "Alice"); const bob = await join(server.url, board.boardId, "bob", "Bob"); const colId = alice.last!.columns[0].id; alice.emit("note:add", { columnId: colId, text: "secreto de Alice" }); const bobState = await bob.waitFor((s) => s.notes.length === 1); expect(bobState.notes[0].text).toBe(""); // oculta para Bob expect(bobState.notes[0].authorName).toBe("Alice"); const aliceState = await alice.waitFor((s) => s.notes.length === 1); expect(aliceState.notes[0].text).toBe("secreto de Alice"); // visible para la autora alice.close(); bob.close(); }); it("revela las notas a todos cuando el moderador lo pide", async () => { const board = await createBoard(server.url); const alice = await join(server.url, board.boardId, "alice", "Alice"); const bob = await join(server.url, board.boardId, "bob", "Bob"); const colId = alice.last!.columns[0].id; alice.emit("note:add", { columnId: colId, text: "visible tras revelar" }); await bob.waitFor((s) => s.notes.length === 1); // Bob (sin token) no puede revelar; Alice (moderadora) sí bob.emit("board:reveal", { revealed: true }); alice.emit("board:reveal", { revealed: true, moderatorToken: board.moderatorToken }); const revealed = await bob.waitFor((s) => s.revealed === true); expect(revealed.notes[0].text).toBe("visible tras revelar"); alice.close(); bob.close(); }); it("respeta el límite de votos por persona", async () => { const board = await createBoard(server.url, { votesPerUser: 2 }); const alice = await join(server.url, board.boardId, "alice", "Alice"); const colId = alice.last!.columns[0].id; for (const txt of ["a", "b", "c"]) alice.emit("note:add", { columnId: colId, text: txt }); const withNotes = await alice.waitFor((s) => s.notes.length === 3); const ids = withNotes.notes.map((n) => n.id); const mine = (s: { votes: { participantId: string }[] }) => s.votes.filter((v) => v.participantId === "alice").length; // vota las dos primeras (confirmando cada una) alice.emit("vote:toggle", { noteId: ids[0] }); await alice.waitFor((s) => mine(s) === 1); alice.emit("vote:toggle", { noteId: ids[1] }); await alice.waitFor((s) => mine(s) === 2); // el tercer voto se rechaza (cupo = 2) alice.emit("vote:toggle", { noteId: ids[2] }); await new Promise((r) => setTimeout(r, 200)); expect(mine(alice.last!)).toBe(2); // quitar un voto libera cupo alice.emit("vote:toggle", { noteId: ids[0] }); const afterUnvote = await alice.waitFor((s) => mine(s) === 1); expect(afterUnvote.votes).toHaveLength(1); alice.close(); }); it("comparte el timer (endsAt) con todos los participantes", async () => { const board = await createBoard(server.url); const alice = await join(server.url, board.boardId, "alice", "Alice"); const bob = await join(server.url, board.boardId, "bob", "Bob"); alice.emit("timer:start", { seconds: 300, moderatorToken: board.moderatorToken }); const bobState = await bob.waitFor((s) => s.timerRunning); expect(bobState.timerEndsAt).toBeTruthy(); expect(new Date(bobState.timerEndsAt!).getTime()).toBeGreaterThan(Date.now()); alice.close(); bob.close(); }); it("crea action items visibles para todos", async () => { const board = await createBoard(server.url); const alice = await join(server.url, board.boardId, "alice", "Alice"); const bob = await join(server.url, board.boardId, "bob", "Bob"); alice.emit("actionitem:add", { text: "documentar el deploy" }); const bobState = await bob.waitFor((s) => s.actionItems.length === 1); expect(bobState.actionItems[0].text).toBe("documentar el deploy"); alice.close(); bob.close(); }); });