add tests
This commit is contained in:
213
apps/website/components/achievements/AchievementCard.test.tsx
Normal file
213
apps/website/components/achievements/AchievementCard.test.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { AchievementCard } from './AchievementCard';
|
||||
|
||||
// Mock the DateDisplay module
|
||||
vi.mock('@/lib/display-objects/DateDisplay', () => ({
|
||||
DateDisplay: {
|
||||
formatShort: vi.fn((date) => `Formatted: ${date}`),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('AchievementCard', () => {
|
||||
const mockProps = {
|
||||
title: 'First Victory',
|
||||
description: 'Win your first race',
|
||||
icon: '🏆',
|
||||
unlockedAt: '2024-01-15T10:30:00Z',
|
||||
rarity: 'common' as const,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('renders all achievement information correctly', () => {
|
||||
render(<AchievementCard {...mockProps} />);
|
||||
|
||||
expect(screen.getByText('🏆')).toBeDefined();
|
||||
expect(screen.getByText('First Victory')).toBeDefined();
|
||||
expect(screen.getByText('Win your first race')).toBeDefined();
|
||||
expect(screen.getByText('Formatted: 2024-01-15T10:30:00Z')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with different rarity variants', () => {
|
||||
const rarities = ['common', 'rare', 'epic', 'legendary'] as const;
|
||||
|
||||
rarities.forEach((rarity) => {
|
||||
const { container } = render(
|
||||
<AchievementCard {...mockProps} rarity={rarity} />
|
||||
);
|
||||
|
||||
// The Card component should receive the correct variant
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with different icons', () => {
|
||||
const icons = ['🏆', '🥇', '⭐', '💎', '🎯'];
|
||||
|
||||
icons.forEach((icon) => {
|
||||
render(<AchievementCard {...mockProps} icon={icon} />);
|
||||
expect(screen.getByText(icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with long description', () => {
|
||||
const longDescription = 'This is a very long description that spans multiple lines and contains detailed information about the achievement and its requirements';
|
||||
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
description={longDescription}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(longDescription)).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with special characters in title', () => {
|
||||
const specialTitle = 'Champion\'s Trophy #1!';
|
||||
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
title={specialTitle}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(specialTitle)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date formatting', () => {
|
||||
it('calls DateDisplay.formatShort with the correct date', () => {
|
||||
const { DateDisplay } = require('@/lib/display-objects/DateDisplay');
|
||||
|
||||
render(<AchievementCard {...mockProps} />);
|
||||
|
||||
expect(DateDisplay.formatShort).toHaveBeenCalledWith('2024-01-15T10:30:00Z');
|
||||
});
|
||||
|
||||
it('handles different date formats', () => {
|
||||
const { DateDisplay } = require('@/lib/display-objects/DateDisplay');
|
||||
|
||||
const differentDates = [
|
||||
'2024-01-15T10:30:00Z',
|
||||
'2024-12-31T23:59:59Z',
|
||||
'2023-06-15T08:00:00Z',
|
||||
];
|
||||
|
||||
differentDates.forEach((date) => {
|
||||
render(<AchievementCard {...mockProps} unlockedAt={date} />);
|
||||
expect(DateDisplay.formatShort).toHaveBeenCalledWith(date);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rarity styling', () => {
|
||||
it('applies correct variant for common rarity', () => {
|
||||
const { container } = render(
|
||||
<AchievementCard {...mockProps} rarity="common" />
|
||||
);
|
||||
|
||||
// The Card component should receive variant="rarity-common"
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct variant for rare rarity', () => {
|
||||
const { container } = render(
|
||||
<AchievementCard {...mockProps} rarity="rare" />
|
||||
);
|
||||
|
||||
// The Card component should receive variant="rarity-rare"
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct variant for epic rarity', () => {
|
||||
const { container } = render(
|
||||
<AchievementCard {...mockProps} rarity="epic" />
|
||||
);
|
||||
|
||||
// The Card component should receive variant="rarity-epic"
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct variant for legendary rarity', () => {
|
||||
const { container } = render(
|
||||
<AchievementCard {...mockProps} rarity="legendary" />
|
||||
);
|
||||
|
||||
// The Card component should receive variant="rarity-legendary"
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty states', () => {
|
||||
it('renders with empty description', () => {
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
description=""
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('First Victory')).toBeDefined();
|
||||
expect(screen.getByText('Formatted: 2024-01-15T10:30:00Z')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with empty icon', () => {
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
icon=""
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('First Victory')).toBeDefined();
|
||||
expect(screen.getByText('Win your first race')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('handles very long title', () => {
|
||||
const longTitle = 'This is an extremely long achievement title that should still be displayed correctly without breaking the layout';
|
||||
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
title={longTitle}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(longTitle)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles unicode characters in icon', () => {
|
||||
const unicodeIcon = '🌟';
|
||||
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
icon={unicodeIcon}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(unicodeIcon)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles emoji in icon', () => {
|
||||
const emojiIcon = '🎮';
|
||||
|
||||
render(
|
||||
<AchievementCard
|
||||
{...mockProps}
|
||||
icon={emojiIcon}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(emojiIcon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
396
apps/website/components/achievements/AchievementGrid.test.tsx
Normal file
396
apps/website/components/achievements/AchievementGrid.test.tsx
Normal file
@@ -0,0 +1,396 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { AchievementGrid } from './AchievementGrid';
|
||||
|
||||
// Mock the AchievementDisplay module
|
||||
vi.mock('@/lib/display-objects/AchievementDisplay', () => ({
|
||||
AchievementDisplay: {
|
||||
getRarityVariant: vi.fn((rarity) => {
|
||||
const rarityMap = {
|
||||
common: { text: 'low', surface: 'rarity-common', iconIntent: 'low' },
|
||||
rare: { text: 'primary', surface: 'rarity-rare', iconIntent: 'primary' },
|
||||
epic: { text: 'primary', surface: 'rarity-epic', iconIntent: 'primary' },
|
||||
legendary: { text: 'warning', surface: 'rarity-legendary', iconIntent: 'warning' },
|
||||
};
|
||||
return rarityMap[rarity as keyof typeof rarityMap] || rarityMap.common;
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('AchievementGrid', () => {
|
||||
const mockAchievements = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'First Victory',
|
||||
description: 'Win your first race',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Speed Demon',
|
||||
description: 'Reach 200 mph',
|
||||
icon: 'zap',
|
||||
rarity: 'rare',
|
||||
earnedAtLabel: 'Feb 20, 2024',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Champion',
|
||||
description: 'Win 10 races',
|
||||
icon: 'crown',
|
||||
rarity: 'epic',
|
||||
earnedAtLabel: 'Mar 10, 2024',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Legend',
|
||||
description: 'Win 100 races',
|
||||
icon: 'star',
|
||||
rarity: 'legendary',
|
||||
earnedAtLabel: 'Apr 5, 2024',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('renders the header with correct title', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
expect(screen.getByText('Achievements')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders the correct count of achievements', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
expect(screen.getByText('4 earned')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders all achievement items', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
mockAchievements.forEach((achievement) => {
|
||||
expect(screen.getByText(achievement.title)).toBeDefined();
|
||||
expect(screen.getByText(achievement.description)).toBeDefined();
|
||||
expect(screen.getByText(achievement.earnedAtLabel)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders achievement icons correctly', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
// Check that the icon mapping works
|
||||
expect(screen.getByText('First Victory')).toBeDefined();
|
||||
expect(screen.getByText('Speed Demon')).toBeDefined();
|
||||
expect(screen.getByText('Champion')).toBeDefined();
|
||||
expect(screen.getByText('Legend')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders achievement rarities correctly', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
expect(screen.getByText('common')).toBeDefined();
|
||||
expect(screen.getByText('rare')).toBeDefined();
|
||||
expect(screen.getByText('epic')).toBeDefined();
|
||||
expect(screen.getByText('legendary')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty states', () => {
|
||||
it('renders with empty achievements array', () => {
|
||||
render(<AchievementGrid achievements={[]} />);
|
||||
|
||||
expect(screen.getByText('Achievements')).toBeDefined();
|
||||
expect(screen.getByText('0 earned')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with single achievement', () => {
|
||||
const singleAchievement = [mockAchievements[0]];
|
||||
|
||||
render(<AchievementGrid achievements={singleAchievement} />);
|
||||
|
||||
expect(screen.getByText('Achievements')).toBeDefined();
|
||||
expect(screen.getByText('1 earned')).toBeDefined();
|
||||
expect(screen.getByText('First Victory')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon mapping', () => {
|
||||
it('maps trophy icon correctly', () => {
|
||||
const trophyAchievement = {
|
||||
id: '1',
|
||||
title: 'Trophy Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[trophyAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Trophy Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maps medal icon correctly', () => {
|
||||
const medalAchievement = {
|
||||
id: '2',
|
||||
title: 'Medal Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'medal',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[medalAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Medal Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maps star icon correctly', () => {
|
||||
const starAchievement = {
|
||||
id: '3',
|
||||
title: 'Star Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'star',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[starAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Star Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maps crown icon correctly', () => {
|
||||
const crownAchievement = {
|
||||
id: '4',
|
||||
title: 'Crown Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'crown',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[crownAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Crown Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maps target icon correctly', () => {
|
||||
const targetAchievement = {
|
||||
id: '5',
|
||||
title: 'Target Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'target',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[targetAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Target Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maps zap icon correctly', () => {
|
||||
const zapAchievement = {
|
||||
id: '6',
|
||||
title: 'Zap Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'zap',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[zapAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Zap Achievement')).toBeDefined();
|
||||
});
|
||||
|
||||
it('defaults to award icon for unknown icon', () => {
|
||||
const unknownIconAchievement = {
|
||||
id: '7',
|
||||
title: 'Unknown Icon Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'unknown',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[unknownIconAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Unknown Icon Achievement')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rarity display', () => {
|
||||
it('applies correct rarity variant for common', () => {
|
||||
const commonAchievement = {
|
||||
id: '1',
|
||||
title: 'Common Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[commonAchievement]} />);
|
||||
|
||||
expect(screen.getByText('common')).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct rarity variant for rare', () => {
|
||||
const rareAchievement = {
|
||||
id: '2',
|
||||
title: 'Rare Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'rare',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[rareAchievement]} />);
|
||||
|
||||
expect(screen.getByText('rare')).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct rarity variant for epic', () => {
|
||||
const epicAchievement = {
|
||||
id: '3',
|
||||
title: 'Epic Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'epic',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[epicAchievement]} />);
|
||||
|
||||
expect(screen.getByText('epic')).toBeDefined();
|
||||
});
|
||||
|
||||
it('applies correct rarity variant for legendary', () => {
|
||||
const legendaryAchievement = {
|
||||
id: '4',
|
||||
title: 'Legendary Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'legendary',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[legendaryAchievement]} />);
|
||||
|
||||
expect(screen.getByText('legendary')).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles unknown rarity gracefully', () => {
|
||||
const unknownRarityAchievement = {
|
||||
id: '5',
|
||||
title: 'Unknown Rarity Achievement',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'unknown',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[unknownRarityAchievement]} />);
|
||||
|
||||
expect(screen.getByText('unknown')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multiple achievements', () => {
|
||||
it('renders multiple achievements with different rarities', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
// Check all titles are rendered
|
||||
mockAchievements.forEach((achievement) => {
|
||||
expect(screen.getByText(achievement.title)).toBeDefined();
|
||||
});
|
||||
|
||||
// Check all descriptions are rendered
|
||||
mockAchievements.forEach((achievement) => {
|
||||
expect(screen.getByText(achievement.description)).toBeDefined();
|
||||
});
|
||||
|
||||
// Check all earned labels are rendered
|
||||
mockAchievements.forEach((achievement) => {
|
||||
expect(screen.getByText(achievement.earnedAtLabel)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders achievements in order', () => {
|
||||
render(<AchievementGrid achievements={mockAchievements} />);
|
||||
|
||||
// The component should render achievements in the order they are provided
|
||||
const titles = screen.getAllByText(/Achievement|Victory|Demon|Champion|Legend/);
|
||||
expect(titles.length).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('handles achievements with long titles', () => {
|
||||
const longTitleAchievement = {
|
||||
id: '1',
|
||||
title: 'This is an extremely long achievement title that should still be displayed correctly without breaking the layout',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[longTitleAchievement]} />);
|
||||
|
||||
expect(screen.getByText(longTitleAchievement.title)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles achievements with long descriptions', () => {
|
||||
const longDescriptionAchievement = {
|
||||
id: '1',
|
||||
title: 'Achievement',
|
||||
description: 'This is a very long description that spans multiple lines and contains detailed information about the achievement and its requirements',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[longDescriptionAchievement]} />);
|
||||
|
||||
expect(screen.getByText(longDescriptionAchievement.description)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles achievements with special characters in title', () => {
|
||||
const specialTitleAchievement = {
|
||||
id: '1',
|
||||
title: 'Champion\'s Trophy #1!',
|
||||
description: 'Test description',
|
||||
icon: 'trophy',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[specialTitleAchievement]} />);
|
||||
|
||||
expect(screen.getByText(specialTitleAchievement.title)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles achievements with unicode characters in icon', () => {
|
||||
const unicodeIconAchievement = {
|
||||
id: '1',
|
||||
title: 'Unicode Achievement',
|
||||
description: 'Test description',
|
||||
icon: '🌟',
|
||||
rarity: 'common',
|
||||
earnedAtLabel: 'Jan 15, 2024',
|
||||
};
|
||||
|
||||
render(<AchievementGrid achievements={[unicodeIconAchievement]} />);
|
||||
|
||||
expect(screen.getByText('Unicode Achievement')).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
405
apps/website/components/achievements/MilestoneItem.test.tsx
Normal file
405
apps/website/components/achievements/MilestoneItem.test.tsx
Normal file
@@ -0,0 +1,405 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { MilestoneItem } from './MilestoneItem';
|
||||
|
||||
describe('MilestoneItem', () => {
|
||||
const mockProps = {
|
||||
label: 'Total Races',
|
||||
value: '150',
|
||||
icon: '🏁',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Clear any previous renders
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('renders all milestone information correctly', () => {
|
||||
render(<MilestoneItem {...mockProps} />);
|
||||
|
||||
expect(screen.getByText('🏁')).toBeDefined();
|
||||
expect(screen.getByText('Total Races')).toBeDefined();
|
||||
expect(screen.getByText('150')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with different icons', () => {
|
||||
const icons = ['🏁', '🏆', '⭐', '💎', '🎯', '⏱️'];
|
||||
|
||||
icons.forEach((icon) => {
|
||||
render(<MilestoneItem {...mockProps} icon={icon} />);
|
||||
expect(screen.getByText(icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with different labels', () => {
|
||||
const labels = [
|
||||
'Total Races',
|
||||
'Wins',
|
||||
'Podiums',
|
||||
'Laps Completed',
|
||||
'Distance Traveled',
|
||||
'Time Spent',
|
||||
];
|
||||
|
||||
labels.forEach((label) => {
|
||||
render(<MilestoneItem {...mockProps} label={label} />);
|
||||
expect(screen.getByText(label)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with different values', () => {
|
||||
const values = ['0', '1', '10', '100', '1000', '10000', '999999'];
|
||||
|
||||
values.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with long label', () => {
|
||||
const longLabel = 'Total Distance Traveled in All Races Combined';
|
||||
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
label={longLabel}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(longLabel)).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with long value', () => {
|
||||
const longValue = '12,345,678';
|
||||
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value={longValue}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(longValue)).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with special characters in label', () => {
|
||||
const specialLabel = 'Races Won (2024)';
|
||||
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
label={specialLabel}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(specialLabel)).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with special characters in value', () => {
|
||||
const specialValue = '1,234.56';
|
||||
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value={specialValue}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(specialValue)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty states', () => {
|
||||
it('renders with empty label', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
label=""
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('🏁')).toBeDefined();
|
||||
expect(screen.getByText('150')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with empty value', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('🏁')).toBeDefined();
|
||||
expect(screen.getByText('Total Races')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with empty icon', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
icon=""
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Total Races')).toBeDefined();
|
||||
expect(screen.getByText('150')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders with all empty values', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
label=""
|
||||
value=""
|
||||
icon=""
|
||||
/>
|
||||
);
|
||||
|
||||
// Should still render the card structure
|
||||
expect(document.body.textContent).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Icon variations', () => {
|
||||
it('renders with emoji icons', () => {
|
||||
const emojiIcons = ['🏁', '🏆', '⭐', '💎', '🎯', '⏱️', '🎮', '⚡'];
|
||||
|
||||
emojiIcons.forEach((icon) => {
|
||||
render(<MilestoneItem {...mockProps} icon={icon} />);
|
||||
expect(screen.getByText(icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with unicode characters', () => {
|
||||
const unicodeIcons = ['★', '☆', '♦', '♥', '♠', '♣'];
|
||||
|
||||
unicodeIcons.forEach((icon) => {
|
||||
render(<MilestoneItem {...mockProps} icon={icon} />);
|
||||
expect(screen.getByText(icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders with text icons', () => {
|
||||
const textIcons = ['A', 'B', 'C', '1', '2', '3', '!', '@', '#'];
|
||||
|
||||
textIcons.forEach((icon) => {
|
||||
render(<MilestoneItem {...mockProps} icon={icon} />);
|
||||
expect(screen.getByText(icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Value formatting', () => {
|
||||
it('renders numeric values', () => {
|
||||
const numericValues = ['0', '1', '10', '100', '1000', '10000'];
|
||||
|
||||
numericValues.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders formatted numbers', () => {
|
||||
const formattedValues = ['1,000', '10,000', '100,000', '1,000,000'];
|
||||
|
||||
formattedValues.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders decimal values', () => {
|
||||
const decimalValues = ['0.0', '1.5', '10.25', '100.99'];
|
||||
|
||||
decimalValues.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders percentage values', () => {
|
||||
const percentageValues = ['0%', '50%', '100%', '150%'];
|
||||
|
||||
percentageValues.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders time values', () => {
|
||||
const timeValues = ['0:00', '1:30', '10:45', '1:23:45'];
|
||||
|
||||
timeValues.forEach((value) => {
|
||||
render(<MilestoneItem {...mockProps} value={value} />);
|
||||
expect(screen.getByText(value)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Label variations', () => {
|
||||
it('renders single word labels', () => {
|
||||
const singleWordLabels = ['Races', 'Wins', 'Losses', 'Time', 'Distance'];
|
||||
|
||||
singleWordLabels.forEach((label) => {
|
||||
render(<MilestoneItem {...mockProps} label={label} />);
|
||||
expect(screen.getByText(label)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders multi-word labels', () => {
|
||||
const multiWordLabels = [
|
||||
'Total Races',
|
||||
'Race Wins',
|
||||
'Podium Finishes',
|
||||
'Laps Completed',
|
||||
'Distance Traveled',
|
||||
];
|
||||
|
||||
multiWordLabels.forEach((label) => {
|
||||
render(<MilestoneItem {...mockProps} label={label} />);
|
||||
expect(screen.getByText(label)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders labels with parentheses', () => {
|
||||
const parentheticalLabels = [
|
||||
'Races (All)',
|
||||
'Wins (Ranked)',
|
||||
'Time (Active)',
|
||||
'Distance (Total)',
|
||||
];
|
||||
|
||||
parentheticalLabels.forEach((label) => {
|
||||
render(<MilestoneItem {...mockProps} label={label} />);
|
||||
expect(screen.getByText(label)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders labels with numbers', () => {
|
||||
const numberedLabels = [
|
||||
'Races 2024',
|
||||
'Wins 2023',
|
||||
'Season 1',
|
||||
'Group A',
|
||||
];
|
||||
|
||||
numberedLabels.forEach((label) => {
|
||||
render(<MilestoneItem {...mockProps} label={label} />);
|
||||
expect(screen.getByText(label)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('handles very long label and value', () => {
|
||||
const longLabel = 'This is an extremely long milestone label that should still be displayed correctly without breaking the layout';
|
||||
const longValue = '999,999,999,999,999,999,999,999,999';
|
||||
|
||||
render(
|
||||
<MilestoneItem
|
||||
icon="🏁"
|
||||
label={longLabel}
|
||||
value={longValue}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(longLabel)).toBeDefined();
|
||||
expect(screen.getByText(longValue)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles special characters in all fields', () => {
|
||||
const specialProps = {
|
||||
label: 'Races Won (2024) #1!',
|
||||
value: '1,234.56',
|
||||
icon: '🏆',
|
||||
};
|
||||
|
||||
render(<MilestoneItem {...specialProps} />);
|
||||
|
||||
expect(screen.getByText(specialProps.label)).toBeDefined();
|
||||
expect(screen.getByText(specialProps.value)).toBeDefined();
|
||||
expect(screen.getByText(specialProps.icon)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles unicode in all fields', () => {
|
||||
const unicodeProps = {
|
||||
label: '★ Star Races ★',
|
||||
value: '★ 100 ★',
|
||||
icon: '★',
|
||||
};
|
||||
|
||||
render(<MilestoneItem {...unicodeProps} />);
|
||||
|
||||
expect(screen.getByText(unicodeProps.label)).toBeDefined();
|
||||
expect(screen.getByText(unicodeProps.value)).toBeDefined();
|
||||
expect(screen.getByText(unicodeProps.icon)).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles zero value', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value="0"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('0')).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles negative value', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value="-5"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-5')).toBeDefined();
|
||||
});
|
||||
|
||||
it('handles scientific notation', () => {
|
||||
render(
|
||||
<MilestoneItem
|
||||
{...mockProps}
|
||||
value="1.5e6"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('1.5e6')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Layout structure', () => {
|
||||
it('renders with correct visual hierarchy', () => {
|
||||
const { container } = render(<MilestoneItem {...mockProps} />);
|
||||
|
||||
// Check that the component renders with the expected structure
|
||||
// The component should have a Card with a Group containing icon, label, and value
|
||||
expect(container.firstChild).toBeDefined();
|
||||
|
||||
// Verify all text elements are present
|
||||
expect(screen.getByText('🏁')).toBeDefined();
|
||||
expect(screen.getByText('Total Races')).toBeDefined();
|
||||
expect(screen.getByText('150')).toBeDefined();
|
||||
});
|
||||
|
||||
it('maintains consistent structure across different props', () => {
|
||||
const testCases = [
|
||||
{ label: 'A', value: '1', icon: 'X' },
|
||||
{ label: 'Long Label', value: '1000', icon: '🏆' },
|
||||
{ label: 'Special!@#', value: '1.23', icon: '★' },
|
||||
];
|
||||
|
||||
testCases.forEach((props) => {
|
||||
const { container } = render(<MilestoneItem {...props} />);
|
||||
|
||||
// Each should render successfully
|
||||
expect(container.firstChild).toBeDefined();
|
||||
expect(screen.getByText(props.label)).toBeDefined();
|
||||
expect(screen.getByText(props.value)).toBeDefined();
|
||||
expect(screen.getByText(props.icon)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user