/** * Tests for StatsSection component */ import { render, screen } from '@testing-library/react'; import { StatsSection } from '@/components/home/StatsSection'; // Mock framer-motion jest.mock('framer-motion', () => { const React = require('react'); return { motion: { div: ({ children, ...props }: any) =>
{children}
, }, useInView: () => true, // Always in view for tests }; }); describe('StatsSection', () => { beforeEach(() => { jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); it('renders section heading', () => { render(); expect(screen.getByText('Built with Quality in Mind')).toBeInTheDocument(); expect(screen.getByText(/Not just another template/i)).toBeInTheDocument(); }); it('renders all stat cards', () => { render(); expect(screen.getByText('Open Source')).toBeInTheDocument(); expect(screen.getByText('Type Safe')).toBeInTheDocument(); expect(screen.getByText('Doc Guides')).toBeInTheDocument(); expect(screen.getByText('Magic')).toBeInTheDocument(); }); it('displays stat descriptions', () => { render(); expect( screen.getByText(/MIT Licensed. No hidden costs or vendor lock-in/i) ).toBeInTheDocument(); expect( screen.getByText(/End-to-end type safety with TypeScript & Pydantic/i) ).toBeInTheDocument(); expect(screen.getByText(/Comprehensive documentation for every feature/i)).toBeInTheDocument(); expect( screen.getByText(/Explicit is better than implicit. No hidden logic/i) ).toBeInTheDocument(); }); it('renders animated counters with correct suffixes', () => { render(); // Counters start at 0, so we should see 0 initially const counters = screen.getAllByText(/^[0-9]+[%+]?$/); expect(counters.length).toBeGreaterThan(0); }); it('animates counters when in view', () => { render(); // The useInView mock returns true, so animation should start // Advance timers to let the counter animation run jest.advanceTimersByTime(2000); // After animation, we should see the final values // The component should eventually show the stat values const statsSection = screen.getByText('Open Source').parentElement; expect(statsSection).toBeInTheDocument(); }); it('displays icons for each stat', () => { render(); // Icons are rendered via lucide-react components // We can verify the stat cards are rendered with proper structure const openSourceCard = screen.getByText('Open Source').closest('div'); expect(openSourceCard).toBeInTheDocument(); const typeSafeCard = screen.getByText('Type Safe').closest('div'); expect(typeSafeCard).toBeInTheDocument(); const docGuidesCard = screen.getByText('Doc Guides').closest('div'); expect(docGuidesCard).toBeInTheDocument(); const magicCard = screen.getByText('Magic').closest('div'); expect(magicCard).toBeInTheDocument(); }); describe('Accessibility', () => { it('has proper heading hierarchy', () => { render(); const heading = screen.getByRole('heading', { name: /built with quality in mind/i }); expect(heading).toBeInTheDocument(); }); it('has descriptive labels for stats', () => { render(); const statLabels = ['Open Source', 'Type Safe', 'Doc Guides', 'Magic']; statLabels.forEach((label) => { expect(screen.getByText(label)).toBeInTheDocument(); }); }); }); });