website refactor

This commit is contained in:
2026-01-21 22:36:01 +01:00
parent ea58909070
commit 5ed958281d
49 changed files with 8763 additions and 131 deletions

View File

@@ -0,0 +1,827 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { LeagueDetailViewDataBuilder } from '../../../apps/website/lib/builders/view-data/LeagueDetailViewDataBuilder';
import type { LeagueWithCapacityAndScoringDTO } from '../../../apps/website/lib/types/generated/LeagueWithCapacityAndScoringDTO';
import type { LeagueMembershipsDTO } from '../../../apps/website/lib/types/generated/LeagueMembershipsDTO';
import type { RaceDTO } from '../../../apps/website/lib/types/generated/RaceDTO';
import type { GetDriverOutputDTO } from '../../../apps/website/lib/types/generated/GetDriverOutputDTO';
import type { LeagueScoringConfigDTO } from '../../../apps/website/lib/types/generated/LeagueScoringConfigDTO';
describe('LeagueDetailViewDataBuilder', () => {
const mockLeague: LeagueWithCapacityAndScoringDTO = {
id: 'league-123',
name: 'Test League',
description: 'A test league description',
ownerId: 'owner-456',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
socialLinks: {
discordUrl: 'https://discord.gg/test',
youtubeUrl: 'https://youtube.com/test',
websiteUrl: 'https://test.com',
},
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
logoUrl: 'https://logo.com/test.png',
pendingJoinRequestsCount: 3,
pendingProtestsCount: 1,
walletBalance: 1000,
};
const mockOwner: GetDriverOutputDTO = {
id: 'owner-456',
iracingId: '12345',
name: 'John Doe',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
avatarUrl: 'https://avatar.com/john.png',
rating: 850,
};
const mockScoringConfig: LeagueScoringConfigDTO = {
leagueId: 'league-123',
seasonId: 'season-1',
gameId: 'game-1',
gameName: 'Test Game',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
championships: [],
};
const mockMemberships: LeagueMembershipsDTO = {
members: [
{
driverId: 'owner-456',
driver: {
id: 'owner-456',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
role: 'owner',
joinedAt: '2024-01-01T00:00:00Z',
},
{
driverId: 'admin-789',
driver: {
id: 'admin-789',
name: 'Jane Smith',
iracingId: '67890',
country: 'UK',
joinedAt: '2024-01-02T00:00:00Z',
},
role: 'admin',
joinedAt: '2024-01-02T00:00:00Z',
},
{
driverId: 'steward-101',
driver: {
id: 'steward-101',
name: 'Bob Wilson',
iracingId: '11111',
country: 'CA',
joinedAt: '2024-01-03T00:00:00Z',
},
role: 'steward',
joinedAt: '2024-01-03T00:00:00Z',
},
{
driverId: 'member-202',
driver: {
id: 'member-202',
name: 'Alice Brown',
iracingId: '22222',
country: 'AU',
joinedAt: '2024-01-04T00:00:00Z',
},
role: 'member',
joinedAt: '2024-01-04T00:00:00Z',
},
],
};
const mockSponsors = [
{
id: 'sponsor-1',
name: 'Test Sponsor',
tier: 'main' as const,
logoUrl: 'https://sponsor.com/logo.png',
websiteUrl: 'https://sponsor.com',
tagline: 'Best sponsor ever',
},
];
describe('build()', () => {
it('should transform all input data correctly', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Race 2',
date: '2024-02-08T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.leagueId).toBe('league-123');
expect(result.name).toBe('Test League');
expect(result.description).toBe('A test league description');
expect(result.logoUrl).toBe('https://logo.com/test.png');
expect(result.walletBalance).toBe(1000);
expect(result.pendingProtestsCount).toBe(1);
expect(result.pendingJoinRequestsCount).toBe(3);
// Check info data
expect(result.info.name).toBe('Test League');
expect(result.info.description).toBe('A test league description');
expect(result.info.membersCount).toBe(4);
expect(result.info.racesCount).toBe(2);
expect(result.info.avgSOF).toBeNull();
expect(result.info.structure).toBe('Solo • 32 max');
expect(result.info.scoring).toBe('preset-1');
expect(result.info.createdAt).toBe('2024-01-01T00:00:00Z');
expect(result.info.discordUrl).toBe('https://discord.gg/test');
expect(result.info.youtubeUrl).toBe('https://youtube.com/test');
expect(result.info.websiteUrl).toBe('https://test.com');
// Check owner summary
expect(result.ownerSummary).not.toBeNull();
expect(result.ownerSummary?.driverId).toBe('owner-456');
expect(result.ownerSummary?.driverName).toBe('John Doe');
expect(result.ownerSummary?.avatarUrl).toBe('https://avatar.com/john.png');
expect(result.ownerSummary?.roleBadgeText).toBe('Owner');
expect(result.ownerSummary?.profileUrl).toBe('/drivers/owner-456');
// Check admin summaries
expect(result.adminSummaries).toHaveLength(1);
expect(result.adminSummaries[0].driverId).toBe('admin-789');
expect(result.adminSummaries[0].roleBadgeText).toBe('Admin');
// Check steward summaries
expect(result.stewardSummaries).toHaveLength(1);
expect(result.stewardSummaries[0].driverId).toBe('steward-101');
expect(result.stewardSummaries[0].roleBadgeText).toBe('Steward');
// Check member summaries
expect(result.memberSummaries).toHaveLength(1);
expect(result.memberSummaries[0].driverId).toBe('member-202');
expect(result.memberSummaries[0].roleBadgeText).toBe('Member');
// Check sponsors
expect(result.sponsors).toHaveLength(1);
expect(result.sponsors[0].id).toBe('sponsor-1');
expect(result.sponsors[0].name).toBe('Test Sponsor');
expect(result.sponsors[0].tier).toBe('main');
// Check running races (empty in this case)
expect(result.runningRaces).toEqual([]);
});
it('should calculate next race correctly', () => {
const now = new Date();
const futureDate = new Date(now.getTime() + 86400000).toISOString(); // 1 day from now
const pastDate = new Date(now.getTime() - 86400000).toISOString(); // 1 day ago
const races: RaceDTO[] = [
{
id: 'race-past',
name: 'Past Race',
date: pastDate,
leagueName: 'Test League',
},
{
id: 'race-future-1',
name: 'Future Race 1',
date: futureDate,
leagueName: 'Test League',
},
{
id: 'race-future-2',
name: 'Future Race 2',
date: new Date(now.getTime() + 172800000).toISOString(), // 2 days from now
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.nextRace).toBeDefined();
expect(result.nextRace?.id).toBe('race-future-1');
expect(result.nextRace?.name).toBe('Future Race 1');
expect(result.nextRace?.date).toBe(futureDate);
});
it('should handle no upcoming races', () => {
const pastDate = new Date(Date.now() - 86400000).toISOString();
const races: RaceDTO[] = [
{
id: 'race-past-1',
name: 'Past Race 1',
date: pastDate,
leagueName: 'Test League',
},
{
id: 'race-past-2',
name: 'Past Race 2',
date: new Date(Date.now() - 172800000).toISOString(),
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.nextRace).toBeUndefined();
});
it('should calculate season progress correctly', () => {
const now = new Date();
const pastDate = new Date(now.getTime() - 86400000).toISOString();
const futureDate = new Date(now.getTime() + 86400000).toISOString();
const races: RaceDTO[] = [
{
id: 'race-past-1',
name: 'Past Race 1',
date: pastDate,
leagueName: 'Test League',
},
{
id: 'race-past-2',
name: 'Past Race 2',
date: new Date(now.getTime() - 172800000).toISOString(),
leagueName: 'Test League',
},
{
id: 'race-future-1',
name: 'Future Race 1',
date: futureDate,
leagueName: 'Test League',
},
{
id: 'race-future-2',
name: 'Future Race 2',
date: new Date(now.getTime() + 172800000).toISOString(),
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.seasonProgress).toBeDefined();
expect(result.seasonProgress?.completedRaces).toBe(2);
expect(result.seasonProgress?.totalRaces).toBe(4);
expect(result.seasonProgress?.percentage).toBe(50);
});
it('should handle no races for season progress', () => {
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races: [],
sponsors: mockSponsors,
});
expect(result.seasonProgress).toBeDefined();
expect(result.seasonProgress?.completedRaces).toBe(0);
expect(result.seasonProgress?.totalRaces).toBe(0);
expect(result.seasonProgress?.percentage).toBe(0);
});
it('should extract recent results from last completed race', () => {
const now = new Date();
const pastDate = new Date(now.getTime() - 86400000).toISOString();
const futureDate = new Date(now.getTime() + 86400000).toISOString();
const races: RaceDTO[] = [
{
id: 'race-past-1',
name: 'Past Race 1',
date: pastDate,
leagueName: 'Test League',
},
{
id: 'race-past-2',
name: 'Past Race 2',
date: new Date(now.getTime() - 172800000).toISOString(),
leagueName: 'Test League',
},
{
id: 'race-future-1',
name: 'Future Race 1',
date: futureDate,
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.recentResults).toBeDefined();
expect(result.recentResults?.length).toBe(2);
expect(result.recentResults?.[0].raceId).toBe('race-past-1');
expect(result.recentResults?.[0].raceName).toBe('Past Race 1');
expect(result.recentResults?.[1].raceId).toBe('race-past-2');
});
it('should handle no completed races for recent results', () => {
const futureDate = new Date(Date.now() + 86400000).toISOString();
const races: RaceDTO[] = [
{
id: 'race-future-1',
name: 'Future Race 1',
date: futureDate,
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.recentResults).toBeDefined();
expect(result.recentResults?.length).toBe(0);
});
it('should handle null owner', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: null,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.ownerSummary).toBeNull();
});
it('should handle null scoring config', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: null,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.info.scoring).toBe('Standard');
});
it('should handle empty memberships', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: { members: [] },
races,
sponsors: mockSponsors,
});
expect(result.info.membersCount).toBe(0);
expect(result.adminSummaries).toHaveLength(0);
expect(result.stewardSummaries).toHaveLength(0);
expect(result.memberSummaries).toHaveLength(0);
});
it('should calculate avgSOF from races with strengthOfField', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Race 2',
date: '2024-02-08T18:00:00Z',
leagueName: 'Test League',
},
];
// Add strengthOfField to races
(races[0] as any).strengthOfField = 1500;
(races[1] as any).strengthOfField = 1800;
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.info.avgSOF).toBe(1650);
});
it('should ignore races with zero or null strengthOfField', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Race 2',
date: '2024-02-08T18:00:00Z',
leagueName: 'Test League',
},
];
// Add strengthOfField to races
(races[0] as any).strengthOfField = 0;
(races[1] as any).strengthOfField = null;
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.info.avgSOF).toBeNull();
});
it('should handle empty races array', () => {
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races: [],
sponsors: mockSponsors,
});
expect(result.info.racesCount).toBe(0);
expect(result.info.avgSOF).toBeNull();
expect(result.nextRace).toBeUndefined();
expect(result.seasonProgress?.completedRaces).toBe(0);
expect(result.seasonProgress?.totalRaces).toBe(0);
expect(result.recentResults?.length).toBe(0);
});
it('should handle empty sponsors array', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: [],
});
expect(result.sponsors).toHaveLength(0);
});
it('should handle missing social links', () => {
const leagueWithoutSocialLinks: LeagueWithCapacityAndScoringDTO = {
...mockLeague,
socialLinks: undefined,
};
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: leagueWithoutSocialLinks,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.info.discordUrl).toBeUndefined();
expect(result.info.youtubeUrl).toBeUndefined();
expect(result.info.websiteUrl).toBeUndefined();
});
it('should handle missing category', () => {
const leagueWithoutCategory: LeagueWithCapacityAndScoringDTO = {
...mockLeague,
category: undefined,
};
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: leagueWithoutCategory,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.info).toBeDefined();
});
it('should handle missing description', () => {
const leagueWithoutDescription: LeagueWithCapacityAndScoringDTO = {
...mockLeague,
description: '',
};
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: leagueWithoutDescription,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.description).toBe('');
expect(result.info.description).toBe('');
});
it('should handle missing logoUrl', () => {
const leagueWithoutLogo: LeagueWithCapacityAndScoringDTO = {
...mockLeague,
logoUrl: undefined,
};
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: leagueWithoutLogo,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.logoUrl).toBeUndefined();
});
it('should handle missing admin fields', () => {
const leagueWithoutAdminFields: LeagueWithCapacityAndScoringDTO = {
...mockLeague,
pendingJoinRequestsCount: undefined,
pendingProtestsCount: undefined,
walletBalance: undefined,
};
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: leagueWithoutAdminFields,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.walletBalance).toBeUndefined();
expect(result.pendingProtestsCount).toBeUndefined();
expect(result.pendingJoinRequestsCount).toBeUndefined();
});
it('should extract running races correctly', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Running Race 1',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Past Race',
date: '2024-01-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-3',
name: 'Running Race 2',
date: '2024-02-08T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.runningRaces).toHaveLength(2);
expect(result.runningRaces[0].id).toBe('race-1');
expect(result.runningRaces[0].name).toBe('Running Race 1');
expect(result.runningRaces[0].date).toBe('2024-02-01T18:00:00Z');
expect(result.runningRaces[1].id).toBe('race-3');
expect(result.runningRaces[1].name).toBe('Running Race 2');
});
it('should handle no running races', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Past Race 1',
date: '2024-01-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Past Race 2',
date: '2024-01-08T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.runningRaces).toEqual([]);
});
it('should handle races with "Running" in different positions', () => {
const races: RaceDTO[] = [
{
id: 'race-1',
name: 'Race Running',
date: '2024-02-01T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-2',
name: 'Running',
date: '2024-02-08T18:00:00Z',
leagueName: 'Test League',
},
{
id: 'race-3',
name: 'Completed Race',
date: '2024-02-15T18:00:00Z',
leagueName: 'Test League',
},
];
const result = LeagueDetailViewDataBuilder.build({
league: mockLeague,
owner: mockOwner,
scoringConfig: mockScoringConfig,
memberships: mockMemberships,
races,
sponsors: mockSponsors,
});
expect(result.runningRaces).toHaveLength(2);
expect(result.runningRaces[0].id).toBe('race-1');
expect(result.runningRaces[1].id).toBe('race-2');
});
});
});

