view data tests
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueDetailViewDataBuilder } from './LeagueDetailViewDataBuilder';
|
||||
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
||||
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
||||
|
||||
describe('LeagueDetailViewDataBuilder', () => {
|
||||
describe('happy paths', () => {
|
||||
it('should transform league DTOs to LeagueDetailViewData correctly', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Pro League',
|
||||
description: 'A competitive league for experienced drivers',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
qualifyingFormat: 'Solo • 32 max',
|
||||
},
|
||||
usedSlots: 25,
|
||||
category: 'competitive',
|
||||
scoring: {
|
||||
gameId: 'game-1',
|
||||
gameName: 'iRacing',
|
||||
primaryChampionshipType: 'Single Championship',
|
||||
scoringPresetId: 'preset-1',
|
||||
scoringPresetName: 'Standard',
|
||||
dropPolicySummary: 'Drop 2 worst races',
|
||||
scoringPatternSummary: 'Points based on finish position',
|
||||
},
|
||||
timingSummary: 'Weekly races on Sundays',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
pendingJoinRequestsCount: 3,
|
||||
pendingProtestsCount: 1,
|
||||
walletBalance: 1000,
|
||||
};
|
||||
|
||||
const owner: GetDriverOutputDTO = {
|
||||
id: 'owner-1',
|
||||
name: 'John Doe',
|
||||
iracingId: '12345',
|
||||
country: 'USA',
|
||||
bio: 'Experienced driver',
|
||||
joinedAt: '2023-01-01T00:00:00.000Z',
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
};
|
||||
|
||||
const scoringConfig: LeagueScoringConfigDTO = {
|
||||
id: 'config-1',
|
||||
leagueId: 'league-1',
|
||||
gameId: 'game-1',
|
||||
gameName: 'iRacing',
|
||||
primaryChampionshipType: 'Single Championship',
|
||||
scoringPresetId: 'preset-1',
|
||||
scoringPresetName: 'Standard',
|
||||
dropPolicySummary: 'Drop 2 worst races',
|
||||
scoringPatternSummary: 'Points based on finish position',
|
||||
dropRaces: 2,
|
||||
pointsPerRace: 100,
|
||||
pointsForWin: 25,
|
||||
pointsForPodium: [20, 15, 10],
|
||||
};
|
||||
|
||||
const memberships: LeagueMembershipsDTO = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
name: 'Alice',
|
||||
iracingId: '11111',
|
||||
country: 'UK',
|
||||
joinedAt: '2023-06-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'admin',
|
||||
joinedAt: '2023-06-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
name: 'Bob',
|
||||
iracingId: '22222',
|
||||
country: 'Germany',
|
||||
joinedAt: '2023-07-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'steward',
|
||||
joinedAt: '2023-07-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
name: 'Charlie',
|
||||
iracingId: '33333',
|
||||
country: 'France',
|
||||
joinedAt: '2023-08-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'member',
|
||||
joinedAt: '2023-08-01T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const races: RaceDTO[] = [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: '2024-01-15T14:00:00.000Z',
|
||||
track: 'Spa',
|
||||
car: 'Porsche 911 GT3',
|
||||
sessionType: 'race',
|
||||
strengthOfField: 1500,
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Race 2',
|
||||
date: '2024-01-22T14:00:00.000Z',
|
||||
track: 'Monza',
|
||||
car: 'Ferrari 488 GT3',
|
||||
sessionType: 'race',
|
||||
strengthOfField: 1600,
|
||||
},
|
||||
];
|
||||
|
||||
const sponsors: any[] = [
|
||||
{
|
||||
id: 'sponsor-1',
|
||||
name: 'Sponsor A',
|
||||
tier: 'main',
|
||||
logoUrl: 'https://example.com/sponsor-a.png',
|
||||
websiteUrl: 'https://sponsor-a.com',
|
||||
tagline: 'Premium racing gear',
|
||||
},
|
||||
];
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner,
|
||||
scoringConfig,
|
||||
memberships,
|
||||
races,
|
||||
sponsors,
|
||||
});
|
||||
|
||||
expect(result.leagueId).toBe('league-1');
|
||||
expect(result.name).toBe('Pro League');
|
||||
expect(result.description).toBe('A competitive league for experienced drivers');
|
||||
expect(result.logoUrl).toBe('https://example.com/logo.png');
|
||||
expect(result.info.name).toBe('Pro League');
|
||||
expect(result.info.description).toBe('A competitive league for experienced drivers');
|
||||
expect(result.info.membersCount).toBe(3);
|
||||
expect(result.info.racesCount).toBe(2);
|
||||
expect(result.info.avgSOF).toBe(1550);
|
||||
expect(result.info.structure).toBe('Solo • 32 max');
|
||||
expect(result.info.scoring).toBe('preset-1');
|
||||
expect(result.info.createdAt).toBe('2024-01-01T00:00:00.000Z');
|
||||
expect(result.info.discordUrl).toBeUndefined();
|
||||
expect(result.info.youtubeUrl).toBeUndefined();
|
||||
expect(result.info.websiteUrl).toBeUndefined();
|
||||
expect(result.ownerSummary).not.toBeNull();
|
||||
expect(result.ownerSummary?.driverId).toBe('owner-1');
|
||||
expect(result.ownerSummary?.driverName).toBe('John Doe');
|
||||
expect(result.ownerSummary?.avatarUrl).toBe('https://example.com/avatar.jpg');
|
||||
expect(result.ownerSummary?.roleBadgeText).toBe('Owner');
|
||||
expect(result.adminSummaries).toHaveLength(1);
|
||||
expect(result.adminSummaries[0].driverId).toBe('driver-1');
|
||||
expect(result.adminSummaries[0].driverName).toBe('Alice');
|
||||
expect(result.adminSummaries[0].roleBadgeText).toBe('Admin');
|
||||
expect(result.stewardSummaries).toHaveLength(1);
|
||||
expect(result.stewardSummaries[0].driverId).toBe('driver-2');
|
||||
expect(result.stewardSummaries[0].driverName).toBe('Bob');
|
||||
expect(result.stewardSummaries[0].roleBadgeText).toBe('Steward');
|
||||
expect(result.memberSummaries).toHaveLength(1);
|
||||
expect(result.memberSummaries[0].driverId).toBe('driver-3');
|
||||
expect(result.memberSummaries[0].driverName).toBe('Charlie');
|
||||
expect(result.memberSummaries[0].roleBadgeText).toBe('Member');
|
||||
expect(result.sponsors).toHaveLength(1);
|
||||
expect(result.sponsors[0].id).toBe('sponsor-1');
|
||||
expect(result.sponsors[0].name).toBe('Sponsor A');
|
||||
expect(result.sponsors[0].tier).toBe('main');
|
||||
expect(result.walletBalance).toBe(1000);
|
||||
expect(result.pendingProtestsCount).toBe(1);
|
||||
expect(result.pendingJoinRequestsCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should handle league with no owner', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.ownerSummary).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle league with no scoring config', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.info.scoring).toBe('Standard');
|
||||
});
|
||||
|
||||
it('should handle league with no races', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.info.racesCount).toBe(0);
|
||||
expect(result.info.avgSOF).toBeNull();
|
||||
expect(result.runningRaces).toEqual([]);
|
||||
expect(result.nextRace).toBeUndefined();
|
||||
expect(result.seasonProgress).toEqual({
|
||||
completedRaces: 0,
|
||||
totalRaces: 0,
|
||||
percentage: 0,
|
||||
});
|
||||
expect(result.recentResults).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('data transformation', () => {
|
||||
it('should preserve all DTO fields in the output', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
qualifyingFormat: 'Solo • 32 max',
|
||||
},
|
||||
usedSlots: 20,
|
||||
category: 'test',
|
||||
scoring: {
|
||||
gameId: 'game-1',
|
||||
gameName: 'Test Game',
|
||||
primaryChampionshipType: 'Test Type',
|
||||
scoringPresetId: 'preset-1',
|
||||
scoringPresetName: 'Test Preset',
|
||||
dropPolicySummary: 'Test drop policy',
|
||||
scoringPatternSummary: 'Test pattern',
|
||||
},
|
||||
timingSummary: 'Test timing',
|
||||
logoUrl: 'https://example.com/test.png',
|
||||
pendingJoinRequestsCount: 5,
|
||||
pendingProtestsCount: 2,
|
||||
walletBalance: 500,
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.leagueId).toBe(league.id);
|
||||
expect(result.name).toBe(league.name);
|
||||
expect(result.description).toBe(league.description);
|
||||
expect(result.logoUrl).toBe(league.logoUrl);
|
||||
expect(result.walletBalance).toBe(league.walletBalance);
|
||||
expect(result.pendingProtestsCount).toBe(league.pendingProtestsCount);
|
||||
expect(result.pendingJoinRequestsCount).toBe(league.pendingJoinRequestsCount);
|
||||
});
|
||||
|
||||
it('should not modify the input DTOs', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 20,
|
||||
};
|
||||
|
||||
const originalLeague = JSON.parse(JSON.stringify(league));
|
||||
LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(league).toEqual(originalLeague);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle league with missing optional fields', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Minimal League',
|
||||
description: '',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.description).toBe('');
|
||||
expect(result.logoUrl).toBeUndefined();
|
||||
expect(result.info.description).toBe('');
|
||||
expect(result.info.discordUrl).toBeUndefined();
|
||||
expect(result.info.youtubeUrl).toBeUndefined();
|
||||
expect(result.info.websiteUrl).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle races with missing strengthOfField', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const races: RaceDTO[] = [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: '2024-01-15T14:00:00.000Z',
|
||||
track: 'Spa',
|
||||
car: 'Porsche 911 GT3',
|
||||
sessionType: 'race',
|
||||
},
|
||||
];
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races,
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.info.avgSOF).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle races with zero strengthOfField', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const races: RaceDTO[] = [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: '2024-01-15T14:00:00.000Z',
|
||||
track: 'Spa',
|
||||
car: 'Porsche 911 GT3',
|
||||
sessionType: 'race',
|
||||
strengthOfField: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races,
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.info.avgSOF).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle races with different dates for next race calculation', () => {
|
||||
const now = new Date();
|
||||
const pastDate = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 1 day ago
|
||||
const futureDate = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 1 day from now
|
||||
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const races: RaceDTO[] = [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Past Race',
|
||||
date: pastDate.toISOString(),
|
||||
track: 'Spa',
|
||||
car: 'Porsche 911 GT3',
|
||||
sessionType: 'race',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Future Race',
|
||||
date: futureDate.toISOString(),
|
||||
track: 'Monza',
|
||||
car: 'Ferrari 488 GT3',
|
||||
sessionType: 'race',
|
||||
},
|
||||
];
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships: { members: [] },
|
||||
races,
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.nextRace).toBeDefined();
|
||||
expect(result.nextRace?.id).toBe('race-2');
|
||||
expect(result.nextRace?.name).toBe('Future Race');
|
||||
expect(result.seasonProgress.completedRaces).toBe(1);
|
||||
expect(result.seasonProgress.totalRaces).toBe(2);
|
||||
expect(result.seasonProgress.percentage).toBe(50);
|
||||
expect(result.recentResults).toHaveLength(1);
|
||||
expect(result.recentResults[0].raceId).toBe('race-1');
|
||||
});
|
||||
|
||||
it('should handle members with different roles', () => {
|
||||
const league: LeagueWithCapacityAndScoringDTO = {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test description',
|
||||
ownerId: 'owner-1',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
settings: {
|
||||
maxDrivers: 32,
|
||||
},
|
||||
usedSlots: 10,
|
||||
};
|
||||
|
||||
const memberships: LeagueMembershipsDTO = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
name: 'Admin',
|
||||
iracingId: '11111',
|
||||
country: 'UK',
|
||||
joinedAt: '2023-06-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'admin',
|
||||
joinedAt: '2023-06-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
name: 'Steward',
|
||||
iracingId: '22222',
|
||||
country: 'Germany',
|
||||
joinedAt: '2023-07-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'steward',
|
||||
joinedAt: '2023-07-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
name: 'Member',
|
||||
iracingId: '33333',
|
||||
country: 'France',
|
||||
joinedAt: '2023-08-01T00:00:00.000Z',
|
||||
},
|
||||
role: 'member',
|
||||
joinedAt: '2023-08-01T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = LeagueDetailViewDataBuilder.build({
|
||||
league,
|
||||
owner: null,
|
||||
scoringConfig: null,
|
||||
memberships,
|
||||
races: [],
|
||||
sponsors: [],
|
||||
});
|
||||
|
||||
expect(result.adminSummaries).toHaveLength(1);
|
||||
expect(result.stewardSummaries).toHaveLength(1);
|
||||
expect(result.memberSummaries).toHaveLength(1);
|
||||
expect(result.info.membersCount).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user