import { describe, expect, it } from "vitest"; import { renderBoard, renderGuess } from "../../../src/modules/semantle/render.js"; describe("semantle/render", () => { describe("renderBoard", () => { it("shows round ready prompt when no guesses", () => { const result = renderBoard([]); expect(result).toContain("🎯 Semantle — 0 guesses"); expect(result).toContain("🆕 Round ready"); expect(result).toContain("/semantle"); }); it("shows singular 'guess' for exactly one guess", () => { const result = renderBoard([{ word: "test", canonical: "test", similarity: 0.5 }]); expect(result).toContain("1 guess"); expect(result).not.toContain("guesses"); }); it("shows plural 'guesses' for multiple guesses", () => { const result = renderBoard([ { word: "test", canonical: "test", similarity: 0.5 }, { word: "best", canonical: "best", similarity: 0.6 }, ]); expect(result).toContain("2 guesses"); }); it("sorts guesses by similarity descending", () => { const guesses = [ { word: "low", canonical: "low", similarity: 0.2 }, { word: "high", canonical: "high", similarity: 0.9 }, { word: "mid", canonical: "mid", similarity: 0.5 }, ]; const result = renderBoard(guesses); const lines = result.split("\n"); // Find index of highest and lowest in the pre block const highIdx = lines.findIndex((l) => l.includes("high")); const midIdx = lines.findIndex((l) => l.includes("mid")); const lowIdx = lines.findIndex((l) => l.includes("low")); expect(highIdx).toBeLessThan(midIdx); expect(midIdx).toBeLessThan(lowIdx); }); it("caps display to top 15 guesses", () => { const guesses = Array.from({ length: 20 }, (_, i) => ({ word: `word${i}`, canonical: `word${i}`, similarity: 1 - i * 0.05, })); const result = renderBoard(guesses); expect(result).toContain("5 older guesses hidden"); expect(result).toContain("word0"); expect(result).not.toContain("word15"); expect(result).not.toContain("word19"); }); it("marks latest guess with arrow emoji", () => { const guesses = [ { word: "old", canonical: "old", similarity: 0.7 }, { word: "new", canonical: "new", similarity: 0.3 }, ]; const result = renderBoard(guesses, "new"); const lines = result.split("\n"); const newLine = lines.find((l) => l.includes("new")); expect(newLine).toMatch(/^➡️/); }); it("shows plain marker for non-latest guesses", () => { const guesses = [ { word: "old", canonical: "old", similarity: 0.7 }, { word: "new", canonical: "new", similarity: 0.3 }, ]; const result = renderBoard(guesses, "new"); // Extract lines from
...block const preMatch = result.match(/
([\s\S]*?)<\/pre>/);
expect(preMatch).toBeTruthy();
const preContent = preMatch[1];
const lines = preContent.split("\n");
// Old row should start with plain marker (two spaces)
const oldLine = lines.find((l) => l.includes("old"));
expect(oldLine).toMatch(/^ {2}/);
});
it("escapes HTML special characters in canonical", () => {
const guesses = [{ word: "