view data tests
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user