Contrast is the one check I've got wrong twice, both times by being clever about backgrounds. The first version read only background-color, so text on any of the gradient-painted sheets was measured against transparency and the dealer puck's dark "D" was reported at 1.2:1 against nothing. The second over-corrected and treated low-alpha gradient stops as opaque, scoring white-on-dark at 1.0:1. So this one is built to be incapable of false positives rather than exact. Backgrounds composite through alpha; 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. It's checked against known answers before being believed: black on white at 21:1, white on white at 1:1, #767676 on white at the 4.54:1 AA boundary, white over a dark gradient, dark text on that same gradient, white over a 6% white wash on a near-black page. Nine cases, all correct — and building them found a real bug in the check, which walked past an opaque gradient and offered the page background as a candidate. That made it too lenient, which is as bad as the old false positives. What it found: Both blind badges failed against their own gradients, in 9px bold text a dealer reads at a glance across a table. At the worst stop: small blind 3.52:1, big blind 2.15:1. The small blind keeps its blue and darkens to 4.77:1. The big blind flips to dark text on brighter gold, 5.84:1 — how a gold chip is marked anyway, and it makes the two badges far easier to tell apart at speed. Quick Split's footnote names a control the dealer is told to reach for, and that name inherited the muted grey of the surrounding aside: 3.39:1. Emphasised text carrying an instruction gets its own colour now. The felt watermark came back at 1.19:1 and is meant to. It's decoration; holding it to a reading threshold would mean making it not a watermark. It's aria-hidden now, which says the same thing where it also does some good, and the audit skips aria-hidden subtrees. 32 contrast passes over the table, all five overlays and Quick Split at five viewports: zero findings, zero unmeasured. Layout audit still clean. 137 tests pass. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
78 lines
3.8 KiB
Markdown
78 lines
3.8 KiB
Markdown
# 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.
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
node tests/holdem/runUiAudit.js # needs Playwright and the site running
|
|
```
|
|
|
|
It checks WCAG AA contrast, 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.
|
|
|
|
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.
|