diff --git a/frontend/__tests__/unit/components/MetricsScoreCircle.test.tsx b/frontend/__tests__/unit/components/MetricsScoreCircle.test.tsx new file mode 100644 index 0000000000..141d550c58 --- /dev/null +++ b/frontend/__tests__/unit/components/MetricsScoreCircle.test.tsx @@ -0,0 +1,221 @@ +import { render, screen } from '@testing-library/react' +import React from 'react' +import '@testing-library/jest-dom' +import MetricsScoreCircle from 'components/MetricsScoreCircle' + +// Mock the Tooltip component from @heroui/tooltip +jest.mock('@heroui/tooltip', () => ({ + Tooltip: ({ + children, + content, + placement, + }: { + children: React.ReactNode + content: string + placement: string + }) => ( +
+ {children} +
+ ), +})) + +describe('MetricsScoreCircle', () => { + // Test 1: Renders successfully with minimal required props + it('renders successfully with score prop', () => { + render() + expect(screen.getByText('75')).toBeInTheDocument() + expect(screen.getByText('Health')).toBeInTheDocument() + expect(screen.getByText('Score')).toBeInTheDocument() + }) + + // Test 2: Text and content rendering + it('displays the correct score value', () => { + const score = 85 + render() + expect(screen.getByText(score.toString())).toBeInTheDocument() + }) + + it('displays static labels correctly', () => { + render() + expect(screen.getByText('Health')).toBeInTheDocument() + expect(screen.getByText('Score')).toBeInTheDocument() + }) + + // Test 3: Conditional rendering logic - Color schemes based on score + describe('score-based styling', () => { + it('applies green styling for high scores (>= 75)', () => { + const { container } = render() + // Look for elements with green background classes + const greenElement = container.querySelector('[class*="bg-green"]') + expect(greenElement).toBeInTheDocument() + }) + + it('applies yellow styling for medium scores (50-74)', () => { + const { container } = render() + // Look for elements with yellow background classes + const yellowElement = container.querySelector('[class*="bg-yellow"]') + expect(yellowElement).toBeInTheDocument() + }) + + it('applies red styling for low scores (< 50)', () => { + const { container } = render() + // Look for elements with red background classes + const redElement = container.querySelector('[class*="bg-red"]') + expect(redElement).toBeInTheDocument() + }) + + it('applies correct styling at boundary values', () => { + // Test score = 50 (should be yellow) + const { container: container50 } = render() + expect(container50.querySelector('[class*="bg-yellow"]')).toBeInTheDocument() + + // Test score = 74 (should be yellow) + const { container: container74 } = render() + expect(container74.querySelector('[class*="bg-yellow"]')).toBeInTheDocument() + + // Test score = 75 (should be green) + const { container: container75 } = render() + expect(container75.querySelector('[class*="bg-green"]')).toBeInTheDocument() + }) + }) + + // Test 4: Conditional rendering - Pulse animation for very low scores + it('shows pulse animation for scores below 30', () => { + const { container } = render() + const pulseElement = container.querySelector('[class*="animate-pulse"]') + expect(pulseElement).toBeInTheDocument() + }) + + it('does not show pulse animation for scores 30 and above', () => { + const { container } = render() + const pulseElement = container.querySelector('[class*="animate-pulse"]') + expect(pulseElement).not.toBeInTheDocument() + }) + + // Test 5: Tooltip functionality + it('renders tooltip with correct content', () => { + render() + const tooltipWrapper = screen.getByTestId('tooltip-wrapper') + expect(tooltipWrapper).toHaveAttribute('data-content', 'Current Project Health Score') + expect(tooltipWrapper).toHaveAttribute('data-placement', 'top') + }) + + // Test 6: DOM structure and classNames + it('has correct DOM structure and classes', () => { + const { container } = render() + + // Check for main structural elements + const tooltipWrapper = screen.getByTestId('tooltip-wrapper') + expect(tooltipWrapper).toBeInTheDocument() + + // Check for elements with expected classes + const circularElement = container.querySelector('[class*="rounded-full"]') + expect(circularElement).toBeInTheDocument() + + const flexElement = container.querySelector('[class*="flex"]') + expect(flexElement).toBeInTheDocument() + }) + + // Test 7: Handles edge cases and invalid inputs + describe('edge cases', () => { + it('handles score of 0', () => { + render() + expect(screen.getByText('0')).toBeInTheDocument() + // Should be red styling + const { container } = render() + expect(container.querySelector('[class*="bg-red"]')).toBeInTheDocument() + }) + + it('handles score of 100', () => { + render() + expect(screen.getByText('100')).toBeInTheDocument() + // Should be green styling + const { container } = render() + expect(container.querySelector('[class*="bg-green"]')).toBeInTheDocument() + }) + + it('handles negative scores', () => { + render() + expect(screen.getByText('-10')).toBeInTheDocument() + // Should be red styling + const { container } = render() + expect(container.querySelector('[class*="bg-red"]')).toBeInTheDocument() + }) + + it('handles scores above 100', () => { + render() + expect(screen.getByText('150')).toBeInTheDocument() + // Should be green styling + const { container } = render() + expect(container.querySelector('[class*="bg-green"]')).toBeInTheDocument() + }) + + it('handles decimal scores', () => { + render() + expect(screen.getByText('75.5')).toBeInTheDocument() + }) + }) + + // Test 8: Accessibility + it('has proper accessibility structure', () => { + render() + + // Check that the tooltip provides accessible description + const tooltipWrapper = screen.getByTestId('tooltip-wrapper') + expect(tooltipWrapper).toBeInTheDocument() + + // Verify text content is accessible + expect(screen.getByText('75')).toBeInTheDocument() + expect(screen.getByText('Health')).toBeInTheDocument() + expect(screen.getByText('Score')).toBeInTheDocument() + }) + + // Test 9: Event handling - hover effects (visual testing through classes) + it('has hover effect classes applied', () => { + const { container } = render() + + // Check for hover-related classes + const hoverElement = container.querySelector('[class*="hover:"]') + expect(hoverElement).toBeInTheDocument() + }) + + // Test 10: Component integration test + it('integrates all features correctly for a low score', () => { + const { container } = render() + + // Should have red styling + expect(container.querySelector('[class*="bg-red"]')).toBeInTheDocument() + + // Should have pulse animation + expect(container.querySelector('[class*="animate-pulse"]')).toBeInTheDocument() + + // Should display correct score + expect(screen.getByText('15')).toBeInTheDocument() + + // Should have tooltip + expect(screen.getByTestId('tooltip-wrapper')).toHaveAttribute( + 'data-content', + 'Current Project Health Score' + ) + }) + + it('integrates all features correctly for a high score', () => { + const { container } = render() + + // Should have green styling + expect(container.querySelector('[class*="bg-green"]')).toBeInTheDocument() + + // Should NOT have pulse animation + expect(container.querySelector('[class*="animate-pulse"]')).not.toBeInTheDocument() + + // Should display correct score + expect(screen.getByText('90')).toBeInTheDocument() + + // Should have tooltip + expect(screen.getByTestId('tooltip-wrapper')).toHaveAttribute( + 'data-content', + 'Current Project Health Score' + ) + }) +})