Files
sternboard/server/test/socket.test.ts
T
Millaguie f11ea58c2c
CI/CD Pipeline / test (push) Successful in 26s
CI/CD Pipeline / build-and-push (push) Successful in 44s
Sternboard: free retro board, no signup (initial)
Servidor Fastify + Socket.IO + Drizzle/Postgres; front Vite+React (EN/ES, tema claro/oscuro).
Tableros por columnas con plantillas, notas privadas/públicas, agrupación, votos
configurables (multivoto), timer compartido con alarma, fases, action items,
export Markdown/JSON e import, autodestrucción, gestión de columnas.
Imagen única (front servido por el servidor) y CI que la construye y publica.
2026-06-16 10:27:33 +02:00

109 lines
4.2 KiB
TypeScript

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();
});
});