mirror of
https://github.com/tiennm99/loto.git
synced 2026-09-02 14:20:46 +00:00
refactor(player-board): extract auto-tick into pure helper + tests
Pull the bus-driven auto-tick effect body out of PlayerBoard.svelte
into `src/lib/auto-tick.js` so the dedup-by-`at` invariant — the one
that already caught a P0 — is unit-testable without mounting Svelte.
The effect is now a thin wrapper that calls `processAutoTick()` and
applies the returned `{crossed, lastHandledAt, changed}`.
8 vitest cases cover NEW draw, dedup on same `at`, re-cross after
manual untick, mode=master/player ignored (timestamp still advances),
off-board number, null lastDraw, and null grid.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|------|---------|
|
||||
| `src/lib/game-logic.js` | Stateless utilities: generateGrid (constraint-aware picker — exact 5 per row & per col, ascending-sorted columns, soft "no 3 consecutive filled cols per row" via rejection sampling), saveGrid, loadGrid, saveCrossedState, loadCrossedState, isRowComplete, getWaitingNumber. |
|
||||
| `src/lib/call-bus.svelte.js` | Pub/sub for master draws → player auto-tick. Reactive `bus.lastDrawn` slot (emits `{ num, at }`). Used in `mode: "both"` to auto-mark master-called numbers on player board. |
|
||||
| `src/lib/auto-tick.js` | Pure `processAutoTick({grid, crossed, lastDraw, lastHandledAt, mode})` extracted from PlayerBoard's bus-driven $effect. Owns the dedup-by-`at` invariant: `lastHandledAt` advances on every NEW timestamp (even no-op draws) so reactive re-runs from `crossed`/`grid` changes never re-fire a stale draw. |
|
||||
| `src/lib/vietnamese-number.js` | `numberToVietnamese(n)` — pure utility mapping 0..90 to spoken Vietnamese, with tonal exceptions (15 → "mười lăm", 21 → "hai mươi mốt", 25 → "hai mươi lăm"). Out-of-range falls back to `String(n)`. |
|
||||
| `src/lib/voice.js` | Bundled-MP3 playback. Exports `playNumber(n)`, `playWaiting(n)` (sequences cho + N), `playBingo()`, `cancelPlayback()`. Lazy `<audio>` cache, cancel-then-play, token-based cancel ensures stale promises can't resume after a new event. Reads active voice from `settings.voice`; URLs go through `import { base } from "$app/paths"` for basePath safety. |
|
||||
| `src/lib/audio-manifest.js` | Re-exports `static/audio/manifest.json` as `VOICES` (array) + `VOICE_IDS` (Set) + `DEFAULT_VOICE`. Manifest is generated by `scripts/generate-audio.py`. |
|
||||
@@ -38,6 +39,7 @@
|
||||
| `src/lib/game-logic.test.js` | 27 unit tests: generateGrid shape (9×9, 5 per row/col, no duplicates), column ranges & ascending sort, no-3-consecutive soft constraint, row completion, waiting number detection, persistence (saveGrid/loadGrid/saveCrossedState/loadCrossedState with validators). |
|
||||
| `src/lib/settings-store.test.js` | 31 unit tests: defaults (incl. voice keys), loadSettings (restore 8 keys, apply CSS vars, toggle dark class, handle empty/corrupt), saveSettings, resetSettings, theme toggle (auto → OS pref detection), master mode, auto-call + speed, color validation, voice round-trip + invalid-id fallback. |
|
||||
| `src/lib/vietnamese-number.test.js` | 40 unit tests: ones (0–9), teens (10–19 incl. mười lăm), 20–90 incl. mốt and lăm exceptions, out-of-range fall-through. |
|
||||
| `src/lib/auto-tick.test.js` | 8 unit tests for `processAutoTick`: NEW draw flips cell, dedup on same `at`, re-cross after manual untick, mode=master/player ignored (timestamp still advances), off-board number no-op, null lastDraw, null grid + empty crossed. |
|
||||
|
||||
### Configuration & PWA
|
||||
| File | Purpose |
|
||||
|
||||
Reference in New Issue
Block a user