View File

@@ -0,0 +1,386 @@
import { describe, it, expect } from 'vitest';
import { LeagueScheduleViewDataBuilder } from '../../../apps/website/lib/builders/view-data/LeagueScheduleViewDataBuilder';
import type { LeagueScheduleApiDto } from '../../../apps/website/lib/types/tbd/LeagueScheduleApiDto';
describe('LeagueScheduleViewDataBuilder', () => {
const mockApiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
track: 'Track A',
car: 'Car A',
sessionType: 'Qualifying',
},
{
id: 'race-2',
name: 'Race 2',
date: '2024-02-08T18:00:00Z',
track: 'Track B',
car: 'Car B',
sessionType: 'Race',
},
{
id: 'race-3',
name: 'Race 3',
date: '2024-02-15T18:00:00Z',
track: 'Track C',
car: 'Car C',
sessionType: 'Race',
},
],
};
describe('build()', () => {
it('should transform all races correctly', () => {
const result = LeagueScheduleViewDataBuilder.build(mockApiDto);
expect(result.leagueId).toBe('league-123');
expect(result.races).toHaveLength(3);
// Check first race
expect(result.races[0].id).toBe('race-1');
expect(result.races[0].name).toBe('Race 1');
expect(result.races[0].scheduledAt).toBe('2024-02-01T18:00:00Z');
expect(result.races[0].track).toBe('Track A');
expect(result.races[0].car).toBe('Car A');
expect(result.races[0].sessionType).toBe('Qualifying');
});
it('should mark past races correctly', () => {
const now = new Date();
const pastDate = new Date(now.getTime() - 86400000).toISOString(); // 1 day ago
const futureDate = new Date(now.getTime() + 86400000).toISOString(); // 1 day from now
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-past',
name: 'Past Race',
date: pastDate,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
{
id: 'race-future',
name: 'Future Race',
date: futureDate,
track: 'Track B',
car: 'Car B',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].isPast).toBe(true);
expect(result.races[0].isUpcoming).toBe(false);
expect(result.races[0].status).toBe('completed');
expect(result.races[1].isPast).toBe(false);
expect(result.races[1].isUpcoming).toBe(true);
expect(result.races[1].status).toBe('scheduled');
});
it('should mark upcoming races correctly', () => {
const now = new Date();
const futureDate = new Date(now.getTime() + 86400000).toISOString(); // 1 day from now
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-future',
name: 'Future Race',
date: futureDate,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].isPast).toBe(false);
expect(result.races[0].isUpcoming).toBe(true);
expect(result.races[0].status).toBe('scheduled');
});
it('should handle empty schedule', () => {
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.leagueId).toBe('league-123');
expect(result.races).toHaveLength(0);
});
it('should handle races with missing optional fields', () => {
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
track: undefined,
car: undefined,
sessionType: undefined,
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].track).toBeUndefined();
expect(result.races[0].car).toBeUndefined();
expect(result.races[0].sessionType).toBeUndefined();
});
it('should handle current driver ID parameter', () => {
const result = LeagueScheduleViewDataBuilder.build(mockApiDto, 'driver-456');
expect(result.currentDriverId).toBe('driver-456');
});
it('should handle admin permission parameter', () => {
const result = LeagueScheduleViewDataBuilder.build(mockApiDto, 'driver-456', true);
expect(result.isAdmin).toBe(true);
expect(result.races[0].canEdit).toBe(true);
expect(result.races[0].canReschedule).toBe(true);
});
it('should handle non-admin permission parameter', () => {
const result = LeagueScheduleViewDataBuilder.build(mockApiDto, 'driver-456', false);
expect(result.isAdmin).toBe(false);
expect(result.races[0].canEdit).toBe(false);
expect(result.races[0].canReschedule).toBe(false);
});
it('should handle default admin parameter as false', () => {
const result = LeagueScheduleViewDataBuilder.build(mockApiDto, 'driver-456');
expect(result.isAdmin).toBe(false);
expect(result.races[0].canEdit).toBe(false);
expect(result.races[0].canReschedule).toBe(false);
});
it('should handle registration status for upcoming races', () => {
const now = new Date();
const futureDate = new Date(now.getTime() + 86400000).toISOString();
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-future',
name: 'Future Race',
date: futureDate,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].isUserRegistered).toBe(false);
expect(result.races[0].canRegister).toBe(true);
});
it('should handle registration status for past races', () => {
const now = new Date();
const pastDate = new Date(now.getTime() - 86400000).toISOString();
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-past',
name: 'Past Race',
date: pastDate,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].isUserRegistered).toBe(false);
expect(result.races[0].canRegister).toBe(false);
});
it('should handle races exactly at current time', () => {
const now = new Date();
const exactDate = now.toISOString();
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-exact',
name: 'Exact Race',
date: exactDate,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
// Race at exact current time is considered upcoming (not past)
// because the comparison uses < (strictly less than)
expect(result.races[0].isPast).toBe(false);
expect(result.races[0].isUpcoming).toBe(true);
expect(result.races[0].status).toBe('scheduled');
});
it('should handle races with different session types', () => {
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-qualifying',
name: 'Qualifying',
date: '2024-02-01T18:00:00Z',
track: 'Track A',
car: 'Car A',
sessionType: 'Qualifying',
},
{
id: 'race-practice',
name: 'Practice',
date: '2024-02-02T18:00:00Z',
track: 'Track B',
car: 'Car B',
sessionType: 'Practice',
},
{
id: 'race-race',
name: 'Race',
date: '2024-02-03T18:00:00Z',
track: 'Track C',
car: 'Car C',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].sessionType).toBe('Qualifying');
expect(result.races[1].sessionType).toBe('Practice');
expect(result.races[2].sessionType).toBe('Race');
});
it('should handle races without session type', () => {
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
track: 'Track A',
car: 'Car A',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].sessionType).toBeUndefined();
});
it('should handle races with empty track and car', () => {
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-1',
name: 'Race 1',
date: '2024-02-01T18:00:00Z',
track: '',
car: '',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races[0].track).toBe('');
expect(result.races[0].car).toBe('');
});
it('should handle multiple races with mixed dates', () => {
const now = new Date();
const pastDate1 = new Date(now.getTime() - 172800000).toISOString(); // 2 days ago
const pastDate2 = new Date(now.getTime() - 86400000).toISOString(); // 1 day ago
const futureDate1 = new Date(now.getTime() + 86400000).toISOString(); // 1 day from now
const futureDate2 = new Date(now.getTime() + 172800000).toISOString(); // 2 days from now
const apiDto: LeagueScheduleApiDto = {
leagueId: 'league-123',
races: [
{
id: 'race-past-2',
name: 'Past Race 2',
date: pastDate1,
track: 'Track A',
car: 'Car A',
sessionType: 'Race',
},
{
id: 'race-past-1',
name: 'Past Race 1',
date: pastDate2,
track: 'Track B',
car: 'Car B',
sessionType: 'Race',
},
{
id: 'race-future-1',
name: 'Future Race 1',
date: futureDate1,
track: 'Track C',
car: 'Car C',
sessionType: 'Race',
},
{
id: 'race-future-2',
name: 'Future Race 2',
date: futureDate2,
track: 'Track D',
car: 'Car D',
sessionType: 'Race',
},
],
};
const result = LeagueScheduleViewDataBuilder.build(apiDto);
expect(result.races).toHaveLength(4);
expect(result.races[0].isPast).toBe(true);
expect(result.races[1].isPast).toBe(true);
expect(result.races[2].isPast).toBe(false);
expect(result.races[3].isPast).toBe(false);
});
});
});

