Files
vyndr/tests/unit/ds2Dashboard.test.js
builtbykev fe294a5de3 DS2: Dashboard Slate Rebuild — one hero, pending collapse, never-empty hero
DESIGN-SPEC Parts 3 + 6 (audit #1, #13, #14). The founder's named #1 rebuild.

slateAdapter.js — the testable engine:
- selectTopGrades: rank tonight's grades by tier → confidence → edge so the
  row varies on a real signal, not identical-weight noise (#13).
- buildHeroReceipts: yesterday's PROVEN A-tier settled HITS (misses excluded),
  carrying the real result — the never-empty proof source (#1, Part 6).
- heroFallbackState: tonight wins, else receipts, else empty.
- pendingSummary: collapse an all-awaiting card's six "Grades post …" rows to
  ONE line count (#14).
- topReadForCard: the single best live graded read to promote (#2).

GameCard.tsx — ONE bold hero per card (large mono/tabular grade + player, rest
demoted); all-awaiting cards render one "N props pending · grade ~X ET" line via
nextRunLabelET instead of repeated filler. Real team logos + team-colored accent
already lead the card (DS0) — preserved.

dashboard/page.tsx — Top grades tonight ranked via selectTopGrades (+ % CONF the
varying signal); when tonight is empty, fetch /api/ledger/model and fall back to
yesterday's PROVEN A-tier receipts (✓ HIT + actual + CLV) so first paint always
proves the model. Honest nextRunLabelET copy kept for the truly-empty case (QA.22).

Tests: tests/unit/ds2Dashboard.test.js (21) — pure-fn + source assertions,
fail-before / pass-after. Full suite 237 suites / 2863 tests green (+21).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 00:07:06 -04:00

195 lines
9.0 KiB
JavaScript

// DS2 (Design v2) — Dashboard Slate Rebuild. The founder's named #1 rebuild:
// real entities per card, ONE bold hero, collapse pending-filler (#14), and the
// NEVER-EMPTY hero (#1, Part 6) — first paint ALWAYS proves the model. The pure
// engine lives in web/src/lib/slateAdapter.js (unit-tested directly); the .tsx
// surfaces are asserted against source (plain-JS Jest) — same pattern as
// ds4Billboards / vyndrParityQA.
const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '..', '..');
const WEB = path.join(ROOT, 'web', 'src');
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');
const adapter = require('../../web/src/lib/slateAdapter');
// ── 1. selectTopGrades — ranking VARIES on a real signal (#13) ─────────────
describe('selectTopGrades — tier first, then the varying signal (confidence/edge)', () => {
test('orders by grade tier, then confidence desc, then edge desc', () => {
const grades = [
{ player: 'C-low', grade: 'B', confidence: 40, edge: 1 },
{ player: 'A-hiconf', grade: 'A', confidence: 90, edge: 2 },
{ player: 'A-loconf', grade: 'A', confidence: 55, edge: 9 },
{ player: 'Aplus', grade: 'A+', confidence: 10, edge: 0 },
];
const out = adapter.selectTopGrades(grades, 10).map((g) => g.player);
expect(out).toEqual(['Aplus', 'A-hiconf', 'A-loconf', 'C-low']);
});
test('ties on grade break on confidence — rows are NOT identical-order noise', () => {
const grades = [
{ player: 'X', grade: 'B', confidence: 30 },
{ player: 'Y', grade: 'B', confidence: 80 },
{ player: 'Z', grade: 'B', confidence: 55 },
];
expect(adapter.selectTopGrades(grades, 10).map((g) => g.player)).toEqual(['Y', 'Z', 'X']);
});
test('honors the limit and drops gradeless rows; empty in → empty out', () => {
const grades = [{ grade: 'A', confidence: 1 }, { grade: 'A', confidence: 2 }, { confidence: 9 }];
expect(adapter.selectTopGrades(grades, 1)).toHaveLength(1);
expect(adapter.selectTopGrades([], 5)).toEqual([]);
expect(adapter.selectTopGrades(null, 5)).toEqual([]);
});
});
// ── 2. buildHeroReceipts — yesterday's A-tier settled HITS (#1) ────────────
describe('buildHeroReceipts — proven A-tier receipts, misses excluded', () => {
const rows = [
{ player_name: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A+', outcome: 'hit', actual_value: 3, clv_result: 'beat', sport: 'mlb' },
{ player_name: 'Missed Guy', stat: 'hits', line: 1.5, side: 'over', grade: 'A', outcome: 'miss', actual_value: 0 },
{ player_name: 'B Guy', stat: 'runs', line: 0.5, side: 'over', grade: 'B', outcome: 'hit', actual_value: 1 },
{ player_name: 'Aminus Hit', stat: 'rbi', line: 0.5, side: 'over', grade: 'A-', outcome: 'hit', actual_value: 2 },
];
test('keeps only A-family grades that HIT (a miss never becomes proof)', () => {
const out = adapter.buildHeroReceipts(rows, 8);
const names = out.map((r) => r.player);
expect(names).toContain('Aaron Judge');
expect(names).toContain('Aminus Hit');
expect(names).not.toContain('Missed Guy'); // A-tier but a MISS
expect(names).not.toContain('B Guy'); // a hit but not A-tier
});
test('carries the real result (actual + grade) so the receipt is verifiable', () => {
const judge = adapter.buildHeroReceipts(rows, 8).find((r) => r.player === 'Aaron Judge');
expect(judge).toMatchObject({ grade: 'A+', outcome: 'hit', actual: 3 });
});
test('best-grade first, honors the limit, empty/absent → []', () => {
expect(adapter.buildHeroReceipts(rows, 8)[0].grade).toBe('A+');
expect(adapter.buildHeroReceipts(rows, 1)).toHaveLength(1);
expect(adapter.buildHeroReceipts([], 5)).toEqual([]);
expect(adapter.buildHeroReceipts(null, 5)).toEqual([]);
});
});
// ── 3. heroFallbackState — the NEVER-EMPTY hero engine (#1, Part 6) ────────
describe('heroFallbackState — tonight wins, else receipts, never empty when proof exists', () => {
const settled = [{ player_name: 'Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A', outcome: 'hit', actual_value: 3 }];
test('tonight has graded props → mode tonight', () => {
const s = adapter.heroFallbackState([{ player: 'P', grade: 'A', confidence: 70 }], settled, 10);
expect(s.mode).toBe('tonight');
expect(s.items).toHaveLength(1);
});
test('tonight EMPTY but yesterday has A-tier hits → mode receipts (proof, not empty)', () => {
const s = adapter.heroFallbackState([], settled, 10);
expect(s.mode).toBe('receipts');
expect(s.items[0].player).toBe('Judge');
});
test('only truly empty when BOTH are absent', () => {
expect(adapter.heroFallbackState([], [], 10).mode).toBe('empty');
expect(adapter.heroFallbackState(null, null, 10).mode).toBe('empty');
});
});
// ── 4. pendingSummary — collapse the dead "Grades post" repetition (#14) ──
describe('pendingSummary — six pending rows collapse to ONE line', () => {
test('all-awaiting card → one summary counting props + players', () => {
const strips = [
{ player: 'One', props: [{ stat: 'TB', line: 1.5, awaiting: true }, { stat: 'HR', line: 0.5, awaiting: true }] },
{ player: 'Two', props: [{ stat: 'Hits', line: 0.5, awaiting: true }] },
];
const s = adapter.pendingSummary(strips);
expect(s).not.toBeNull();
expect(s.count).toBe(3);
expect(s.players).toBe(2);
});
test('any graded prop present → no collapse (the card has real reads to show)', () => {
const strips = [
{ player: 'One', props: [{ stat: 'TB', line: 1.5, grade: 'A' }] },
{ player: 'Two', props: [{ stat: 'Hits', line: 0.5, awaiting: true }] },
];
expect(adapter.pendingSummary(strips)).toBeNull();
});
test('no awaiting props at all → null', () => {
expect(adapter.pendingSummary([{ player: 'One', props: [] }])).toBeNull();
expect(adapter.pendingSummary([])).toBeNull();
expect(adapter.pendingSummary(null)).toBeNull();
});
});
// ── 5. topReadForCard — the ONE bold hero per card (#2) ────────────────────
describe('topReadForCard — the single best graded prop leads the card', () => {
const strips = [
{ player: 'Mid', team: 'NYY', props: [{ stat: 'Hits', line: 0.5, side: 'O', grade: 'B' }] },
{ player: 'Best', team: 'BOS', archetype: { primary: 'BOMBER' }, props: [{ stat: 'TB', line: 1.5, side: 'O', grade: 'A+' }] },
{ player: 'Dead', team: 'LAD', props: [{ stat: 'HR', line: 0.5, side: 'O', grade: 'A+', dead: true }] },
];
test('picks the highest-tier live grade (dead reads never lead)', () => {
const top = adapter.topReadForCard(strips);
expect(top).not.toBeNull();
expect(top.player).toBe('Best');
expect(top.grade).toBe('A+');
expect(top.stat).toBe('TB');
});
test('no graded props → null (nothing to promote)', () => {
expect(adapter.topReadForCard([{ player: 'X', props: [{ stat: 'TB', line: 1, awaiting: true }] }])).toBeNull();
expect(adapter.topReadForCard([])).toBeNull();
});
});
// ── 6. GameCard.tsx — one hero + pending collapse + real entities ─────────
describe('GameCard.tsx wires the DS2 rebuild', () => {
const src = read('components/vyndr/GameCard.tsx');
test('leads with the real team entity (logo) + a team-colored accent edge', () => {
expect(src).toContain('<TeamLogo');
expect(src).toContain('accentColor(g.home.abbr');
expect(src).toContain('borderLeft');
});
test('promotes ONE bold hero read via topReadForCard (large mono/tabular grade)', () => {
expect(src).toContain('topReadForCard');
expect(src).toMatch(/fontVariantNumeric:\s*'tabular-nums'/);
// the hero grade badge is the largest on the card (strips use size 'sm').
expect(src).toMatch(/size=\{48\}|size="lg"/);
});
test('collapses all-awaiting cards to ONE pending line (kills the #14 repetition)', () => {
expect(src).toContain('pendingSummary');
expect(src).toContain('nextRunLabelET');
expect(src).toContain('props pending');
});
});
// ── 7. dashboard/page.tsx — the never-empty hero + varying rankings ────────
describe('dashboard Top grades tonight — proof-first, ranked, honest', () => {
const src = read('app/dashboard/page.tsx');
test('uses the never-empty engine (heroFallbackState / buildHeroReceipts)', () => {
expect(src).toMatch(/heroFallbackState|buildHeroReceipts/);
});
test('ranks tonight grades by the varying signal (selectTopGrades) and shows confidence', () => {
expect(src).toContain('selectTopGrades');
expect(src).toContain('confidence');
});
test('falls back to yesterday PROVEN A-tier receipts (with the settled result)', () => {
expect(src).toContain('/api/ledger/model');
expect(src).toMatch(/HIT|PROVEN|YESTERDAY/);
});
test('still keeps the honest schedule-derived waiting copy for the truly-empty case (QA.22)', () => {
expect(src).toContain('nextRunLabelET');
});
});