Files
skribbl-gartic-color/test/socket-gartic.js
T

146 lines
7.0 KiB
JavaScript

// Gartic mode socket flow test.
const { io } = require("socket.io-client");
const URL = "http://localhost:3000";
const TIMEOUT_MS = 30000;
const results = [];
let exitCode = 0;
const pass = (n) => { results.push({ name: n, status: "pass" }); console.log("PASS:", n); };
const fail = (n, e) => { results.push({ name: n, status: "fail", error: String(e) }); console.log("FAIL:", n, "-", e); exitCode = 1; };
function waitFor(socket, event, predicate = () => true, timeout = 8000) {
return new Promise((resolve, reject) => {
const t = setTimeout(() => reject(new Error(`timeout waiting for ${event}`)), timeout);
const handler = (data) => {
if (predicate(data)) {
clearTimeout(t);
socket.off(event, handler);
resolve(data);
}
};
socket.on(event, handler);
});
}
function emitAck(s, event, payload) {
return new Promise((resolve, reject) => {
const t = setTimeout(() => reject(new Error(`ack timeout for ${event}`)), 5000);
s.emit(event, payload, (resp) => { clearTimeout(t); resolve(resp); });
});
}
const killer = setTimeout(() => { console.log("FAIL: Global timeout"); process.exit(1); }, TIMEOUT_MS);
killer.unref();
(async () => {
let a, b, c;
try {
a = io(URL, { transports: ["websocket"], forceNew: true });
b = io(URL, { transports: ["websocket"], forceNew: true });
c = io(URL, { transports: ["websocket"], forceNew: true });
await Promise.all([
new Promise((r) => a.on("connect", r)),
new Promise((r) => b.on("connect", r)),
new Promise((r) => c.on("connect", r)),
]);
pass("gartic: three clients connected");
// A creates gartic room
const create = await emitAck(a, "room:create", {
nickname: "Alice", avatar: "🐱", mode: "gartic",
settings: {},
});
if (!create || !create.ok) throw new Error("create failed: " + JSON.stringify(create));
const code = create.code;
pass("gartic: room:create ok, code=" + code);
// B and C join
const j1 = await emitAck(b, "room:join", { code, nickname: "Bob", avatar: "🐶" });
if (!j1 || !j1.ok) throw new Error("B join failed: " + JSON.stringify(j1));
pass("gartic: B joined");
const j2 = await emitAck(c, "room:join", { code, nickname: "Carol", avatar: "🦊" });
if (!j2 || !j2.ok) throw new Error("C join failed: " + JSON.stringify(j2));
pass("gartic: C joined");
// Each client should receive a "gartic:turn" with task=prompt after game:start
const aTurn0P = waitFor(a, "gartic:turn", () => true, 5000);
const bTurn0P = waitFor(b, "gartic:turn", () => true, 5000);
const cTurn0P = waitFor(c, "gartic:turn", () => true, 5000);
const start = await emitAck(a, "game:start", {});
if (!start || !start.ok) throw new Error("game:start: " + JSON.stringify(start));
pass("gartic: game:start ok");
const aT0 = await aTurn0P;
const bT0 = await bTurn0P;
const cT0 = await cTurn0P;
if (aT0.task !== "prompt" || bT0.task !== "prompt" || cT0.task !== "prompt") throw new Error("expected prompt task, got " + aT0.task + "/" + bT0.task + "/" + cT0.task);
pass("gartic: all three got gartic:turn task=prompt");
// All three submit prompts
const aTurn1P = waitFor(a, "gartic:turn", () => true, 8000);
const bTurn1P = waitFor(b, "gartic:turn", () => true, 8000);
const cTurn1P = waitFor(c, "gartic:turn", () => true, 8000);
const sub1 = await emitAck(a, "gartic:submit", { type: "prompt", data: "a cat" });
if (!sub1 || !sub1.ok) throw new Error("A prompt submit failed: " + JSON.stringify(sub1));
const sub2 = await emitAck(b, "gartic:submit", { type: "prompt", data: "a robot" });
if (!sub2 || !sub2.ok) throw new Error("B prompt submit failed: " + JSON.stringify(sub2));
const sub3 = await emitAck(c, "gartic:submit", { type: "prompt", data: "a tree" });
if (!sub3 || !sub3.ok) throw new Error("C prompt submit failed: " + JSON.stringify(sub3));
pass("gartic: prompts submitted ok");
const aT1 = await aTurn1P;
const bT1 = await bTurn1P;
const cT1 = await cTurn1P;
if (aT1.task !== "drawing" || bT1.task !== "drawing" || cT1.task !== "drawing") throw new Error("expected drawing task, got " + aT1.task);
pass("gartic: all three got next turn task=drawing with content");
if (!aT1.content || !bT1.content || !cT1.content) throw new Error("drawing content missing");
pass("gartic: drawing turn includes prior prompt as content");
// Submit drawings — should advance to guess phase (not done) for 3 players
const tinyImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==";
const aTurn2P = waitFor(a, "gartic:turn", () => true, 8000);
const bTurn2P = waitFor(b, "gartic:turn", () => true, 8000);
const cTurn2P = waitFor(c, "gartic:turn", () => true, 8000);
const d1 = await emitAck(a, "gartic:submit", { type: "drawing", data: tinyImg });
if (!d1 || !d1.ok) throw new Error("A drawing submit failed: " + JSON.stringify(d1));
const d2 = await emitAck(b, "gartic:submit", { type: "drawing", data: tinyImg });
if (!d2 || !d2.ok) throw new Error("B drawing submit failed: " + JSON.stringify(d2));
const d3 = await emitAck(c, "gartic:submit", { type: "drawing", data: tinyImg });
if (!d3 || !d3.ok) throw new Error("C drawing submit failed: " + JSON.stringify(d3));
pass("gartic: drawings submitted ok");
// 3 players: totalTurns = 3 -> turnIndex 0 (prompt), 1 (drawing), 2 (guess)
const aT2 = await aTurn2P;
const bT2 = await bTurn2P;
const cT2 = await cTurn2P;
if (aT2.task !== "guess" || bT2.task !== "guess" || cT2.task !== "guess") throw new Error("expected guess task, got " + aT2.task + "/" + bT2.task + "/" + cT2.task);
pass("gartic: all three got next turn task=guess");
// Submit guesses — book should complete
const bookCompleteP = waitFor(a, "gartic:bookComplete", () => true, 10000);
const g1 = await emitAck(a, "gartic:submit", { type: "guess", data: "kitty" });
if (!g1 || !g1.ok) throw new Error("A guess submit failed: " + JSON.stringify(g1));
const g2 = await emitAck(b, "gartic:submit", { type: "guess", data: "metal man" });
if (!g2 || !g2.ok) throw new Error("B guess submit failed: " + JSON.stringify(g2));
const g3 = await emitAck(c, "gartic:submit", { type: "guess", data: "plant" });
if (!g3 || !g3.ok) throw new Error("C guess submit failed: " + JSON.stringify(g3));
pass("gartic: guesses submitted ok");
const bookComplete = await bookCompleteP;
if (!bookComplete || !Array.isArray(bookComplete.books)) {
throw new Error("expected gartic:bookComplete event");
}
pass("gartic: gartic:bookComplete fired, books=" + bookComplete.books.length);
} catch (e) {
fail("gartic flow", e && e.message ? e.message : String(e));
} finally {
try { a && a.disconnect(); } catch (_) {}
try { b && b.disconnect(); } catch (_) {}
try { c && c.disconnect(); } catch (_) {}
clearTimeout(killer);
console.log("__RESULTS__ " + JSON.stringify(results));
setTimeout(() => process.exit(exitCode), 200);
}
})();