/** * SimplifiedSelector — unit tests * Tests the data contracts and logic that the SimplifiedSelector component relies on. * Since the frontend uses Next.js/React without jest-dom configured in the Node test suite, * we test the underlying data shapes, stat lists, and line pre-fill logic. */ describe('SimplifiedSelector', () => { const NBA_STATS = ['points', 'rebounds', 'assists', 'threes', 'blocks', 'steals', 'pra', 'turnovers']; const MLB_STATS = [ 'strikeouts', 'hits_allowed', 'earned_runs', 'innings_pitched', 'walks_allowed', 'hits', 'total_bases', 'rbi', 'runs', 'stolen_bases', 'home_runs', 'walks', 'singles', 'doubles', ]; // Test 1: Sport toggle renders correctly (data contract) test('sport toggle has exactly NBA and MLB options', () => { const sports = ['NBA', 'MLB']; expect(sports).toHaveLength(2); expect(sports).toContain('NBA'); expect(sports).toContain('MLB'); }); test('NBA stat list contains expected prop types', () => { expect(NBA_STATS).toContain('points'); expect(NBA_STATS).toContain('rebounds'); expect(NBA_STATS).toContain('assists'); expect(NBA_STATS).toContain('threes'); expect(NBA_STATS).toContain('pra'); expect(NBA_STATS).toHaveLength(8); }); test('MLB stat list contains expected prop types', () => { expect(MLB_STATS).toContain('strikeouts'); expect(MLB_STATS).toContain('hits'); expect(MLB_STATS).toContain('total_bases'); expect(MLB_STATS).toContain('home_runs'); expect(MLB_STATS).toHaveLength(14); }); // Test 2: Player search accepts text input (logic contract) test('player search requires minimum 2 characters before querying', () => { const shouldSearch = (query) => query.length >= 2; expect(shouldSearch('')).toBe(false); expect(shouldSearch('L')).toBe(false); expect(shouldSearch('Le')).toBe(true); expect(shouldSearch('LeBron')).toBe(true); }); // Test 3: Stat dropdown populates from player selection test('stat dropdown filters to available odds when player has odds data', () => { const playerOdds = [ { player: 'LeBron James', stat_type: 'points', line: 25.5, direction: 'over', book: 'draftkings' }, { player: 'LeBron James', stat_type: 'rebounds', line: 7.5, direction: 'over', book: 'draftkings' }, { player: 'LeBron James', stat_type: 'assists', line: 7.5, direction: 'over', book: 'fanduel' }, ]; const availableStats = NBA_STATS.filter((s) => playerOdds.some((o) => o.stat_type === s)); expect(availableStats).toEqual(['points', 'rebounds', 'assists']); expect(availableStats).not.toContain('threes'); }); test('stat dropdown shows all stats when no odds data available', () => { const playerOdds = []; const availableStats = playerOdds.length > 0 ? NBA_STATS.filter((s) => playerOdds.some((o) => o.stat_type === s)) : NBA_STATS; expect(availableStats).toEqual(NBA_STATS); }); // Test 4: Line pre-fills when player and stat selected test('line pre-fills from matching odds data', () => { const playerOdds = [ { player: 'LeBron James', stat_type: 'points', line: 25.5, direction: 'over', book: 'draftkings' }, { player: 'LeBron James', stat_type: 'rebounds', line: 7.5, direction: 'under', book: 'fanduel' }, ]; const selectedStat = 'points'; const match = playerOdds.find((o) => o.stat_type === selectedStat); expect(match).toBeDefined(); expect(match.line).toBe(25.5); expect(match.direction).toBe('over'); }); test('line stays empty when no matching odds for selected stat', () => { const playerOdds = [ { player: 'LeBron James', stat_type: 'points', line: 25.5, direction: 'over', book: 'draftkings' }, ]; const selectedStat = 'blocks'; const match = playerOdds.find((o) => o.stat_type === selectedStat); expect(match).toBeUndefined(); }); // Test 5: Grade card renders after scan completes (data shape contract) test('scan result contains grade data needed for GradeCard rendering', () => { const mockResult = { scan_id: 'test-123', parlay_grade: 'B', parlay_confidence: 72, correlation_flags: [], legs: [{ index: 0, player: 'LeBron James', stat_type: 'points', line: 25.5, direction: 'over', grade: 'A', confidence: 85, edge_pct: 4.2, kill_conditions: [], reasoning_summary: 'Strong recent form supports the over.', }], scan_count: 1, scans_remaining: 4, upgrade_pitch: null, }; expect(mockResult.parlay_grade).toBeDefined(); expect(mockResult.parlay_confidence).toBeDefined(); expect(mockResult.legs[0].grade).toBeDefined(); expect(mockResult.legs[0].confidence).toBeDefined(); expect(['A', 'B', 'C', 'D']).toContain(mockResult.legs[0].grade); }); // Sport switching resets state test('switching sport should reset stat list', () => { let currentSport = 'NBA'; let stats = currentSport === 'NBA' ? NBA_STATS : MLB_STATS; expect(stats).toContain('points'); currentSport = 'MLB'; stats = currentSport === 'NBA' ? NBA_STATS : MLB_STATS; expect(stats).toContain('strikeouts'); expect(stats).not.toContain('points'); }); // Scan payload shape test('scan payload includes all required fields', () => { const payload = { player: 'LeBron James', stat_type: 'points', line: 25.5, direction: 'over', sport: 'NBA', }; expect(payload).toHaveProperty('player'); expect(payload).toHaveProperty('stat_type'); expect(payload).toHaveProperty('line'); expect(payload).toHaveProperty('direction'); expect(payload).toHaveProperty('sport'); expect(typeof payload.line).toBe('number'); }); });