View File

@@ -0,0 +1,541 @@
import { describe, it, expect } from 'vitest';
import { LeagueStandingsViewDataBuilder } from '../../../apps/website/lib/builders/view-data/LeagueStandingsViewDataBuilder';
import type { LeagueStandingDTO } from '../../../apps/website/lib/types/generated/LeagueStandingDTO';
import type { LeagueMemberDTO } from '../../../apps/website/lib/types/generated/LeagueMemberDTO';
describe('LeagueStandingsViewDataBuilder', () => {
const mockStandings: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: 0,
lastRacePoints: 25,
droppedRaceIds: ['race-1', 'race-2'],
},
{
driverId: 'driver-2',
driver: {
id: 'driver-2',
name: 'Jane Smith',
iracingId: '67890',
country: 'UK',
joinedAt: '2024-01-02T00:00:00Z',
},
points: 120,
position: 2,
wins: 2,
podiums: 4,
races: 10,
positionChange: 1,
lastRacePoints: 18,
droppedRaceIds: ['race-3'],
},
{
driverId: 'driver-3',
driver: {
id: 'driver-3',
name: 'Bob Wilson',
iracingId: '11111',
country: 'CA',
joinedAt: '2024-01-03T00:00:00Z',
},
points: 90,
position: 3,
wins: 1,
podiums: 3,
races: 10,
positionChange: -1,
lastRacePoints: 12,
droppedRaceIds: [],
},
];
const mockMemberships: LeagueMemberDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
role: 'member',
joinedAt: '2024-01-01T00:00:00Z',
},
{
driverId: 'driver-2',
driver: {
id: 'driver-2',
name: 'Jane Smith',
iracingId: '67890',
country: 'UK',
joinedAt: '2024-01-02T00:00:00Z',
},
role: 'member',
joinedAt: '2024-01-02T00:00:00Z',
},
];
describe('build()', () => {
it('should transform standings correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.leagueId).toBe('league-123');
expect(result.standings).toHaveLength(3);
// Check first standing
expect(result.standings[0].driverId).toBe('driver-1');
expect(result.standings[0].position).toBe(1);
expect(result.standings[0].totalPoints).toBe(150);
expect(result.standings[0].racesFinished).toBe(10);
expect(result.standings[0].racesStarted).toBe(10);
expect(result.standings[0].positionChange).toBe(0);
expect(result.standings[0].lastRacePoints).toBe(25);
expect(result.standings[0].droppedRaceIds).toEqual(['race-1', 'race-2']);
expect(result.standings[0].wins).toBe(3);
expect(result.standings[0].podiums).toBe(5);
});
it('should calculate position change correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].positionChange).toBe(0); // No change
expect(result.standings[1].positionChange).toBe(1); // Moved up
expect(result.standings[2].positionChange).toBe(-1); // Moved down
});
it('should map last race points correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].lastRacePoints).toBe(25);
expect(result.standings[1].lastRacePoints).toBe(18);
expect(result.standings[2].lastRacePoints).toBe(12);
});
it('should handle dropped race IDs correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].droppedRaceIds).toEqual(['race-1', 'race-2']);
expect(result.standings[1].droppedRaceIds).toEqual(['race-3']);
expect(result.standings[2].droppedRaceIds).toEqual([]);
});
it('should calculate championship stats (wins, podiums)', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].wins).toBe(3);
expect(result.standings[0].podiums).toBe(5);
expect(result.standings[1].wins).toBe(2);
expect(result.standings[1].podiums).toBe(4);
expect(result.standings[2].wins).toBe(1);
expect(result.standings[2].podiums).toBe(3);
});
it('should extract driver metadata correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.drivers).toHaveLength(3);
// Check first driver
expect(result.drivers[0].id).toBe('driver-1');
expect(result.drivers[0].name).toBe('John Doe');
expect(result.drivers[0].iracingId).toBe('12345');
expect(result.drivers[0].country).toBe('US');
});
it('should convert memberships correctly', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.memberships).toHaveLength(2);
// Check first membership
expect(result.memberships[0].driverId).toBe('driver-1');
expect(result.memberships[0].leagueId).toBe('league-123');
expect(result.memberships[0].role).toBe('member');
expect(result.memberships[0].status).toBe('active');
});
it('should handle empty standings', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: [] },
{ members: mockMemberships },
'league-123'
);
expect(result.standings).toHaveLength(0);
expect(result.drivers).toHaveLength(0);
});
it('should handle empty memberships', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: [] },
'league-123'
);
expect(result.memberships).toHaveLength(0);
});
it('should handle missing driver objects in standings', () => {
const standingsWithMissingDriver: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: 0,
lastRacePoints: 25,
droppedRaceIds: [],
},
{
driverId: 'driver-2',
driver: {
id: 'driver-2',
name: 'Jane Smith',
iracingId: '67890',
country: 'UK',
joinedAt: '2024-01-02T00:00:00Z',
},
points: 120,
position: 2,
wins: 2,
podiums: 4,
races: 10,
positionChange: 1,
lastRacePoints: 18,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithMissingDriver },
{ members: mockMemberships },
'league-123'
);
expect(result.drivers).toHaveLength(2);
expect(result.drivers[0].id).toBe('driver-1');
expect(result.drivers[1].id).toBe('driver-2');
});
it('should handle standings with missing positionChange', () => {
const standingsWithoutPositionChange: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: undefined as any,
lastRacePoints: 25,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithoutPositionChange },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].positionChange).toBe(0);
});
it('should handle standings with missing lastRacePoints', () => {
const standingsWithoutLastRacePoints: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: 0,
lastRacePoints: undefined as any,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithoutLastRacePoints },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].lastRacePoints).toBe(0);
});
it('should handle standings with missing droppedRaceIds', () => {
const standingsWithoutDroppedRaceIds: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: 0,
lastRacePoints: 25,
droppedRaceIds: undefined as any,
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithoutDroppedRaceIds },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].droppedRaceIds).toEqual([]);
});
it('should handle standings with missing wins', () => {
const standingsWithoutWins: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: undefined as any,
podiums: 5,
races: 10,
positionChange: 0,
lastRacePoints: 25,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithoutWins },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].wins).toBe(0);
});
it('should handle standings with missing podiums', () => {
const standingsWithoutPodiums: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: undefined as any,
races: 10,
positionChange: 0,
lastRacePoints: 25,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithoutPodiums },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].podiums).toBe(0);
});
it('should handle team championship mode', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123',
true
);
expect(result.isTeamChampionship).toBe(true);
});
it('should handle non-team championship mode by default', () => {
const result = LeagueStandingsViewDataBuilder.build(
{ standings: mockStandings },
{ members: mockMemberships },
'league-123'
);
expect(result.isTeamChampionship).toBe(false);
});
it('should handle standings with zero points', () => {
const standingsWithZeroPoints: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 0,
position: 1,
wins: 0,
podiums: 0,
races: 10,
positionChange: 0,
lastRacePoints: 0,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithZeroPoints },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].totalPoints).toBe(0);
expect(result.standings[0].wins).toBe(0);
expect(result.standings[0].podiums).toBe(0);
});
it('should handle standings with negative position change', () => {
const standingsWithNegativeChange: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: -2,
lastRacePoints: 25,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithNegativeChange },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].positionChange).toBe(-2);
});
it('should handle standings with positive position change', () => {
const standingsWithPositiveChange: LeagueStandingDTO[] = [
{
driverId: 'driver-1',
driver: {
id: 'driver-1',
name: 'John Doe',
iracingId: '12345',
country: 'US',
joinedAt: '2024-01-01T00:00:00Z',
},
points: 150,
position: 1,
wins: 3,
podiums: 5,
races: 10,
positionChange: 3,
lastRacePoints: 25,
droppedRaceIds: [],
},
];
const result = LeagueStandingsViewDataBuilder.build(
{ standings: standingsWithPositiveChange },
{ members: mockMemberships },
'league-123'
);
expect(result.standings[0].positionChange).toBe(3);
});
});
});

