// Sitting people down, which is the first thing anybody does with the tool. // // Naming a chair and dealing that chair in were two separate acts and the second was // easy to miss. A dealer setting up typed six names, got six seats that still weren't // in the hand, and the action bar hid itself because by its reckoning nobody was // playing: six people at the table and a blank screen with no way forward. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { HoldemState, Betting, ActionBar, makeTable } = require("./load"); const emptyTable = (seatCount = 10) => { const state = HoldemState.createDefault(); HoldemState.resizeSeats(state, seatCount); return state; }; const bar = state => Object.assign(Object.create(ActionBar), { state }); describe("the bar knows the difference between an empty table and a named one", () => { test("a table nobody has touched gets no bar", () => { assert.equal(bar(emptyTable()).isRunning(), false, "an empty felt stays uncluttered"); }); test("a table with names on it gets one, dealt in or not", () => { const state = emptyTable(); state.seats[0].name = "Ann"; state.seats[1].name = "Ben"; assert.equal(Betting.liveSeats(state).length, 0, "nobody is in the hand"); assert.equal(bar(state).isRunning(), true, "but there are people at the table to say something to"); }); test("whitespace isn't a player", () => { const state = emptyTable(); state.seats[0].name = " "; assert.equal(bar(state).isRunning(), false); }); test("a table that is playing still gets one", () => { assert.equal(bar(makeTable({ seatCount: 6, seated: [0, 1] })).isRunning(), true); }); }); describe("naming an empty chair sits somebody down", () => { // What the seat editor's name field does, as the rule it applies rather than // through the DOM: a chair with no name and nobody in it is empty, and putting a // name on it is somebody arriving. const nameSeat = (player, name) => { const wasEmpty = !player.name.trim() && !player.inHand; player.name = name; if (wasEmpty && player.name.trim()) { player.inHand = true; player.folded = false; } }; test("an empty chair gets dealt in", () => { const state = emptyTable(); nameSeat(state.seats[0], "Ann"); assert.equal(state.seats[0].inHand, true); assert.equal(state.seats[0].folded, false); }); test("six names is six players, not six benched seats", () => { const state = emptyTable(); state.defaultStack = 200; // a table that has told the tool what a buy-in is ["Ann", "Ben", "Cal", "Dee", "Eve", "Fay"].forEach((name, i) => nameSeat(state.seats[i], name)); assert.equal(state.seats.filter(p => p.inHand).length, 6); assert.equal(bar(state).isRunning(), true); assert.ok(Betting.blindPreview(state), "and there's a hand to deal"); }); test("renaming somebody who is sitting out doesn't deal them back in", () => { // A correction, not an arrival. Dealing them in over a typo would be the tool // overruling a dealer who sat them out on purpose. const state = emptyTable(); state.seats[0].name = "Ann"; state.seats[0].inHand = false; nameSeat(state.seats[0], "Anne"); assert.equal(state.seats[0].inHand, false, "still sitting out, now spelled right"); }); test("clearing a name doesn't sit anybody down", () => { const state = emptyTable(); nameSeat(state.seats[0], "Ann"); nameSeat(state.seats[0], ""); assert.equal(state.seats[0].name, ""); }); test("a chair already in the hand is left alone", () => { const state = makeTable({ seatCount: 6, seated: [0] }); state.seats[0].folded = true; nameSeat(state.seats[0], "Ann"); assert.equal(state.seats[0].folded, true, "mid-hand a fold is not undone by typing"); }); }); describe("the way back from a table where everybody is sat out", () => { test("there are named seats to offer", () => { // Can't be reached by naming any more, but a table where everyone was sat out // — or a save from before naming seated people — still lands here. const state = emptyTable(); ["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; }); const benched = state.seats .map((player, index) => ({ player, index })) .filter(({ player }) => (player.name || "").trim() && !player.inHand) .map(({ index }) => index); assert.deepEqual(benched, [0, 1, 2]); assert.equal(Betting.blindPreview(state), null, "no hand to deal until they're in"); }); test("dealing them all in makes a hand possible", () => { const state = emptyTable(); ["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].stack = 100; }); state.seats.forEach(player => { if ((player.name || "").trim()) { player.inHand = true; player.folded = false; } }); assert.equal(Betting.liveSeats(state).length, 3); assert.ok(Betting.blindPreview(state), "the table can be dealt"); }); test("an unnamed chair is never swept in by it", () => { const state = emptyTable(); state.seats[0].name = "Ann"; state.seats.forEach(player => { if ((player.name || "").trim()) { player.inHand = true; } }); assert.equal(state.seats.filter(p => p.inHand).length, 1, "empty chairs stay empty"); }); }); describe("a table where nobody has bought in", () => { // Different from a table that has busted out, and the difference is the whole // point: one needs a starting stack named, the other needs a rebuy. Telling a // dealer their brand-new players have "no chips left" sends them hunting for the // wrong thing. const neverBoughtIn = state => state.seats.every(p => !(Number(p.buyIns) > 0) && !(Number(p.stack) > 0)); test("six freshly named players have never bought in", () => { const state = emptyTable(); ["Ann", "Ben", "Cal", "Dee", "Eve", "Fay"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; }); assert.equal(neverBoughtIn(state), true); assert.equal(Betting.blindPreview(state), null, "and there is no hand to deal"); }); test("a table that has busted out is not the same thing", () => { const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 200 }); state.seats.forEach(p => { p.buyIns = 200; p.stack = 0; }); assert.equal(neverBoughtIn(state), false, "they had chips — they lost them"); }); test("one player still holding chips is not a never-bought-in table either", () => { const state = emptyTable(); ["Ann", "Ben"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; }); state.seats[0].stack = 50; assert.equal(neverBoughtIn(state), false); }); test("naming a starting stack is enough to deal", () => { // The one field the message now points at. A seat with no stack of its own // falls back to the table default, so setting it seats everybody's chips at // once — which is why it's the right thing to send the dealer to. const state = emptyTable(); ["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; }); assert.equal(Betting.blindPreview(state), null); state.defaultStack = 200; assert.ok(Betting.blindPreview(state), "one number and the table can play"); }); });