view models
This commit is contained in:
@@ -1,212 +1,132 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { RaceCardViewModel, RacesPageViewModel } from './RacesPageViewModel';
|
||||
import { RacesPageViewModel } from './RacesPageViewModel';
|
||||
import { RaceListItemViewModel } from './RaceListItemViewModel';
|
||||
|
||||
describe('RaceCardViewModel', () => {
|
||||
it('should create instance with all properties', () => {
|
||||
const dto = {
|
||||
id: 'race-123',
|
||||
title: 'Season Finale',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
};
|
||||
describe('RaceListItemViewModel', () => {
|
||||
const baseDto = {
|
||||
id: 'race-123',
|
||||
track: 'Spa-Francorchamps',
|
||||
car: 'GT3',
|
||||
scheduledAt: '2025-01-01T20:00:00Z',
|
||||
status: 'scheduled',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'Test League',
|
||||
strengthOfField: 2500,
|
||||
isUpcoming: true,
|
||||
isLive: false,
|
||||
isPast: false,
|
||||
};
|
||||
|
||||
const viewModel = new RaceCardViewModel(dto);
|
||||
it('maps DTO fields to properties', () => {
|
||||
const viewModel = new RaceListItemViewModel(baseDto);
|
||||
|
||||
expect(viewModel.id).toBe('race-123');
|
||||
expect(viewModel.title).toBe('Season Finale');
|
||||
expect(viewModel.scheduledTime).toBe('2023-12-31T20:00:00Z');
|
||||
expect(viewModel.status).toBe('upcoming');
|
||||
expect(viewModel.track).toBe('Spa-Francorchamps');
|
||||
expect(viewModel.car).toBe('GT3');
|
||||
expect(viewModel.scheduledAt).toBe('2025-01-01T20:00:00Z');
|
||||
expect(viewModel.status).toBe('scheduled');
|
||||
expect(viewModel.leagueId).toBe('league-1');
|
||||
expect(viewModel.leagueName).toBe('Test League');
|
||||
expect(viewModel.strengthOfField).toBe(2500);
|
||||
expect(viewModel.isUpcoming).toBe(true);
|
||||
expect(viewModel.isLive).toBe(false);
|
||||
expect(viewModel.isPast).toBe(false);
|
||||
});
|
||||
|
||||
it('should format scheduled time as locale string', () => {
|
||||
const dto = {
|
||||
id: 'race-123',
|
||||
title: 'Test Race',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
};
|
||||
it('formats scheduled time as locale string', () => {
|
||||
const viewModel = new RaceListItemViewModel(baseDto);
|
||||
|
||||
const viewModel = new RaceCardViewModel(dto);
|
||||
const formatted = viewModel.formattedScheduledTime;
|
||||
|
||||
expect(formatted).toContain('2023');
|
||||
expect(formatted).toContain('12/31');
|
||||
expect(formatted).toContain('2025');
|
||||
});
|
||||
|
||||
it('should handle different race statuses', () => {
|
||||
const statuses = ['upcoming', 'live', 'finished', 'cancelled'];
|
||||
it('computes status badge variants', () => {
|
||||
const scheduled = new RaceListItemViewModel({ ...baseDto, status: 'scheduled' });
|
||||
const running = new RaceListItemViewModel({ ...baseDto, status: 'running' });
|
||||
const completed = new RaceListItemViewModel({ ...baseDto, status: 'completed' });
|
||||
const cancelled = new RaceListItemViewModel({ ...baseDto, status: 'cancelled' });
|
||||
const other = new RaceListItemViewModel({ ...baseDto, status: 'unknown' });
|
||||
|
||||
statuses.forEach(status => {
|
||||
const dto = {
|
||||
id: 'race-123',
|
||||
title: 'Test Race',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status,
|
||||
};
|
||||
|
||||
const viewModel = new RaceCardViewModel(dto);
|
||||
|
||||
expect(viewModel.status).toBe(status);
|
||||
});
|
||||
expect(scheduled.statusBadgeVariant).toBe('info');
|
||||
expect(running.statusBadgeVariant).toBe('success');
|
||||
expect(completed.statusBadgeVariant).toBe('secondary');
|
||||
expect(cancelled.statusBadgeVariant).toBe('danger');
|
||||
expect(other.statusBadgeVariant).toBe('default');
|
||||
});
|
||||
});
|
||||
|
||||
describe('RacesPageViewModel', () => {
|
||||
it('should create instance with upcoming and completed races', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [
|
||||
{
|
||||
id: 'race-1',
|
||||
title: 'Race 1',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
title: 'Race 2',
|
||||
scheduledTime: '2024-01-01T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
],
|
||||
completedRaces: [
|
||||
{
|
||||
id: 'race-3',
|
||||
title: 'Race 3',
|
||||
scheduledTime: '2023-12-20T20:00:00Z',
|
||||
status: 'finished',
|
||||
},
|
||||
],
|
||||
totalCount: 3,
|
||||
};
|
||||
const createDto = () => ({
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
track: 'Spa',
|
||||
car: 'GT3',
|
||||
scheduledAt: '2025-01-01T20:00:00Z',
|
||||
status: 'scheduled',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'League 1',
|
||||
strengthOfField: 2500,
|
||||
isUpcoming: true,
|
||||
isLive: false,
|
||||
isPast: false,
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
track: 'Nürburgring',
|
||||
car: 'GT4',
|
||||
scheduledAt: '2025-01-02T20:00:00Z',
|
||||
status: 'running',
|
||||
leagueId: 'league-2',
|
||||
leagueName: 'League 2',
|
||||
strengthOfField: null,
|
||||
isUpcoming: true,
|
||||
isLive: true,
|
||||
isPast: false,
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
track: 'Daytona',
|
||||
car: 'LMP2',
|
||||
scheduledAt: '2024-12-31T20:00:00Z',
|
||||
status: 'completed',
|
||||
leagueId: 'league-3',
|
||||
leagueName: 'League 3',
|
||||
strengthOfField: 2000,
|
||||
isUpcoming: false,
|
||||
isLive: false,
|
||||
isPast: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
it('wraps races DTOs in RaceListItemViewModel instances', () => {
|
||||
const viewModel = new RacesPageViewModel(createDto());
|
||||
|
||||
expect(viewModel.races).toHaveLength(3);
|
||||
expect(viewModel.races[0]).toBeInstanceOf(RaceListItemViewModel);
|
||||
});
|
||||
|
||||
it('computes total count from races length', () => {
|
||||
const viewModel = new RacesPageViewModel(createDto());
|
||||
|
||||
expect(viewModel.totalCount).toBe(3);
|
||||
});
|
||||
|
||||
it('filters upcoming, live and past races', () => {
|
||||
const viewModel = new RacesPageViewModel(createDto());
|
||||
|
||||
expect(viewModel.upcomingRaces).toHaveLength(2);
|
||||
expect(viewModel.liveRaces).toHaveLength(1);
|
||||
expect(viewModel.pastRaces).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('filters scheduled, running and completed races by status', () => {
|
||||
const viewModel = new RacesPageViewModel(createDto());
|
||||
|
||||
expect(viewModel.scheduledRaces).toHaveLength(1);
|
||||
expect(viewModel.runningRaces).toHaveLength(1);
|
||||
expect(viewModel.completedRaces).toHaveLength(1);
|
||||
expect(viewModel.totalCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should convert DTOs to view models', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [
|
||||
{
|
||||
id: 'race-1',
|
||||
title: 'Race 1',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
],
|
||||
completedRaces: [],
|
||||
totalCount: 1,
|
||||
};
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
|
||||
expect(viewModel.upcomingRaces[0]).toBeInstanceOf(RaceCardViewModel);
|
||||
expect(viewModel.upcomingRaces[0].id).toBe('race-1');
|
||||
});
|
||||
|
||||
it('should return correct upcoming count', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [
|
||||
{
|
||||
id: 'race-1',
|
||||
title: 'Race 1',
|
||||
scheduledTime: '2023-12-31T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
title: 'Race 2',
|
||||
scheduledTime: '2024-01-01T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
title: 'Race 3',
|
||||
scheduledTime: '2024-01-02T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
],
|
||||
completedRaces: [],
|
||||
totalCount: 3,
|
||||
};
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
|
||||
expect(viewModel.upcomingCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should return correct completed count', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [],
|
||||
completedRaces: [
|
||||
{
|
||||
id: 'race-1',
|
||||
title: 'Race 1',
|
||||
scheduledTime: '2023-12-20T20:00:00Z',
|
||||
status: 'finished',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
title: 'Race 2',
|
||||
scheduledTime: '2023-12-21T20:00:00Z',
|
||||
status: 'finished',
|
||||
},
|
||||
],
|
||||
totalCount: 2,
|
||||
};
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
|
||||
expect(viewModel.completedCount).toBe(2);
|
||||
});
|
||||
|
||||
it('should handle empty race lists', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [],
|
||||
completedRaces: [],
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
|
||||
expect(viewModel.upcomingCount).toBe(0);
|
||||
expect(viewModel.completedCount).toBe(0);
|
||||
expect(viewModel.totalCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle mixed race lists', () => {
|
||||
const dto = {
|
||||
upcomingRaces: [
|
||||
{
|
||||
id: 'race-1',
|
||||
title: 'Upcoming',
|
||||
scheduledTime: '2024-01-01T20:00:00Z',
|
||||
status: 'upcoming',
|
||||
},
|
||||
],
|
||||
completedRaces: [
|
||||
{
|
||||
id: 'race-2',
|
||||
title: 'Completed 1',
|
||||
scheduledTime: '2023-12-20T20:00:00Z',
|
||||
status: 'finished',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
title: 'Completed 2',
|
||||
scheduledTime: '2023-12-21T20:00:00Z',
|
||||
status: 'finished',
|
||||
},
|
||||
],
|
||||
totalCount: 3,
|
||||
};
|
||||
|
||||
const viewModel = new RacesPageViewModel(dto);
|
||||
|
||||
expect(viewModel.upcomingCount).toBe(1);
|
||||
expect(viewModel.completedCount).toBe(2);
|
||||
expect(viewModel.totalCount).toBe(3);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user