import { describe, it, expect } from 'vitest'; import { LeagueDetailViewModel, LeagueViewModel, DriverViewModel, RaceViewModel } from './LeagueDetailViewModel'; describe('LeagueDetailViewModel', () => { const baseLeague = { id: 'league-1', name: 'Pro Series', game: 'iRacing', tier: 'premium' as const, season: '2025 Season 1', description: 'High level competition', drivers: 24, races: 10, completedRaces: 4, totalImpressions: 100_000, avgViewsPerRace: 10_000, engagement: 3.5, rating: 4.7, seasonStatus: 'active' as const, seasonDates: { start: '2025-01-01', end: '2025-04-01' }, nextRace: { name: 'Round 5', date: '2025-02-01' }, sponsorSlots: { main: { available: true, price: 5_000, benefits: ['Branding', 'Mentions'] }, secondary: { available: 2, total: 3, price: 2_000, benefits: ['Branding'] }, }, }; const drivers = [ { id: 'd1', name: 'Alice', country: 'US', position: 1, races: 4, impressions: 30_000, team: 'Alpha' }, { id: 'd2', name: 'Bob', country: 'DE', position: 2, races: 4, impressions: 25_000, team: 'Beta' }, ]; const races = [ { id: 'r1', name: 'Round 1', date: '2025-01-01', views: 12_000, status: 'completed' as const }, { id: 'r2', name: 'Round 2', date: '2025-01-08', views: 11_000, status: 'upcoming' as const }, ]; it('maps nested league, drivers and races into view models', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); expect(vm.league).toBeInstanceOf(LeagueViewModel); expect(vm.drivers).toHaveLength(2); expect(vm.drivers[0]).toBeInstanceOf(DriverViewModel); expect(vm.races).toHaveLength(2); expect(vm.races[0]).toBeInstanceOf(RaceViewModel); }); it('keeps core league metrics available on LeagueViewModel', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); expect(vm.league.id).toBe('league-1'); expect(vm.league.drivers).toBe(24); expect(vm.league.races).toBe(10); expect(vm.league.completedRaces).toBe(4); expect(vm.league.totalImpressions).toBe(100_000); expect(vm.league.avgViewsPerRace).toBe(10_000); }); it('exposes formatted impression and views statistics', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); expect(vm.league.formattedTotalImpressions).toBe(baseLeague.totalImpressions.toLocaleString()); expect(vm.league.formattedAvgViewsPerRace).toBe(baseLeague.avgViewsPerRace.toLocaleString()); expect(vm.league.projectedTotalViews).toBe(baseLeague.avgViewsPerRace * baseLeague.races); expect(vm.league.formattedProjectedTotal).toBe(vm.league.projectedTotalViews.toLocaleString()); }); it('calculates CPM for main sponsor from projected total views', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); const expectedCpm = Math.round((baseLeague.sponsorSlots.main.price / vm.league.projectedTotalViews) * 1000); expect(vm.league.mainSponsorCpm).toBe(expectedCpm); expect(vm.league.formattedMainSponsorCpm).toBe(`$${expectedCpm.toFixed(2)}`); }); it('derives races left and tier configuration', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); expect(vm.league.racesLeft).toBe(baseLeague.races - baseLeague.completedRaces); const tierConfig = vm.league.tierConfig; expect(tierConfig.color).toBeDefined(); expect(tierConfig.bgColor).toBeDefined(); expect(tierConfig.border).toBeDefined(); }); it('formats driver impressions and race date/views', () => { const vm = new LeagueDetailViewModel({ league: baseLeague, drivers, races }); expect(vm.drivers[0].formattedImpressions).toBe(drivers[0].impressions.toLocaleString()); // formattedDate uses a short, locale-specific date string like "Jan 1" const formattedDate = vm.races[0].formattedDate; expect(typeof formattedDate).toBe('string'); expect(formattedDate.length).toBeGreaterThan(0); expect(vm.races[0].formattedViews).toBe(races[0].views.toLocaleString()); }); });