view models
This commit is contained in:
130
apps/website/lib/view-models/RaceResultsDetailViewModel.test.ts
Normal file
130
apps/website/lib/view-models/RaceResultsDetailViewModel.test.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { RaceResultsDetailViewModel } from './RaceResultsDetailViewModel';
|
||||
import { RaceResultViewModel } from './RaceResultViewModel';
|
||||
import type { RaceResultsDetailDTO } from '../types/generated/RaceResultsDetailDTO';
|
||||
import type { RaceResultDTO } from '../types/generated/RaceResultDTO';
|
||||
|
||||
const createResult = (overrides: Partial<RaceResultDTO> = {}): RaceResultDTO => ({
|
||||
driverId: 'driver-1',
|
||||
driverName: 'Driver One',
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
position: 2,
|
||||
startPosition: 3,
|
||||
incidents: 1,
|
||||
fastestLap: 90.5,
|
||||
positionChange: 1,
|
||||
isPodium: true,
|
||||
isClean: true,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const createDto = (overrides: Partial<RaceResultsDetailDTO & { results?: RaceResultDTO[] }> = {}): RaceResultsDetailDTO & { results?: RaceResultDTO[] } => ({
|
||||
raceId: 'race-1',
|
||||
track: 'Spa',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('RaceResultsDetailViewModel', () => {
|
||||
it('maps basic fields and wraps results into view models', () => {
|
||||
const dto = createDto({
|
||||
results: [createResult({ driverId: 'driver-1' }), createResult({ driverId: 'driver-2' })],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
expect(viewModel.raceId).toBe('race-1');
|
||||
expect(viewModel.track).toBe('Spa');
|
||||
expect(viewModel.results).toHaveLength(2);
|
||||
expect(viewModel.results[0]).toBeInstanceOf(RaceResultViewModel);
|
||||
});
|
||||
|
||||
it('sorts results by position', () => {
|
||||
const dto = createDto({
|
||||
results: [
|
||||
createResult({ driverId: 'driver-1', position: 3 }),
|
||||
createResult({ driverId: 'driver-2', position: 1 }),
|
||||
createResult({ driverId: 'driver-3', position: 2 }),
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
const byPosition = viewModel.resultsByPosition;
|
||||
|
||||
expect(byPosition.map(r => r.position)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('sorts results by fastest lap', () => {
|
||||
const dto = createDto({
|
||||
results: [
|
||||
createResult({ driverId: 'driver-1', fastestLap: 95 }),
|
||||
createResult({ driverId: 'driver-2', fastestLap: 90 }),
|
||||
createResult({ driverId: 'driver-3', fastestLap: 100 }),
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
const byLap = viewModel.resultsByFastestLap;
|
||||
|
||||
expect(byLap.map(r => r.fastestLap)).toEqual([90, 95, 100]);
|
||||
});
|
||||
|
||||
it('filters clean drivers', () => {
|
||||
const dto = createDto({
|
||||
results: [
|
||||
createResult({ driverId: 'driver-1', isClean: true }),
|
||||
createResult({ driverId: 'driver-2', isClean: false }),
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
const clean = viewModel.cleanDrivers;
|
||||
|
||||
expect(clean).toHaveLength(1);
|
||||
expect(clean[0].driverId).toBe('driver-1');
|
||||
});
|
||||
|
||||
it('finds current user result by driver id', () => {
|
||||
const dto = createDto({
|
||||
results: [
|
||||
createResult({ driverId: 'driver-1' }),
|
||||
createResult({ driverId: 'driver-2' }),
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-2');
|
||||
|
||||
expect(viewModel.currentUserResult?.driverId).toBe('driver-2');
|
||||
});
|
||||
|
||||
it('computes stats for total drivers, clean rate and average incidents', () => {
|
||||
const dto = createDto({
|
||||
results: [
|
||||
createResult({ driverId: 'driver-1', isClean: true, incidents: 0 }),
|
||||
createResult({ driverId: 'driver-2', isClean: false, incidents: 4 }),
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
const stats = viewModel.stats;
|
||||
|
||||
expect(stats.totalDrivers).toBe(2);
|
||||
expect(stats.cleanRate).toBeCloseTo(50);
|
||||
expect(stats.averageIncidents).toBeCloseTo(2);
|
||||
});
|
||||
|
||||
it('returns zeroed stats when there are no results', () => {
|
||||
const dto = createDto({ results: [] });
|
||||
|
||||
const viewModel = new RaceResultsDetailViewModel(dto, 'driver-1');
|
||||
|
||||
const stats = viewModel.stats;
|
||||
|
||||
expect(stats.totalDrivers).toBe(0);
|
||||
expect(stats.cleanRate).toBe(0);
|
||||
expect(stats.averageIncidents).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user