141 lines
3.9 KiB
TypeScript
141 lines
3.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { RaceStewardingViewModel } from './RaceStewardingViewModel';
|
|
|
|
const createRaceStewardingDto = () => ({
|
|
raceDetail: {
|
|
race: {
|
|
id: 'race-1',
|
|
track: 'Spa-Francorchamps',
|
|
scheduledAt: '2024-01-01T20:00:00Z',
|
|
status: 'completed',
|
|
},
|
|
league: {
|
|
id: 'league-1',
|
|
name: 'Test League',
|
|
},
|
|
},
|
|
protests: {
|
|
protests: [
|
|
{
|
|
id: 'p1',
|
|
protestingDriverId: 'd1',
|
|
accusedDriverId: 'd2',
|
|
incident: { lap: 1, description: 'Turn 1 divebomb' },
|
|
filedAt: '2024-01-01T21:00:00Z',
|
|
status: 'pending',
|
|
},
|
|
{
|
|
id: 'p2',
|
|
protestingDriverId: 'd3',
|
|
accusedDriverId: 'd4',
|
|
incident: { lap: 5, description: 'Blocking' },
|
|
filedAt: '2024-01-01T21:05:00Z',
|
|
status: 'under_review',
|
|
},
|
|
{
|
|
id: 'p3',
|
|
protestingDriverId: 'd5',
|
|
accusedDriverId: 'd6',
|
|
incident: { lap: 10, description: 'Contact' },
|
|
filedAt: '2024-01-01T21:10:00Z',
|
|
status: 'upheld',
|
|
},
|
|
{
|
|
id: 'p4',
|
|
protestingDriverId: 'd7',
|
|
accusedDriverId: 'd8',
|
|
incident: { lap: 12, description: 'Off-track overtake' },
|
|
filedAt: '2024-01-01T21:15:00Z',
|
|
status: 'dismissed',
|
|
},
|
|
{
|
|
id: 'p5',
|
|
protestingDriverId: 'd9',
|
|
accusedDriverId: 'd10',
|
|
incident: { lap: 15, description: 'Withdrawn protest' },
|
|
filedAt: '2024-01-01T21:20:00Z',
|
|
status: 'withdrawn',
|
|
},
|
|
],
|
|
driverMap: {
|
|
d1: { id: 'd1', name: 'Driver 1' },
|
|
d2: { id: 'd2', name: 'Driver 2' },
|
|
},
|
|
},
|
|
penalties: {
|
|
penalties: [
|
|
{
|
|
id: 'pen1',
|
|
driverId: 'd2',
|
|
type: 'time',
|
|
value: 5,
|
|
reason: 'Avoidable contact',
|
|
},
|
|
{
|
|
id: 'pen2',
|
|
driverId: 'd3',
|
|
type: 'points',
|
|
value: 2,
|
|
reason: 'Reckless driving',
|
|
},
|
|
],
|
|
driverMap: {
|
|
d3: { id: 'd3', name: 'Driver 3' },
|
|
d4: { id: 'd4', name: 'Driver 4' },
|
|
},
|
|
},
|
|
});
|
|
|
|
describe('RaceStewardingViewModel', () => {
|
|
it('maps core race, league, protests, penalties and driver map', () => {
|
|
const dto = createRaceStewardingDto();
|
|
|
|
const viewModel = new RaceStewardingViewModel(dto as any);
|
|
|
|
expect(viewModel.race).toEqual(dto.raceDetail.race);
|
|
expect(viewModel.league).toEqual(dto.raceDetail.league);
|
|
expect(viewModel.protests).toEqual(dto.protests.protests);
|
|
expect(viewModel.penalties).toEqual(dto.penalties.penalties);
|
|
expect(viewModel.driverMap).toEqual({
|
|
...dto.protests.driverMap,
|
|
...dto.penalties.driverMap,
|
|
});
|
|
});
|
|
|
|
it('derives pending and resolved protest buckets and counts', () => {
|
|
const dto = createRaceStewardingDto();
|
|
|
|
const viewModel = new RaceStewardingViewModel(dto as any);
|
|
|
|
const pendingIds = viewModel.pendingProtests.map(p => p.id);
|
|
const resolvedIds = viewModel.resolvedProtests.map(p => p.id);
|
|
|
|
expect(pendingIds.sort()).toEqual(['p1', 'p2']);
|
|
expect(resolvedIds.sort()).toEqual(['p3', 'p4', 'p5']);
|
|
expect(viewModel.pendingCount).toBe(2);
|
|
expect(viewModel.resolvedCount).toBe(3);
|
|
});
|
|
|
|
it('derives penalties count from penalties list', () => {
|
|
const dto = createRaceStewardingDto();
|
|
|
|
const viewModel = new RaceStewardingViewModel(dto as any);
|
|
|
|
expect(viewModel.penaltiesCount).toBe(2);
|
|
});
|
|
|
|
it('handles empty protests and penalties gracefully', () => {
|
|
const dto = createRaceStewardingDto();
|
|
dto.protests.protests = [];
|
|
dto.penalties.penalties = [];
|
|
|
|
const viewModel = new RaceStewardingViewModel(dto as any);
|
|
|
|
expect(viewModel.pendingProtests).toEqual([]);
|
|
expect(viewModel.resolvedProtests).toEqual([]);
|
|
expect(viewModel.pendingCount).toBe(0);
|
|
expect(viewModel.resolvedCount).toBe(0);
|
|
expect(viewModel.penaltiesCount).toBe(0);
|
|
});
|
|
});
|