view models
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueStewardingViewModel, type RaceWithProtests, type Protest, type Penalty } from './LeagueStewardingViewModel';
|
||||
|
||||
const createProtest = (id: string, status: string): Protest => ({
|
||||
id,
|
||||
protestingDriverId: `pd-${id}`,
|
||||
accusedDriverId: `ad-${id}`,
|
||||
incident: { lap: 1, description: 'Test incident' },
|
||||
filedAt: '2024-01-01T21:00:00Z',
|
||||
status,
|
||||
});
|
||||
|
||||
const createPenalty = (id: string): Penalty => ({
|
||||
id,
|
||||
driverId: `d-${id}`,
|
||||
type: 'time',
|
||||
value: 5,
|
||||
reason: 'Test penalty',
|
||||
});
|
||||
|
||||
const createRaceWithProtests = (id: string, pendingCount: number, resolvedCount: number, penaltiesCount: number): RaceWithProtests => ({
|
||||
race: {
|
||||
id,
|
||||
track: 'Spa-Francorchamps',
|
||||
scheduledAt: new Date('2024-01-01T20:00:00Z'),
|
||||
},
|
||||
pendingProtests: Array.from({ length: pendingCount }, (_, index) => createProtest(`${id}-pending-${index + 1}`, 'pending')),
|
||||
resolvedProtests: Array.from({ length: resolvedCount }, (_, index) => createProtest(`${id}-resolved-${index + 1}`, 'upheld')),
|
||||
penalties: Array.from({ length: penaltiesCount }, (_, index) => createPenalty(`${id}-pen-${index + 1}`)),
|
||||
});
|
||||
|
||||
describe('LeagueStewardingViewModel', () => {
|
||||
it('aggregates totals across all races', () => {
|
||||
const races: RaceWithProtests[] = [
|
||||
createRaceWithProtests('race-1', 2, 1, 1),
|
||||
createRaceWithProtests('race-2', 1, 3, 2),
|
||||
createRaceWithProtests('race-3', 0, 0, 0),
|
||||
];
|
||||
|
||||
const driverMap = {
|
||||
d1: { id: 'd1', name: 'Driver 1' },
|
||||
d2: { id: 'd2', name: 'Driver 2' },
|
||||
};
|
||||
|
||||
const viewModel = new LeagueStewardingViewModel(races, driverMap);
|
||||
|
||||
expect(viewModel.totalPending).toBe(3);
|
||||
expect(viewModel.totalResolved).toBe(4);
|
||||
expect(viewModel.totalPenalties).toBe(3);
|
||||
});
|
||||
|
||||
it('filters races into pending and history buckets', () => {
|
||||
const races: RaceWithProtests[] = [
|
||||
createRaceWithProtests('race-1', 2, 0, 0), // pending only
|
||||
createRaceWithProtests('race-2', 0, 3, 0), // history only (resolved)
|
||||
createRaceWithProtests('race-3', 0, 0, 2), // history only (penalties)
|
||||
createRaceWithProtests('race-4', 0, 0, 0), // no activity
|
||||
];
|
||||
|
||||
const viewModel = new LeagueStewardingViewModel(races, {});
|
||||
|
||||
const pendingIds = viewModel.pendingRaces.map(r => r.race.id).sort();
|
||||
const historyIds = viewModel.historyRaces.map(r => r.race.id).sort();
|
||||
|
||||
expect(pendingIds).toEqual(['race-1']);
|
||||
expect(historyIds).toEqual(['race-2', 'race-3']);
|
||||
});
|
||||
|
||||
it('exposes all drivers as a flat array', () => {
|
||||
const races: RaceWithProtests[] = [createRaceWithProtests('race-1', 0, 0, 0)];
|
||||
|
||||
const driverMap = {
|
||||
d1: { id: 'd1', name: 'Driver 1', avatarUrl: 'avatar-1.png' },
|
||||
d2: { id: 'd2', name: 'Driver 2', iracingId: 'ir-2' },
|
||||
};
|
||||
|
||||
const viewModel = new LeagueStewardingViewModel(races, driverMap);
|
||||
|
||||
expect(viewModel.allDrivers).toHaveLength(2);
|
||||
expect(viewModel.allDrivers).toEqual([
|
||||
driverMap.d1,
|
||||
driverMap.d2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles an empty dataset gracefully', () => {
|
||||
const viewModel = new LeagueStewardingViewModel([], {});
|
||||
|
||||
expect(viewModel.totalPending).toBe(0);
|
||||
expect(viewModel.totalResolved).toBe(0);
|
||||
expect(viewModel.totalPenalties).toBe(0);
|
||||
expect(viewModel.pendingRaces).toEqual([]);
|
||||
expect(viewModel.historyRaces).toEqual([]);
|
||||
expect(viewModel.allDrivers).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user