View File

@@ -0,0 +1,932 @@
import { describe, it, expect } from 'vitest';
import { LeaguesViewDataBuilder } from '../../../apps/website/lib/builders/view-data/LeaguesViewDataBuilder';
import type { AllLeaguesWithCapacityAndScoringDTO } from '../../../apps/website/lib/types/generated/AllLeaguesWithCapacityAndScoringDTO';
import type { LeagueWithCapacityAndScoringDTO } from '../../../apps/website/lib/types/generated/LeagueWithCapacityAndScoringDTO';
describe('LeaguesViewDataBuilder', () => {
const mockLeagues: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League 1',
description: 'A test league description',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
socialLinks: {
discordUrl: 'https://discord.gg/test1',
youtubeUrl: 'https://youtube.com/test1',
websiteUrl: 'https://test1.com',
},
scoring: {
gameId: 'game-1',
gameName: 'Test Game 1',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
logoUrl: 'https://logo.com/test1.png',
pendingJoinRequestsCount: 3,
pendingProtestsCount: 1,
walletBalance: 1000,
},
{
id: 'league-2',
name: 'Test League 2',
description: 'Another test league',
ownerId: 'owner-2',
createdAt: '2024-01-02T00:00:00Z',
settings: {
maxDrivers: 16,
qualifyingFormat: 'Team',
},
usedSlots: 8,
category: 'Oval',
socialLinks: {
discordUrl: 'https://discord.gg/test2',
},
scoring: {
gameId: 'game-2',
gameName: 'Test Game 2',
primaryChampionshipType: 'Team',
scoringPresetId: 'preset-2',
scoringPresetName: 'Advanced',
dropPolicySummary: 'Drop 1 worst race',
scoringPatternSummary: 'Points based on finish position with bonuses',
},
timingSummary: 'Every Saturday at 7 PM',
logoUrl: 'https://logo.com/test2.png',
},
{
id: 'league-3',
name: 'Test League 3',
description: 'A third test league',
ownerId: 'owner-3',
createdAt: '2024-01-03T00:00:00Z',
settings: {
maxDrivers: 24,
qualifyingFormat: 'Solo',
},
usedSlots: 24,
category: 'Road',
scoring: {
gameId: 'game-3',
gameName: 'Test Game 3',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-3',
scoringPresetName: 'Custom',
dropPolicySummary: 'No drops',
scoringPatternSummary: 'Fixed points per position',
},
timingSummary: 'Every Friday at 9 PM',
},
];
describe('build()', () => {
it('should transform all leagues correctly', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues).toHaveLength(3);
// Check first league
expect(result.leagues[0].id).toBe('league-1');
expect(result.leagues[0].name).toBe('Test League 1');
expect(result.leagues[0].description).toBe('A test league description');
expect(result.leagues[0].logoUrl).toBe('https://logo.com/test1.png');
expect(result.leagues[0].ownerId).toBe('owner-1');
expect(result.leagues[0].createdAt).toBe('2024-01-01T00:00:00Z');
expect(result.leagues[0].maxDrivers).toBe(32);
expect(result.leagues[0].usedDriverSlots).toBe(15);
expect(result.leagues[0].structureSummary).toBe('Solo');
expect(result.leagues[0].timingSummary).toBe('Every Sunday at 8 PM');
expect(result.leagues[0].category).toBe('Road');
// Check scoring
expect(result.leagues[0].scoring).toBeDefined();
expect(result.leagues[0].scoring?.gameId).toBe('game-1');
expect(result.leagues[0].scoring?.gameName).toBe('Test Game 1');
expect(result.leagues[0].scoring?.primaryChampionshipType).toBe('Solo');
expect(result.leagues[0].scoring?.scoringPresetId).toBe('preset-1');
expect(result.leagues[0].scoring?.scoringPresetName).toBe('Standard');
expect(result.leagues[0].scoring?.dropPolicySummary).toBe('Drop 2 worst races');
expect(result.leagues[0].scoring?.scoringPatternSummary).toBe('Points based on finish position');
});
it('should handle leagues with missing description', () => {
const leaguesWithoutDescription: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: '',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutDescription,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].description).toBe(null);
});
it('should handle leagues with missing logoUrl', () => {
const leaguesWithoutLogo: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutLogo,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].logoUrl).toBe(null);
});
it('should handle leagues with missing category', () => {
const leaguesWithoutCategory: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutCategory,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].category).toBe(null);
});
it('should handle leagues with missing scoring', () => {
const leaguesWithoutScoring: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutScoring,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring).toBeUndefined();
});
it('should handle leagues with missing social links', () => {
const leaguesWithoutSocialLinks: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutSocialLinks,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0]).toBeDefined();
});
it('should handle leagues with missing timingSummary', () => {
const leaguesWithoutTimingSummary: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutTimingSummary,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].timingSummary).toBe('');
});
it('should handle empty leagues array', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: [],
totalCount: 0,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues).toHaveLength(0);
});
it('should handle leagues with different categories', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].category).toBe('Road');
expect(result.leagues[1].category).toBe('Oval');
expect(result.leagues[2].category).toBe('Road');
});
it('should handle leagues with different structures', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].structureSummary).toBe('Solo');
expect(result.leagues[1].structureSummary).toBe('Team');
expect(result.leagues[2].structureSummary).toBe('Solo');
});
it('should handle leagues with different scoring presets', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.scoringPresetName).toBe('Standard');
expect(result.leagues[1].scoring?.scoringPresetName).toBe('Advanced');
expect(result.leagues[2].scoring?.scoringPresetName).toBe('Custom');
});
it('should handle leagues with different drop policies', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.dropPolicySummary).toBe('Drop 2 worst races');
expect(result.leagues[1].scoring?.dropPolicySummary).toBe('Drop 1 worst race');
expect(result.leagues[2].scoring?.dropPolicySummary).toBe('No drops');
});
it('should handle leagues with different scoring patterns', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.scoringPatternSummary).toBe('Points based on finish position');
expect(result.leagues[1].scoring?.scoringPatternSummary).toBe('Points based on finish position with bonuses');
expect(result.leagues[2].scoring?.scoringPatternSummary).toBe('Fixed points per position');
});
it('should handle leagues with different primary championship types', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.primaryChampionshipType).toBe('Solo');
expect(result.leagues[1].scoring?.primaryChampionshipType).toBe('Team');
expect(result.leagues[2].scoring?.primaryChampionshipType).toBe('Solo');
});
it('should handle leagues with different game names', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.gameName).toBe('Test Game 1');
expect(result.leagues[1].scoring?.gameName).toBe('Test Game 2');
expect(result.leagues[2].scoring?.gameName).toBe('Test Game 3');
});
it('should handle leagues with different game IDs', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].scoring?.gameId).toBe('game-1');
expect(result.leagues[1].scoring?.gameId).toBe('game-2');
expect(result.leagues[2].scoring?.gameId).toBe('game-3');
});
it('should handle leagues with different max drivers', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].maxDrivers).toBe(32);
expect(result.leagues[1].maxDrivers).toBe(16);
expect(result.leagues[2].maxDrivers).toBe(24);
});
it('should handle leagues with different used slots', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].usedDriverSlots).toBe(15);
expect(result.leagues[1].usedDriverSlots).toBe(8);
expect(result.leagues[2].usedDriverSlots).toBe(24);
});
it('should handle leagues with different owners', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].ownerId).toBe('owner-1');
expect(result.leagues[1].ownerId).toBe('owner-2');
expect(result.leagues[2].ownerId).toBe('owner-3');
});
it('should handle leagues with different creation dates', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].createdAt).toBe('2024-01-01T00:00:00Z');
expect(result.leagues[1].createdAt).toBe('2024-01-02T00:00:00Z');
expect(result.leagues[2].createdAt).toBe('2024-01-03T00:00:00Z');
});
it('should handle leagues with different timing summaries', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].timingSummary).toBe('Every Sunday at 8 PM');
expect(result.leagues[1].timingSummary).toBe('Every Saturday at 7 PM');
expect(result.leagues[2].timingSummary).toBe('Every Friday at 9 PM');
});
it('should handle leagues with different names', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].name).toBe('Test League 1');
expect(result.leagues[1].name).toBe('Test League 2');
expect(result.leagues[2].name).toBe('Test League 3');
});
it('should handle leagues with different descriptions', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].description).toBe('A test league description');
expect(result.leagues[1].description).toBe('Another test league');
expect(result.leagues[2].description).toBe('A third test league');
});
it('should handle leagues with different logo URLs', () => {
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: mockLeagues,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].logoUrl).toBe('https://logo.com/test1.png');
expect(result.leagues[1].logoUrl).toBe('https://logo.com/test2.png');
expect(result.leagues[2].logoUrl).toBeNull();
});
it('should handle leagues with activeDriversCount', () => {
const leaguesWithActiveDrivers: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
// Add activeDriversCount to the league
(leaguesWithActiveDrivers[0] as any).activeDriversCount = 12;
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithActiveDrivers,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].activeDriversCount).toBe(12);
});
it('should handle leagues with nextRaceAt', () => {
const leaguesWithNextRace: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
// Add nextRaceAt to the league
(leaguesWithNextRace[0] as any).nextRaceAt = '2024-02-01T18:00:00Z';
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithNextRace,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].nextRaceAt).toBe('2024-02-01T18:00:00Z');
});
it('should handle leagues without activeDriversCount and nextRaceAt', () => {
const leaguesWithoutMetadata: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Test League',
description: 'A test league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithoutMetadata,
totalCount: 1,
};
const result = LeaguesViewDataBuilder.build(apiDto);
expect(result.leagues[0].activeDriversCount).toBeUndefined();
expect(result.leagues[0].nextRaceAt).toBeUndefined();
});
it('should handle leagues with different usedDriverSlots for featured leagues', () => {
const leaguesWithDifferentSlots: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Small League',
description: 'A small league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 16,
qualifyingFormat: 'Solo',
},
usedSlots: 8,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
{
id: 'league-2',
name: 'Large League',
description: 'A large league',
ownerId: 'owner-2',
createdAt: '2024-01-02T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 25,
category: 'Road',
scoring: {
gameId: 'game-2',
gameName: 'Test Game 2',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-2',
scoringPresetName: 'Advanced',
dropPolicySummary: 'Drop 1 worst race',
scoringPatternSummary: 'Points based on finish position with bonuses',
},
timingSummary: 'Every Saturday at 7 PM',
},
{
id: 'league-3',
name: 'Medium League',
description: 'A medium league',
ownerId: 'owner-3',
createdAt: '2024-01-03T00:00:00Z',
settings: {
maxDrivers: 24,
qualifyingFormat: 'Team',
},
usedSlots: 20,
category: 'Oval',
scoring: {
gameId: 'game-3',
gameName: 'Test Game 3',
primaryChampionshipType: 'Team',
scoringPresetId: 'preset-3',
scoringPresetName: 'Custom',
dropPolicySummary: 'No drops',
scoringPatternSummary: 'Fixed points per position',
},
timingSummary: 'Every Friday at 9 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithDifferentSlots,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
// Verify that usedDriverSlots is correctly mapped
expect(result.leagues[0].usedDriverSlots).toBe(8);
expect(result.leagues[1].usedDriverSlots).toBe(25);
expect(result.leagues[2].usedDriverSlots).toBe(20);
// Verify that leagues can be filtered for featured leagues (usedDriverSlots > 20)
const featuredLeagues = result.leagues.filter(l => (l.usedDriverSlots ?? 0) > 20);
expect(featuredLeagues).toHaveLength(1);
expect(featuredLeagues[0].id).toBe('league-2');
});
it('should handle leagues with different categories for filtering', () => {
const leaguesWithDifferentCategories: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'Road League 1',
description: 'A road league',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
{
id: 'league-2',
name: 'Oval League 1',
description: 'An oval league',
ownerId: 'owner-2',
createdAt: '2024-01-02T00:00:00Z',
settings: {
maxDrivers: 16,
qualifyingFormat: 'Solo',
},
usedSlots: 8,
category: 'Oval',
scoring: {
gameId: 'game-2',
gameName: 'Test Game 2',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-2',
scoringPresetName: 'Advanced',
dropPolicySummary: 'Drop 1 worst race',
scoringPatternSummary: 'Points based on finish position with bonuses',
},
timingSummary: 'Every Saturday at 7 PM',
},
{
id: 'league-3',
name: 'Road League 2',
description: 'Another road league',
ownerId: 'owner-3',
createdAt: '2024-01-03T00:00:00Z',
settings: {
maxDrivers: 24,
qualifyingFormat: 'Team',
},
usedSlots: 20,
category: 'Road',
scoring: {
gameId: 'game-3',
gameName: 'Test Game 3',
primaryChampionshipType: 'Team',
scoringPresetId: 'preset-3',
scoringPresetName: 'Custom',
dropPolicySummary: 'No drops',
scoringPatternSummary: 'Fixed points per position',
},
timingSummary: 'Every Friday at 9 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithDifferentCategories,
totalCount: 3,
};
const result = LeaguesViewDataBuilder.build(apiDto);
// Verify that category is correctly mapped
expect(result.leagues[0].category).toBe('Road');
expect(result.leagues[1].category).toBe('Oval');
expect(result.leagues[2].category).toBe('Road');
// Verify that leagues can be filtered by category
const roadLeagues = result.leagues.filter(l => l.category === 'Road');
expect(roadLeagues).toHaveLength(2);
expect(roadLeagues[0].id).toBe('league-1');
expect(roadLeagues[1].id).toBe('league-3');
const ovalLeagues = result.leagues.filter(l => l.category === 'Oval');
expect(ovalLeagues).toHaveLength(1);
expect(ovalLeagues[0].id).toBe('league-2');
});
it('should handle leagues with null category for filtering', () => {
const leaguesWithNullCategory: LeagueWithCapacityAndScoringDTO[] = [
{
id: 'league-1',
name: 'League with Category',
description: 'A league with category',
ownerId: 'owner-1',
createdAt: '2024-01-01T00:00:00Z',
settings: {
maxDrivers: 32,
qualifyingFormat: 'Solo',
},
usedSlots: 15,
category: 'Road',
scoring: {
gameId: 'game-1',
gameName: 'Test Game',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-1',
scoringPresetName: 'Standard',
dropPolicySummary: 'Drop 2 worst races',
scoringPatternSummary: 'Points based on finish position',
},
timingSummary: 'Every Sunday at 8 PM',
},
{
id: 'league-2',
name: 'League without Category',
description: 'A league without category',
ownerId: 'owner-2',
createdAt: '2024-01-02T00:00:00Z',
settings: {
maxDrivers: 16,
qualifyingFormat: 'Solo',
},
usedSlots: 8,
scoring: {
gameId: 'game-2',
gameName: 'Test Game 2',
primaryChampionshipType: 'Solo',
scoringPresetId: 'preset-2',
scoringPresetName: 'Advanced',
dropPolicySummary: 'Drop 1 worst race',
scoringPatternSummary: 'Points based on finish position with bonuses',
},
timingSummary: 'Every Saturday at 7 PM',
},
];
const apiDto: AllLeaguesWithCapacityAndScoringDTO = {
leagues: leaguesWithNullCategory,
totalCount: 2,
};
const result = LeaguesViewDataBuilder.build(apiDto);
// Verify that null category is handled correctly
expect(result.leagues[0].category).toBe('Road');
expect(result.leagues[1].category).toBe(null);
// Verify that leagues can be filtered by category (null category should be filterable)
const roadLeagues = result.leagues.filter(l => l.category === 'Road');
expect(roadLeagues).toHaveLength(1);
expect(roadLeagues[0].id).toBe('league-1');
const noCategoryLeagues = result.leagues.filter(l => l.category === null);
expect(noCategoryLeagues).toHaveLength(1);
expect(noCategoryLeagues[0].id).toBe('league-2');
});
});
});