Files
jheapsandClaude Opus 5 ab92259e3e Put the dealer's own notes in the file they hand over
The CSV export carries the hands, the standings and the settle-up. It carried neither
of the two places a dealer types context.

Each seat can hold custom fields -- "IOU: $20 to Ann", "Seat rent: paid to 2am",
"Venmo: @ann" -- and the table has a notes box for the rest: side bets, a high-hand
promo, who owes the box from last week. Both are typed precisely because they matter to
settling up, and both were visible only inside the tool. The one place the context was
written down was the one place it could not leave.

Now the standings rows end with a Notes column carrying that seat's fields, and the
table's notes go in a section of their own at the end.

  Player,Paid In,Won,Net,Bought In,Chips Now,Up/Down,Notes
  Ann,20.00,60.00,40.00,200.00,150.00,-50.00,Seat rent: paid to 2am; Venmo: @ann
  Ben,20.00,0.00,-20.00,200.00,200.00,0.00,IOU: $20 to Ann
  Cal,20.00,0.00,-20.00,200.00,250.00,50.00,

  Table Notes
  "High-hand promo until midnight.
  Mo owes the box $15, from last week."

Notes go last so the money columns keep the shape a spreadsheet expects, and the
multi-line note is quoted as one cell rather than becoming rows that read as more
players.

An existing test caught this, correctly

"every money cell parses as a number" asserted that every column after the name was
numeric, and a Notes column is text by definition. The invariant it protects is real --
the file exists so somebody can total a column -- so it now names the six money columns
and checks those, rather than "everything after the first". That is the assertion it
always meant.

9 tests (278 total): a seat's fields ride along, the money columns keep their order, a
seat with nothing written gets an empty cell rather than the word undefined, a blank
field does not leave a stray colon, a multi-line note survives as one cell, no notes
means no section rather than an empty heading, and asking for the standings does not
change the seats.

Driven through the real Export CSV button, not just the function: the downloaded file
carries both seats' notes and the table's. Audit and contrast clean, no console errors.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
2026-09-01 19:52:20 -06:00
..
2026-09-01 12:34:53 -06:00
2026-09-01 12:52:56 -06:00
2026-09-01 12:52:56 -06:00

Hold'em dealer tests

Unit tests for the money logic behind /Casino/TexasHoldem — the betting engine, the side-pot calculator, the split calculator and the table state.

node --test tests/holdem/*.test.js

No install step, no package.json, no dependencies. The browser modules are plain script-tag files that each end with a module.exports guard, so Node can require them as-is; load.js puts them on globalThis under the same names they use to find each other in the page, in the load order from TexasHoldem.cshtml.

What's covered

File Covers
potCalculator.test.js Main and side pots, dead money, uncalled bets, odd-cent distribution
betting.test.js Blinds, street completion, raises reopening the action, all-in run-outs
state.test.js Button rotation, which seats post blinds, session standings
splitCalculator.test.js Chopped pots, odd chips, unmakeable denominations
invariants.test.js 1000 randomised hands per property (see below)

The layout audit

The unit tests above cover the money. uiAudit.js covers the thing you can only see in a browser: whether a dealer can actually reach every control.

node tests/holdem/runUiAudit.js          # needs Playwright and the site running

It checks WCAG AA contrast, touch target sizes, touch-action and tap highlights, that fields stay selectable and are large enough not to trigger iOS zoom, that nothing important is covered or off-screen with no way to scroll to it, that no grid column has collapsed to nothing, and that no text is painted outside its box. It runs over the table, all five overlays and Quick Split, at thirteen viewports from a 360px phone to a 1600px desktop and the same phones on their side.

Deliberately not in CI: the unit suite is dependency-free and runs in a second, while this one needs a browser and a running site. Run it after any change that adds or resizes a control. With no Playwright to hand, paste uiAudit.js into devtools and call LayoutAudit.runAll().

The contrast check is built so it cannot report a false positive: backgrounds composite through alpha, and where a gradient is involved every colour stop is tried and the most flattering one used. A finding means the text fails even under the reading most generous to it. Two earlier attempts at this got it wrong in both directions — one read only background-color and scored text on a gradient against nothing, the other treated low-alpha stops as opaque — so uiAudit.js is checked against known-answer cases before it is believed.

Touch targets are measured by probing outward with elementFromPoint rather than by reading the element box, because the box lies in both directions: a small control can be padded out with a transparent ::after and respond well beyond its painted size, and a checkbox inside a label is hit by tapping the label. The 44px rule is only enforced where hover: none says there's no mouse.

It exists because these checks were being re-typed into a console every time something changed, and each time they only covered whatever had just been touched. That is how the standings table shipped with a player-name column zero pixels wide and kept it for two rounds.

Why the randomised suite exists

Every correctness bug found in this tool so far has been shape-dependent: blinds posted to empty chairs, the button skipping past players, heads-up acting out of order, an overbet announced as a side pot. None of them show up on a full six-handed table with equal stacks, which is the table anyone writes a fixed example for.

invariants.test.js deals a thousand hands across random seat counts, random empty chairs, random stack depths and random dealer input, and checks the things a dealer would notice being wrong:

  • chips are never created or destroyed during play
  • the pot breakdown plus the uncalled returns always equal the money that went in
  • paying the pot out settles it to the cent
  • once uncalled chips are handed back, nobody is still betting above the whole table
  • the button and both blinds always land on players who are actually in the hand

The generator is seeded, so a failure reproduces from the seed printed in the message rather than being a flake to re-run.