442 lines
13 KiB
TypeScript
442 lines
13 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { DriverRankingsViewDataBuilder } from './DriverRankingsViewDataBuilder';
|
|
import type { DriverLeaderboardItemDTO } from '@/lib/types/generated/DriverLeaderboardItemDTO';
|
|
|
|
describe('DriverRankingsViewDataBuilder', () => {
|
|
describe('happy paths', () => {
|
|
it('should transform DriverLeaderboardItemDTO array to DriverRankingsViewData correctly', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
avatarUrl: 'https://example.com/avatar1.jpg',
|
|
},
|
|
{
|
|
id: 'driver-2',
|
|
name: 'Jane Smith',
|
|
rating: 1100.0,
|
|
skillLevel: 'advanced',
|
|
nationality: 'Canada',
|
|
racesCompleted: 100,
|
|
wins: 15,
|
|
podiums: 40,
|
|
isActive: true,
|
|
rank: 2,
|
|
avatarUrl: 'https://example.com/avatar2.jpg',
|
|
},
|
|
{
|
|
id: 'driver-3',
|
|
name: 'Bob Johnson',
|
|
rating: 950.0,
|
|
skillLevel: 'intermediate',
|
|
nationality: 'UK',
|
|
racesCompleted: 80,
|
|
wins: 10,
|
|
podiums: 30,
|
|
isActive: true,
|
|
rank: 3,
|
|
avatarUrl: 'https://example.com/avatar3.jpg',
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
// Verify drivers
|
|
expect(result.drivers).toHaveLength(3);
|
|
expect(result.drivers[0].id).toBe('driver-1');
|
|
expect(result.drivers[0].name).toBe('John Doe');
|
|
expect(result.drivers[0].rating).toBe(1234.56);
|
|
expect(result.drivers[0].skillLevel).toBe('pro');
|
|
expect(result.drivers[0].nationality).toBe('USA');
|
|
expect(result.drivers[0].racesCompleted).toBe(150);
|
|
expect(result.drivers[0].wins).toBe(25);
|
|
expect(result.drivers[0].podiums).toBe(60);
|
|
expect(result.drivers[0].rank).toBe(1);
|
|
expect(result.drivers[0].avatarUrl).toBe('https://example.com/avatar1.jpg');
|
|
expect(result.drivers[0].winRate).toBe('16.7');
|
|
expect(result.drivers[0].medalBg).toBe('bg-warning-amber');
|
|
expect(result.drivers[0].medalColor).toBe('text-warning-amber');
|
|
|
|
// Verify podium (top 3 with special ordering: 2nd, 1st, 3rd)
|
|
expect(result.podium).toHaveLength(3);
|
|
expect(result.podium[0].id).toBe('driver-1');
|
|
expect(result.podium[0].name).toBe('John Doe');
|
|
expect(result.podium[0].rating).toBe(1234.56);
|
|
expect(result.podium[0].wins).toBe(25);
|
|
expect(result.podium[0].podiums).toBe(60);
|
|
expect(result.podium[0].avatarUrl).toBe('https://example.com/avatar1.jpg');
|
|
expect(result.podium[0].position).toBe(2); // 2nd place
|
|
|
|
expect(result.podium[1].id).toBe('driver-2');
|
|
expect(result.podium[1].position).toBe(1); // 1st place
|
|
|
|
expect(result.podium[2].id).toBe('driver-3');
|
|
expect(result.podium[2].position).toBe(3); // 3rd place
|
|
|
|
// Verify default values
|
|
expect(result.searchQuery).toBe('');
|
|
expect(result.selectedSkill).toBe('all');
|
|
expect(result.sortBy).toBe('rank');
|
|
expect(result.showFilters).toBe(false);
|
|
});
|
|
|
|
it('should handle empty driver array', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers).toEqual([]);
|
|
expect(result.podium).toEqual([]);
|
|
expect(result.searchQuery).toBe('');
|
|
expect(result.selectedSkill).toBe('all');
|
|
expect(result.sortBy).toBe('rank');
|
|
expect(result.showFilters).toBe(false);
|
|
});
|
|
|
|
it('should handle less than 3 drivers for podium', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
avatarUrl: 'https://example.com/avatar1.jpg',
|
|
},
|
|
{
|
|
id: 'driver-2',
|
|
name: 'Jane Smith',
|
|
rating: 1100.0,
|
|
skillLevel: 'advanced',
|
|
nationality: 'Canada',
|
|
racesCompleted: 100,
|
|
wins: 15,
|
|
podiums: 40,
|
|
isActive: true,
|
|
rank: 2,
|
|
avatarUrl: 'https://example.com/avatar2.jpg',
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers).toHaveLength(2);
|
|
expect(result.podium).toHaveLength(2);
|
|
expect(result.podium[0].position).toBe(2); // 2nd place
|
|
expect(result.podium[1].position).toBe(1); // 1st place
|
|
});
|
|
|
|
it('should handle missing avatar URLs with empty string fallback', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].avatarUrl).toBe('');
|
|
expect(result.podium[0].avatarUrl).toBe('');
|
|
});
|
|
|
|
it('should calculate win rate correctly', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 100,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
{
|
|
id: 'driver-2',
|
|
name: 'Jane Smith',
|
|
rating: 1100.0,
|
|
skillLevel: 'advanced',
|
|
nationality: 'Canada',
|
|
racesCompleted: 50,
|
|
wins: 10,
|
|
podiums: 25,
|
|
isActive: true,
|
|
rank: 2,
|
|
},
|
|
{
|
|
id: 'driver-3',
|
|
name: 'Bob Johnson',
|
|
rating: 950.0,
|
|
skillLevel: 'intermediate',
|
|
nationality: 'UK',
|
|
racesCompleted: 0,
|
|
wins: 0,
|
|
podiums: 0,
|
|
isActive: true,
|
|
rank: 3,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].winRate).toBe('25.0');
|
|
expect(result.drivers[1].winRate).toBe('20.0');
|
|
expect(result.drivers[2].winRate).toBe('0.0');
|
|
});
|
|
|
|
it('should assign correct medal colors based on position', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
{
|
|
id: 'driver-2',
|
|
name: 'Jane Smith',
|
|
rating: 1100.0,
|
|
skillLevel: 'advanced',
|
|
nationality: 'Canada',
|
|
racesCompleted: 100,
|
|
wins: 15,
|
|
podiums: 40,
|
|
isActive: true,
|
|
rank: 2,
|
|
},
|
|
{
|
|
id: 'driver-3',
|
|
name: 'Bob Johnson',
|
|
rating: 950.0,
|
|
skillLevel: 'intermediate',
|
|
nationality: 'UK',
|
|
racesCompleted: 80,
|
|
wins: 10,
|
|
podiums: 30,
|
|
isActive: true,
|
|
rank: 3,
|
|
},
|
|
{
|
|
id: 'driver-4',
|
|
name: 'Alice Brown',
|
|
rating: 800.0,
|
|
skillLevel: 'beginner',
|
|
nationality: 'Germany',
|
|
racesCompleted: 60,
|
|
wins: 5,
|
|
podiums: 15,
|
|
isActive: true,
|
|
rank: 4,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].medalBg).toBe('bg-warning-amber');
|
|
expect(result.drivers[0].medalColor).toBe('text-warning-amber');
|
|
expect(result.drivers[1].medalBg).toBe('bg-gray-300');
|
|
expect(result.drivers[1].medalColor).toBe('text-gray-300');
|
|
expect(result.drivers[2].medalBg).toBe('bg-orange-700');
|
|
expect(result.drivers[2].medalColor).toBe('text-orange-700');
|
|
expect(result.drivers[3].medalBg).toBe('bg-gray-800');
|
|
expect(result.drivers[3].medalColor).toBe('text-gray-400');
|
|
});
|
|
});
|
|
|
|
describe('data transformation', () => {
|
|
it('should preserve all DTO fields in the output', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-123',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].name).toBe(driverDTOs[0].name);
|
|
expect(result.drivers[0].nationality).toBe(driverDTOs[0].nationality);
|
|
expect(result.drivers[0].avatarUrl).toBe(driverDTOs[0].avatarUrl);
|
|
expect(result.drivers[0].skillLevel).toBe(driverDTOs[0].skillLevel);
|
|
});
|
|
|
|
it('should not modify the input DTO', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-123',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
},
|
|
];
|
|
|
|
const originalDTO = JSON.parse(JSON.stringify(driverDTOs));
|
|
DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(driverDTOs).toEqual(originalDTO);
|
|
});
|
|
|
|
it('should handle large numbers correctly', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 999999.99,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 10000,
|
|
wins: 2500,
|
|
podiums: 5000,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].rating).toBe(999999.99);
|
|
expect(result.drivers[0].wins).toBe(2500);
|
|
expect(result.drivers[0].podiums).toBe(5000);
|
|
expect(result.drivers[0].racesCompleted).toBe(10000);
|
|
expect(result.drivers[0].winRate).toBe('25.0');
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle null/undefined avatar URLs', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
avatarUrl: null as any,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].avatarUrl).toBe('');
|
|
expect(result.podium[0].avatarUrl).toBe('');
|
|
});
|
|
|
|
it('should handle null/undefined rating', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: null as any,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].rating).toBeNull();
|
|
expect(result.podium[0].rating).toBeNull();
|
|
});
|
|
|
|
it('should handle zero races completed for win rate calculation', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 0,
|
|
wins: 0,
|
|
podiums: 0,
|
|
isActive: true,
|
|
rank: 1,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].winRate).toBe('0.0');
|
|
});
|
|
|
|
it('should handle rank 0', () => {
|
|
const driverDTOs: DriverLeaderboardItemDTO[] = [
|
|
{
|
|
id: 'driver-1',
|
|
name: 'John Doe',
|
|
rating: 1234.56,
|
|
skillLevel: 'pro',
|
|
nationality: 'USA',
|
|
racesCompleted: 150,
|
|
wins: 25,
|
|
podiums: 60,
|
|
isActive: true,
|
|
rank: 0,
|
|
},
|
|
];
|
|
|
|
const result = DriverRankingsViewDataBuilder.build(driverDTOs);
|
|
|
|
expect(result.drivers[0].rank).toBe(0);
|
|
expect(result.drivers[0].medalBg).toBe('bg-gray-800');
|
|
expect(result.drivers[0].medalColor).toBe('text-gray-400');
|
|
});
|
|
});
|
|
});
|