100 lines
3.7 KiB
TypeScript
100 lines
3.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { AvailableLeaguesViewData, AvailableLeagueViewData } from '../view-data/AvailableLeaguesViewData';
|
|
import { AvailableLeaguesViewModel, AvailableLeagueViewModel } from './AvailableLeaguesViewModel';
|
|
|
|
describe('AvailableLeaguesViewModel', () => {
|
|
const baseLeague: AvailableLeagueViewData = {
|
|
id: 'league-1',
|
|
name: 'Pro Series',
|
|
game: 'iRacing',
|
|
description: 'Competitive league for serious drivers',
|
|
drivers: 24,
|
|
avgViewsPerRace: 12_500,
|
|
formattedAvgViews: '12.5k',
|
|
mainSponsorSlot: { available: true, price: 5_000 },
|
|
secondarySlots: { available: 2, total: 3, price: 1_500 },
|
|
cpm: 400,
|
|
formattedCpm: '$400',
|
|
hasAvailableSlots: true,
|
|
rating: 4.7,
|
|
tier: 'premium' as const,
|
|
tierConfig: {
|
|
color: '#FFD700',
|
|
bgColor: '#FFF8DC',
|
|
border: '2px solid #FFD700',
|
|
icon: '⭐',
|
|
},
|
|
nextRace: 'Next Sunday',
|
|
seasonStatus: 'active' as const,
|
|
statusConfig: {
|
|
color: '#10B981',
|
|
bg: '#D1FAE5',
|
|
label: 'Active Season',
|
|
},
|
|
};
|
|
|
|
const baseViewData: AvailableLeaguesViewData = {
|
|
leagues: [baseLeague],
|
|
};
|
|
|
|
it('maps league array into view models', () => {
|
|
const vm = new AvailableLeaguesViewModel(baseViewData);
|
|
|
|
expect(vm.leagues).toHaveLength(1);
|
|
expect(vm.leagues[0]).toBeInstanceOf(AvailableLeagueViewModel);
|
|
expect(vm.leagues[0]?.id).toBe(baseLeague.id);
|
|
expect(vm.leagues[0]?.name).toBe(baseLeague.name);
|
|
expect(vm.leagues[0]?.avgViewsPerRace).toBe(baseLeague.avgViewsPerRace);
|
|
});
|
|
|
|
it('exposes formatted average views and CPM for main sponsor slot', () => {
|
|
const leagueVm = new AvailableLeagueViewModel(baseLeague);
|
|
|
|
expect(leagueVm.formattedAvgViews).toBe('12.5k');
|
|
|
|
const expectedCpm = Math.round((baseLeague.mainSponsorSlot.price / baseLeague.avgViewsPerRace) * 1000);
|
|
expect(leagueVm.cpm).toBe(expectedCpm);
|
|
expect(leagueVm.formattedCpm).toBe('$400');
|
|
});
|
|
|
|
it('detects available sponsor slots from main or secondary slots', () => {
|
|
const withBothAvailable = new AvailableLeagueViewModel(baseLeague);
|
|
expect(withBothAvailable.hasAvailableSlots).toBe(true);
|
|
|
|
const onlySecondary = new AvailableLeagueViewModel({
|
|
...baseLeague,
|
|
mainSponsorSlot: { available: false, price: 5_000 },
|
|
secondarySlots: { available: 1, total: 3, price: 1_500 },
|
|
});
|
|
expect(onlySecondary.hasAvailableSlots).toBe(true);
|
|
|
|
const noneAvailable = new AvailableLeagueViewModel({
|
|
...baseLeague,
|
|
mainSponsorSlot: { available: false, price: 5_000 },
|
|
secondarySlots: { available: 0, total: 3, price: 1_500 },
|
|
});
|
|
expect(noneAvailable.hasAvailableSlots).toBe(false);
|
|
});
|
|
|
|
it('returns tier configuration for badge styling', () => {
|
|
const premium = new AvailableLeagueViewModel({ ...baseLeague, tier: 'premium' });
|
|
const standard = new AvailableLeagueViewModel({ ...baseLeague, tier: 'standard' });
|
|
const starter = new AvailableLeagueViewModel({ ...baseLeague, tier: 'starter' });
|
|
|
|
expect(premium.tierConfig.icon).toBe('⭐');
|
|
expect(standard.tierConfig.icon).toBe('🏆');
|
|
expect(starter.tierConfig.icon).toBe('🚀');
|
|
});
|
|
|
|
it('returns status configuration for season state', () => {
|
|
const active = new AvailableLeagueViewModel({ ...baseLeague, seasonStatus: 'active' });
|
|
const upcoming = new AvailableLeagueViewModel({ ...baseLeague, seasonStatus: 'upcoming' });
|
|
const completed = new AvailableLeagueViewModel({ ...baseLeague, seasonStatus: 'completed' });
|
|
|
|
expect(active.statusConfig.label).toBe('Active Season');
|
|
expect(upcoming.statusConfig.label).toBe('Starting Soon');
|
|
expect(completed.statusConfig.label).toBe('Season Ended');
|
|
});
|
|
|
|
});
|