776 lines
19 KiB
TypeScript
776 lines
19 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { RaceResultsViewDataBuilder } from './RaceResultsViewDataBuilder';
|
|
|
|
describe('RaceResultsViewDataBuilder', () => {
|
|
describe('happy paths', () => {
|
|
it('should transform API DTO to RaceResultsViewData correctly', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-2',
|
|
driverName: 'Driver 2',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
pointsSystem: {
|
|
1: 25,
|
|
2: 18,
|
|
3: 15,
|
|
},
|
|
fastestLapTime: 120000,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result).toEqual({
|
|
raceTrack: 'Test Track',
|
|
raceScheduledAt: '2024-01-01T10:00:00Z',
|
|
totalDrivers: 20,
|
|
leagueName: 'Test League',
|
|
raceSOF: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
driverAvatar: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-2',
|
|
driverName: 'Driver 2',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
pointsSystem: {
|
|
1: 25,
|
|
2: 18,
|
|
3: 15,
|
|
},
|
|
fastestLapTime: 120000,
|
|
});
|
|
});
|
|
|
|
it('should handle empty results and penalties', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 0,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: null,
|
|
results: [],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.raceSOF).toBeNull();
|
|
});
|
|
|
|
it('should handle multiple results and penalties', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
{
|
|
position: 2,
|
|
driverId: 'driver-2',
|
|
driverName: 'Driver 2',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'UK',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:24.000',
|
|
fastestLap: '1:21.000',
|
|
points: 18,
|
|
incidents: 1,
|
|
isCurrentUser: true,
|
|
},
|
|
],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-3',
|
|
driverName: 'Driver 3',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
{
|
|
driverId: 'driver-4',
|
|
driverName: 'Driver 4',
|
|
type: 'grid_penalty',
|
|
value: 3,
|
|
reason: 'Qualifying infringement',
|
|
notes: null,
|
|
},
|
|
],
|
|
pointsSystem: {
|
|
1: 25,
|
|
2: 18,
|
|
3: 15,
|
|
},
|
|
fastestLapTime: 120000,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results).toHaveLength(2);
|
|
expect(result.penalties).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe('data transformation', () => {
|
|
it('should preserve all DTO fields in the output', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {
|
|
1: 25,
|
|
2: 18,
|
|
3: 15,
|
|
},
|
|
fastestLapTime: 120000,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.raceTrack).toBe(apiDto.race.track);
|
|
expect(result.raceScheduledAt).toBe(apiDto.race.scheduledAt);
|
|
expect(result.totalDrivers).toBe(apiDto.stats.totalDrivers);
|
|
expect(result.leagueName).toBe(apiDto.league.name);
|
|
expect(result.raceSOF).toBe(apiDto.strengthOfField);
|
|
expect(result.pointsSystem).toEqual(apiDto.pointsSystem);
|
|
expect(result.fastestLapTime).toBe(apiDto.fastestLapTime);
|
|
});
|
|
|
|
it('should not modify the input DTO', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const originalDto = { ...apiDto };
|
|
RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(apiDto).toEqual(originalDto);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle null API DTO', () => {
|
|
const result = RaceResultsViewDataBuilder.build(null);
|
|
|
|
expect(result.raceSOF).toBeNull();
|
|
expect(result.results).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.pointsSystem).toEqual({});
|
|
expect(result.fastestLapTime).toBe(0);
|
|
});
|
|
|
|
it('should handle undefined API DTO', () => {
|
|
const result = RaceResultsViewDataBuilder.build(undefined);
|
|
|
|
expect(result.raceSOF).toBeNull();
|
|
expect(result.results).toHaveLength(0);
|
|
expect(result.penalties).toHaveLength(0);
|
|
expect(result.pointsSystem).toEqual({});
|
|
expect(result.fastestLapTime).toBe(0);
|
|
});
|
|
|
|
it('should handle results without country', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: null,
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].country).toBe('US');
|
|
});
|
|
|
|
it('should handle results without car', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: null,
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].car).toBe('Unknown');
|
|
});
|
|
|
|
it('should handle results without laps', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: null,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].laps).toBe(0);
|
|
});
|
|
|
|
it('should handle results without time', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: null,
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].time).toBe('0:00.00');
|
|
});
|
|
|
|
it('should handle results without fastest lap', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: null,
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].fastestLap).toBe('0.00');
|
|
});
|
|
|
|
it('should handle results without points', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: null,
|
|
incidents: 0,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].points).toBe(0);
|
|
});
|
|
|
|
it('should handle results without incidents', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: null,
|
|
isCurrentUser: false,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].incidents).toBe(0);
|
|
});
|
|
|
|
it('should handle results without isCurrentUser', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [
|
|
{
|
|
position: 1,
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
avatarUrl: 'avatar-url',
|
|
country: 'US',
|
|
car: 'Test Car',
|
|
laps: 30,
|
|
time: '1:23.456',
|
|
fastestLap: '1:20.000',
|
|
points: 25,
|
|
incidents: 0,
|
|
isCurrentUser: null,
|
|
},
|
|
],
|
|
penalties: [],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.results[0].isCurrentUser).toBe(false);
|
|
});
|
|
|
|
it('should handle penalties without driver name', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-1',
|
|
driverName: null,
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].driverName).toBe('Unknown');
|
|
});
|
|
|
|
it('should handle penalties without value', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
type: 'time_penalty',
|
|
value: null,
|
|
reason: 'Track limits',
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].value).toBe(0);
|
|
});
|
|
|
|
it('should handle penalties without reason', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
type: 'time_penalty',
|
|
value: 5,
|
|
reason: null,
|
|
notes: 'Warning issued',
|
|
},
|
|
],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].reason).toBe('Penalty applied');
|
|
});
|
|
|
|
it('should handle different penalty types', () => {
|
|
const apiDto = {
|
|
race: {
|
|
track: 'Test Track',
|
|
scheduledAt: '2024-01-01T10:00:00Z',
|
|
},
|
|
stats: {
|
|
totalDrivers: 20,
|
|
},
|
|
league: {
|
|
name: 'Test League',
|
|
},
|
|
strengthOfField: 1500,
|
|
results: [],
|
|
penalties: [
|
|
{
|
|
driverId: 'driver-1',
|
|
driverName: 'Driver 1',
|
|
type: 'grid_penalty',
|
|
value: 3,
|
|
reason: 'Qualifying infringement',
|
|
notes: null,
|
|
},
|
|
{
|
|
driverId: 'driver-2',
|
|
driverName: 'Driver 2',
|
|
type: 'points_deduction',
|
|
value: 10,
|
|
reason: 'Dangerous driving',
|
|
notes: null,
|
|
},
|
|
{
|
|
driverId: 'driver-3',
|
|
driverName: 'Driver 3',
|
|
type: 'disqualification',
|
|
value: 0,
|
|
reason: 'Technical infringement',
|
|
notes: null,
|
|
},
|
|
{
|
|
driverId: 'driver-4',
|
|
driverName: 'Driver 4',
|
|
type: 'warning',
|
|
value: 0,
|
|
reason: 'Minor infraction',
|
|
notes: null,
|
|
},
|
|
{
|
|
driverId: 'driver-5',
|
|
driverName: 'Driver 5',
|
|
type: 'license_points',
|
|
value: 2,
|
|
reason: 'Multiple incidents',
|
|
notes: null,
|
|
},
|
|
],
|
|
pointsSystem: {},
|
|
fastestLapTime: 0,
|
|
};
|
|
|
|
const result = RaceResultsViewDataBuilder.build(apiDto);
|
|
|
|
expect(result.penalties[0].type).toBe('grid_penalty');
|
|
expect(result.penalties[1].type).toBe('points_deduction');
|
|
expect(result.penalties[2].type).toBe('disqualification');
|
|
expect(result.penalties[3].type).toBe('warning');
|
|
expect(result.penalties[4].type).toBe('license_points');
|
|
});
|
|
});
|
|
});
|