Files
vyndr/tests/unit/bookComparisonWiring.test.js
builtbykev 2ab2eeaa7d Wire BookComparison to the prop card (display-only, honest states)
BookComparison.tsx was built but UNROUTED (dead). Route it to the GradeResultCard
via a new self-fetching BookComparisonPanel that reads the live /api/books feed
(source:'bookprices' — the snapshot-locked, fenced, byte-identical store).

Contract fix (Review Zero 0.1): books frequently sit at DIFFERENT lines (WNBA DK
21.5 / FD 18.5; MLB 2/3), so BookComparison now renders EACH book's own line
per-row — never one shared header line implying a false same-number comparison.

Honest states: single-book (the common case for MLB) → one book, "One book
posting this prop.", NO crown/second row; multi-book → all books' own line+price,
NONE crowned (BOOK_CROWN_ENABLED=false — no best-price claim, verified live
crowned:false); no books → renders NULL (panel self-hides), never a placeholder.

No regression: only the always-empty inline d.books section was replaced; grade,
projection, PropLine line, and PriceTriplet price are untouched (wiring test
asserts them). Freshness (0.4): bookprices is written in the SAME snapshot that
locks the grade (intraday refresh touches neither gradedAt.line nor bookprices) —
same fresh, no stale-label needed. Web-only → grade byte-identical trivially.
Full suite 3851 green, web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsztNChZ7vEvSR61AuMhD1
2026-07-29 04:09:34 -04:00

66 lines
2.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Book Comparison wired to the prop card (display-only). Source-assertion style,
// matching vyndrCoreScreens.test.js.
const fs = require('fs');
const path = require('path');
const read = (p) => fs.readFileSync(path.join(__dirname, '../../web/src', p), 'utf8');
describe('BookComparison — honest states', () => {
const src = read('components/BookComparison.tsx');
it('renders EACH books own line (books frequently differ in line)', () => {
// per-row line, not one shared header line.
expect(src).toMatch(/sideChar\}\$\{b\.line\}/);
expect(src).toMatch(/gridTemplateColumns: '1fr auto auto auto'/); // book · line · price · best
});
it('single-book renders an intentional state, never a fake second row', () => {
expect(src).toMatch(/const single = books\.length === 1/);
expect(src).toContain('One book posting this prop.');
});
it('crown/BEST only shows on isBest (gated off → never shows)', () => {
expect(src).toContain('b.isBest ?');
// savings copy is guarded behind savings > 0 (0 when crown off)
expect(src).toMatch(/savings != null && savings > 0/);
});
it('empty book list → renders nothing (honest-absent)', () => {
expect(src).toMatch(/if \(!books \|\| books\.length === 0\) return null/);
});
});
describe('BookComparisonPanel — the wire to /api/books', () => {
const src = read('components/vyndr/BookComparisonPanel.tsx');
it('fetches the live /api/books per-prop endpoint', () => {
expect(src).toMatch(/\/api\/books\/\$\{encodeURIComponent\(sport\)\}/);
expect(src).toContain('BookComparison');
});
it('honest-absent: renders NULL until real book rows exist', () => {
expect(src).toMatch(/data\.books\.length === 0\) return null/);
});
it('does not enable the crown or pass a fabricated best', () => {
expect(src).not.toMatch(/isBest:\s*true/);
expect(src).not.toContain('BOOK_CROWN_ENABLED');
});
});
describe('GradeResultCard — wired + existing elements survive', () => {
const src = read('components/vyndr/GradeResultCard.tsx');
it('renders the live BookComparisonPanel (BookComparison is no longer dead)', () => {
expect(src).toContain('import BookComparisonPanel');
expect(src).toContain('<BookComparisonPanel sport={d.sport} player={d.player} stat={d.stat} side={d.side} />');
});
it('the existing grade / projection / line / price elements are untouched', () => {
expect(src).toContain('PriceTriplet'); // the price layer
expect(src).toMatch(/PROJECTION|d\.projection/); // projection row
expect(src).toContain('GradeBadge'); // the grade
expect(src).toContain("l: 'LINE'"); // the PropLine line cell
});
});