406 lines
11 KiB
TypeScript
406 lines
11 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|
|
});
|