import { describe, it, expect } from 'vitest'; import { render, screen } from '@testing-library/react'; import { GoalProgressBar } from '@/components/goals/GoalProgressBar'; describe('GoalProgressBar', () => { describe('progress calculation', () => { it('should render correct percentage', () => { render(); expect(screen.getByText('50.0%')).toBeInTheDocument(); }); it('should calculate percentage correctly', () => { render(); expect(screen.getByText('75.0%')).toBeInTheDocument(); }); it('should cap percentage at 100%', () => { render(); expect(screen.getByText('100.0%')).toBeInTheDocument(); }); it('should handle zero target value', () => { render(); expect(screen.getByText('0.0%')).toBeInTheDocument(); }); it('should handle zero current value', () => { render(); expect(screen.getByText('0.0%')).toBeInTheDocument(); }); }); describe('value display', () => { it('should display current and target values', () => { render(); expect(screen.getByText(/50.*\/.*100/)).toBeInTheDocument(); }); it('should display unit when provided', () => { render(); expect(screen.getByText(/5,000.*\/.*10,000 USD/)).toBeInTheDocument(); }); it('should hide values when showValue is false', () => { render(); expect(screen.queryByText(/50.*\/.*100/)).not.toBeInTheDocument(); }); it('should hide percentage when showPercentage is false', () => { render(); expect(screen.queryByText('50.0%')).not.toBeInTheDocument(); }); it('should format large numbers with locale', () => { render(); expect(screen.getByText(/1,000,000.*\/.*2,000,000/)).toBeInTheDocument(); }); }); describe('color logic', () => { it('should show green color when progress >= 100%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-green-500'); expect(progressFill).toBeInTheDocument(); }); it('should show green-400 color when progress >= 80%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-green-400'); expect(progressFill).toBeInTheDocument(); }); it('should show yellow color when progress >= 50%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-yellow-500'); expect(progressFill).toBeInTheDocument(); }); it('should show orange color when progress >= 25%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-orange-500'); expect(progressFill).toBeInTheDocument(); }); it('should show red color when progress < 25%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-red-500'); expect(progressFill).toBeInTheDocument(); }); }); describe('size variants', () => { it('should render small size', () => { const { container } = render( ); const progressBar = container.querySelector('.h-2'); expect(progressBar).toBeInTheDocument(); }); it('should render medium size (default)', () => { const { container } = render( ); const progressBar = container.querySelector('.h-3'); expect(progressBar).toBeInTheDocument(); }); it('should render large size', () => { const { container } = render( ); const progressBar = container.querySelector('.h-4'); expect(progressBar).toBeInTheDocument(); }); }); describe('milestones', () => { it('should render milestones when provided', () => { const milestones = [ { percentage: 25 }, { percentage: 50 }, { percentage: 75 }, ]; const { container } = render( ); // Should have milestone markers const milestoneMarkers = container.querySelectorAll('.rounded-full.border-2'); expect(milestoneMarkers.length).toBeGreaterThanOrEqual(3); }); it('should mark achieved milestones as green', () => { const milestones = [ { percentage: 25 }, { percentage: 50 }, { percentage: 75 }, ]; const { container } = render( ); // 25% and 50% milestones should be achieved (green) const achievedMilestones = container.querySelectorAll('.bg-green-500.border-green-600'); expect(achievedMilestones.length).toBe(2); }); it('should show milestone labels for large size', () => { const milestones = [ { percentage: 25 }, { percentage: 50 }, { percentage: 75 }, ]; render( ); expect(screen.getByText('25%')).toBeInTheDocument(); expect(screen.getByText('50%')).toBeInTheDocument(); expect(screen.getByText('75%')).toBeInTheDocument(); }); it('should not show milestone labels for non-large sizes', () => { const milestones = [ { percentage: 50 }, ]; render( ); // The percentage label in the stats row is different from milestone labels // Milestone labels are only shown for size="lg" const allText = screen.queryAllByText('50%'); // Should not have additional milestone label for small size expect(allText.length).toBe(0); }); }); describe('custom className', () => { it('should apply custom className', () => { const { container } = render( ); const wrapper = container.firstChild; expect(wrapper).toHaveClass('my-custom-class'); }); }); describe('edge cases', () => { it('should handle decimal values', () => { render(); expect(screen.getByText('33.3%')).toBeInTheDocument(); }); it('should handle very small progress', () => { render(); expect(screen.getByText('0.1%')).toBeInTheDocument(); }); it('should handle exact boundary at 80%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-green-400'); expect(progressFill).toBeInTheDocument(); }); it('should handle exact boundary at 50%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-yellow-500'); expect(progressFill).toBeInTheDocument(); }); it('should handle exact boundary at 25%', () => { const { container } = render(); const progressFill = container.querySelector('.bg-orange-500'); expect(progressFill).toBeInTheDocument(); }); }); });