Files
gridpilot.gg/apps/website/lib/view-models/RaceDetailViewModel.test.ts
2026-01-12 01:01:49 +01:00

309 lines
9.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { RaceDetailLeagueDTO } from '@/lib/types/generated/RaceDetailLeagueDTO';
import type { RaceDetailRaceDTO } from '@/lib/types/generated/RaceDetailRaceDTO';
import type { RaceDetailRegistrationDTO } from '@/lib/types/generated/RaceDetailRegistrationDTO';
import type { RaceDetailUserResultDTO } from '@/lib/types/generated/RaceDetailUserResultDTO';
import type { RaceDetailEntryDTO } from '@/lib/types/RaceDetailEntryDTO';
import { RaceDetailViewModel } from './RaceDetailViewModel';
describe('RaceDetailViewModel', () => {
const createMockRace = (overrides?: Partial<RaceDetailRaceDTO>): RaceDetailRaceDTO => ({
id: 'race-123',
title: 'Test Race',
scheduledAt: '2023-12-31T20:00:00Z',
status: 'upcoming',
...overrides,
});
const createMockLeague = (): RaceDetailLeagueDTO => ({
id: 'league-123',
name: 'Test League',
});
const createMockRegistration = (
overrides?: Partial<RaceDetailRegistrationDTO>
): RaceDetailRegistrationDTO => ({
isRegistered: false,
canRegister: true,
...overrides,
});
it('should create instance with all properties', () => {
const race = createMockRace();
const league = createMockLeague();
const entries: RaceDetailEntryDTO[] = [];
const registration = createMockRegistration();
const userResult: RaceDetailUserResultDTO | null = null;
const viewModel = new RaceDetailViewModel({
race,
league,
entryList: entries,
registration,
userResult,
}, 'current-driver');
expect(viewModel.race).toBe(race);
expect(viewModel.league).toBe(league);
expect(viewModel.entryList).toHaveLength(0);
expect(viewModel.registration).toBe(registration);
expect(viewModel.userResult).toBe(userResult);
});
it('should handle null race and league', () => {
const viewModel = new RaceDetailViewModel({
race: null,
league: null,
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.race).toBeNull();
expect(viewModel.league).toBeNull();
});
it('should return correct isRegistered value', () => {
const registeredVm = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ isRegistered: true }),
userResult: null,
});
const notRegisteredVm = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ isRegistered: false }),
userResult: null,
});
expect(registeredVm.isRegistered).toBe(true);
expect(notRegisteredVm.isRegistered).toBe(false);
});
it('should return correct canRegister value', () => {
const canRegisterVm = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ canRegister: true }),
userResult: null,
});
const cannotRegisterVm = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ canRegister: false }),
userResult: null,
});
expect(canRegisterVm.canRegister).toBe(true);
expect(cannotRegisterVm.canRegister).toBe(false);
});
it('should format race status correctly', () => {
const upcomingVm = new RaceDetailViewModel({
race: createMockRace({ status: 'upcoming' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
const liveVm = new RaceDetailViewModel({
race: createMockRace({ status: 'live' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
const finishedVm = new RaceDetailViewModel({
race: createMockRace({ status: 'finished' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(upcomingVm.raceStatusDisplay).toBe('Upcoming');
expect(liveVm.raceStatusDisplay).toBe('Live');
expect(finishedVm.raceStatusDisplay).toBe('Finished');
});
it('should return Unknown for status when race is null', () => {
const viewModel = new RaceDetailViewModel({
race: null,
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.raceStatusDisplay).toBe('Unknown');
});
it('should format scheduled time correctly', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace({ scheduledAt: '2023-12-31T20:00:00Z' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
const formatted = viewModel.formattedScheduledTime;
expect(formatted).toContain('2023');
expect(formatted).toContain('12/31');
});
it('should return empty string for formatted time when race is null', () => {
const viewModel = new RaceDetailViewModel({
race: null,
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.formattedScheduledTime).toBe('');
});
it('should return correct entry count', () => {
const entries: RaceDetailEntryDTO[] = [
{ driverId: 'driver-1', carId: 'car-1' },
{ driverId: 'driver-2', carId: 'car-2' },
{ driverId: 'driver-3', carId: 'car-3' },
] as RaceDetailEntryDTO[];
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: entries,
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.entryCount).toBe(3);
});
it('should return true for hasResults when userResult exists', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: { position: 1, lapTime: 90.5 } as RaceDetailUserResultDTO,
});
expect(viewModel.hasResults).toBe(true);
});
it('should return false for hasResults when userResult is null', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.hasResults).toBe(false);
});
it('should return correct registration status message when registered', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ isRegistered: true }),
userResult: null,
});
expect(viewModel.registrationStatusMessage).toBe('You are registered for this race');
});
it('should return correct registration status message when can register', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ isRegistered: false, canRegister: true }),
userResult: null,
});
expect(viewModel.registrationStatusMessage).toBe('You can register for this race');
});
it('should return correct registration status message when cannot register', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration({ isRegistered: false, canRegister: false }),
userResult: null,
});
expect(viewModel.registrationStatusMessage).toBe('Registration not available');
});
it('should expose canReopenRace for completed and cancelled statuses', () => {
const completedVm = new RaceDetailViewModel({
race: createMockRace({ status: 'completed' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
const cancelledVm = new RaceDetailViewModel({
race: createMockRace({ status: 'cancelled' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
const upcomingVm = new RaceDetailViewModel({
race: createMockRace({ status: 'upcoming' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(completedVm.canReopenRace).toBe(true);
expect(cancelledVm.canReopenRace).toBe(true);
expect(upcomingVm.canReopenRace).toBe(false);
});
it('should handle error property', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace(),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
error: 'Failed to load race details',
});
expect(viewModel.error).toBe('Failed to load race details');
});
it('should handle custom race status', () => {
const viewModel = new RaceDetailViewModel({
race: createMockRace({ status: 'cancelled' }),
league: createMockLeague(),
entryList: [],
registration: createMockRegistration(),
userResult: null,
});
expect(viewModel.raceStatusDisplay).toBe('cancelled');
});
});