// Who can win each pot, said in the fewest names that answer it. // // The breakdown used to list every eligible player for every pot. Pot eligibility is // nested — potCalculator.test.js checks that across 20,000 tables — so the main pot's // line was every name at the table and each side pot repeated it minus one. The lines // that were too long to fit were exactly the ones nobody needed spelled out, and the // short ones at the bottom were the side pots a dealer actually announces by name. // // Measured on an iPhone 14 before this: one all-in five-handed already clipped the main // pot, and twelve-handed the first row wanted 521px of a 254px slot, with no count and // no title — a dealer reading "Alexandra…" could not tell whether three people or // eleven were eligible, which is the number the showdown turns on. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { Render } = require("./load"); // Names are "P" so a test reads as ids and the joining is still exercised. const nameFor = id => `P${id}`; const say = (eligible, everyone) => Render.describeEligible(eligible, everyone, nameFor); describe("the eligible line", () => { test("says so plainly when the whole table can win it", () => { // The main pot, every hand. Twelve names to say "nobody is excluded yet" was // the single longest line in the component and the least informative. assert.equal(say(["a", "b", "c"], ["a", "b", "c"]), "all 3"); assert.equal(say(["a"], ["a"]), "all 1"); }); test("names who is out when that is the shorter list", () => { const table = ["a", "b", "c", "d", "e", "f", "g", "h"]; assert.equal(say(["b", "c", "d", "e", "f", "g", "h"], table), "7 in, not Pa"); assert.equal(say(["c", "d", "e", "f", "g", "h"], table), "6 in, not Pa and Pb"); assert.equal(say(["d", "e", "f", "g", "h"], table), "5 in, not Pa, Pb and Pc"); }); test("names who is in when that is the shorter list", () => { // The far side pots, which are the ones a dealer announces by name: "side pot // is 80 between Ben and Carlos". const table = ["a", "b", "c", "d", "e", "f", "g", "h"]; assert.equal(say(["g", "h"], table), "2 in, Pg and Ph"); assert.equal(say(["h"], table), "1 in, Ph"); assert.equal(say(["f", "g", "h"], table), "3 in, Pf, Pg and Ph"); }); test("and when the two are the same length, says who is in", () => { // A tie is not a reason to make the dealer do the subtraction: "who can win // this" is the question, so answer it directly when it costs nothing. const table = ["a", "b", "c", "d"]; assert.equal(say(["c", "d"], table), "2 in, Pc and Pd"); }); test("leads with the count, because it is the tail that gets cut off", () => { // .potLayerWho is right-aligned with an ellipsis, so a line too long for a phone // loses its end. Every form has to begin with the number of hands to ask for. const table = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]; [table, ["c", "d", "e", "f", "g", "h", "i", "j", "k", "l"], ["k", "l"]] .forEach(eligible => assert.match(say(eligible, table), /^(all \d+|\d+ in,)/, `"${say(eligible, table)}" buries the count where a narrow screen can eat it`)); }); test("never spells out more than half the table", () => { // The point of picking the shorter side. Nested eligibility makes the two lists // complements, so this is a guarantee rather than a hope. const table = Array.from({ length: 12 }, (_, i) => `s${i}`); for (let inCount = 1; inCount <= 12; inCount++) { const line = say(table.slice(0, inCount), table); const named = (line.match(/P/g) || []).length; assert.ok(named <= 6, `${inCount} of 12 named ${named} people: "${line}"`); } }); }); // A stub rather than a DOM: the file has no jsdom and does not want one. Enough of an // element to answer what the row is made of. const fakeDom = () => { const make = () => ({ className: "", textContent: "", title: "", children: [], classList: { add() {} }, appendChild(child) { this.children.push(child); return child; }, }); global.document = { createElement: make }; return () => { delete global.document; }; }; describe("the row it builds", () => { const pots = [ { label: "Main pot", amount: 300, eligible: ["a", "b", "c", "d"] }, { label: "Side pot 1", amount: 150, eligible: ["c", "d"] }, ]; const textOf = row => row.children.map(c => c.textContent); test("carries the full list in a title, for anyone with a pointer", () => { const undo = fakeDom(); try { const wrap = Render.buildPotBreakdown(pots, [], nameFor); const who = wrap.children.map(row => row.children[2]); assert.equal(who[0].title, "Pa, Pb, Pc, Pd"); assert.equal(who[1].title, "Pc, Pd"); // The visible text is the short form; the title is the long one. A tooltip // is not a mechanism a keyboard reaches, which is why it is the extra and // not the answer. assert.equal(who[0].textContent, "all 4"); assert.equal(who[1].textContent, "2 in, Pc and Pd"); } finally { undo(); } }); test("puts the label, the money and the people in that order", () => { const undo = fakeDom(); try { const wrap = Render.buildPotBreakdown(pots, [], nameFor); assert.deepEqual(textOf(wrap.children[0]), ["Main pot", "$300", "all 4"]); } finally { undo(); } }); test("and an uncalled bet still reads as money going back", () => { // Not an eligibility question — nobody is competing for it — so it keeps its // own wording and gets no title to expand. const undo = fakeDom(); try { const wrap = Render.buildPotBreakdown(pots, [{ id: "a", amount: 25 }], nameFor); const row = wrap.children[2]; assert.deepEqual(textOf(row), ["Uncalled", "$25", "back to Pa"]); assert.equal(row.children[2].title, ""); } finally { undo(); } }); });