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();
});
});
});