import { describe, expect, it } from 'vitest'; import type { LeagueStandingDTO } from '../types/generated/LeagueStandingDTO'; import { StandingEntryViewModel } from './StandingEntryViewModel'; describe('StandingEntryViewModel', () => { const createMockStanding = (overrides?: Partial): LeagueStandingDTO => ({ driverId: 'driver-1', driver: { id: 'driver-1', iracingId: '12345', name: 'Test Driver', country: 'US', joinedAt: '2025-01-01T00:00:00Z', }, position: 1, points: 100, wins: 3, podiums: 5, races: 8, positionChange: 0, lastRacePoints: 0, droppedRaceIds: [], ...overrides, }); it('should create instance with all properties', () => { const dto = createMockStanding(); const viewModel = new StandingEntryViewModel({ ...dto, leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', }); expect(viewModel.driverId).toBe('driver-1'); expect(viewModel.position).toBe(1); expect(viewModel.points).toBe(100); expect(viewModel.wins).toBe(3); expect(viewModel.podiums).toBe(5); expect(viewModel.races).toBe(8); }); it('should return position as badge string', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 5 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', }); expect(viewModel.positionBadge).toBe('P5'); }); it('should calculate points gap to leader correctly', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 2, points: 85 }), leaderPoints: 100, // leader points nextPoints: 70, // next points currentUserId: 'driver-2', }); expect(viewModel.pointsGapToLeader).toBe(-15); }); it('should show zero gap when driver is leader', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 1, points: 100 }), leaderPoints: 100, // leader points nextPoints: 85, // next points currentUserId: 'driver-1', }); expect(viewModel.pointsGapToLeader).toBe(0); }); it('should calculate points gap to next position correctly', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 2, points: 85 }), leaderPoints: 100, // leader points nextPoints: 70, // next points currentUserId: 'driver-2', }); expect(viewModel.pointsGapToNext).toBe(15); }); it('should identify current user correctly', () => { const viewModel1 = new StandingEntryViewModel({ ...createMockStanding({ driverId: 'driver-1' }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', }); const viewModel2 = new StandingEntryViewModel({ ...createMockStanding({ driverId: 'driver-1' }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-2', }); expect(viewModel1.isCurrentUser).toBe(true); expect(viewModel2.isCurrentUser).toBe(false); }); it('should return "same" trend when no previous position', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 1 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', }); expect(viewModel.trend).toBe('same'); }); it('should return "up" trend when position improved', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 1 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', previousPosition: 3, // previous position was 3rd }); expect(viewModel.trend).toBe('up'); }); it('should return "down" trend when position worsened', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 5 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', previousPosition: 2, // previous position was 2nd }); expect(viewModel.trend).toBe('down'); }); it('should return "same" trend when position unchanged', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 3 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', previousPosition: 3, // same position }); expect(viewModel.trend).toBe('same'); }); it('should return correct trend arrow for up', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 1 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', previousPosition: 3, }); expect(viewModel.trendArrow).toBe('↑'); }); it('should return correct trend arrow for down', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 5 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', previousPosition: 2, }); expect(viewModel.trendArrow).toBe('↓'); }); it('should return correct trend arrow for same', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 3 }), leaderPoints: 100, nextPoints: 85, currentUserId: 'driver-1', }); expect(viewModel.trendArrow).toBe('-'); }); it('should handle edge case of last place with no one behind', () => { const viewModel = new StandingEntryViewModel({ ...createMockStanding({ position: 10, points: 20 }), leaderPoints: 100, // leader points nextPoints: 20, // same points (last place) currentUserId: 'driver-10', }); expect(viewModel.pointsGapToNext).toBe(0); expect(viewModel.pointsGapToLeader).toBe(-80); }); });