import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import { TrackStatsDisplay } from './TrackStatsDisplay'; import { getTrackStats } from '../services/analyticsService'; import { TrackServiceError as TrackStatsError } from '../errors/trackErrors'; // Mock dependencies vi.mock('../services/trackStatsService'); vi.mock('@/components/ui/loading-spinner', () => ({ LoadingSpinner: () =>
Loading...
, })); vi.mock('@/components/ui/alert', () => ({ Alert: ({ children, className }: any) => (
{children}
), AlertDescription: ({ children }: any) =>
{children}
, })); describe('TrackStatsDisplay', () => { const mockStats = { views: 1000, likes: 250, comments: 50, total_play_time: 3665, // 1h 1m 5s downloads: 25, }; beforeEach(() => { vi.clearAllMocks(); }); it('should render loading state initially', () => { vi.mocked(getTrackStats).mockImplementation( () => new Promise(() => {}), // Never resolves ); render(); expect(screen.getByTestId('loading-spinner')).toBeInTheDocument(); }); it('should display stats in horizontal layout', async () => { vi.mocked(getTrackStats).mockResolvedValue(mockStats); render(); await waitFor(() => { expect(screen.getByText('1.0K')).toBeInTheDocument(); // views expect(screen.getByText('250')).toBeInTheDocument(); // likes expect(screen.getByText('50')).toBeInTheDocument(); // comments expect(screen.getByText('25')).toBeInTheDocument(); // downloads expect(screen.getByText('1h 1m')).toBeInTheDocument(); // total_play_time }); // Check icons are present (Eye, Heart, MessageCircle, Download, Clock) const icons = screen.getAllByRole('img', { hidden: true }); expect(icons.length).toBeGreaterThan(0); }); it('should display stats in vertical layout', async () => { vi.mocked(getTrackStats).mockResolvedValue(mockStats); render(); await waitFor(() => { expect(screen.getByText('1.0K')).toBeInTheDocument(); }); }); it('should show labels when showLabels is true', async () => { vi.mocked(getTrackStats).mockResolvedValue(mockStats); render(); await waitFor(() => { expect(screen.getByText('Vues')).toBeInTheDocument(); expect(screen.getByText('Likes')).toBeInTheDocument(); expect(screen.getByText('Commentaires')).toBeInTheDocument(); expect(screen.getByText('Téléchargements')).toBeInTheDocument(); expect(screen.getByText("Temps d'écoute")).toBeInTheDocument(); }); }); it('should format large numbers correctly', async () => { const largeStats = { views: 1500000, likes: 2500, comments: 500, total_play_time: 7200, downloads: 100, }; vi.mocked(getTrackStats).mockResolvedValue(largeStats); render(); await waitFor(() => { expect(screen.getByText('1.5M')).toBeInTheDocument(); // views expect(screen.getByText('2.5K')).toBeInTheDocument(); // likes }); }); it('should format duration correctly', async () => { const statsWithDuration = { views: 100, likes: 10, comments: 5, total_play_time: 3665, // 1h 1m 5s downloads: 2, }; vi.mocked(getTrackStats).mockResolvedValue(statsWithDuration); render(); await waitFor(() => { expect(screen.getByText('1h 1m')).toBeInTheDocument(); }); }); it('should format duration with only minutes', async () => { const statsWithMinutes = { views: 100, likes: 10, comments: 5, total_play_time: 125, // 2m 5s downloads: 2, }; vi.mocked(getTrackStats).mockResolvedValue(statsWithMinutes); render(); await waitFor(() => { expect(screen.getByText('2m 5s')).toBeInTheDocument(); }); }); it('should format duration with only seconds', async () => { const statsWithSeconds = { views: 100, likes: 10, comments: 5, total_play_time: 45, // 45s downloads: 2, }; vi.mocked(getTrackStats).mockResolvedValue(statsWithSeconds); render(); await waitFor(() => { expect(screen.getByText('45s')).toBeInTheDocument(); }); }); it('should display error message on failure', async () => { const error = new TrackStatsError('Track introuvable', 'NOT_FOUND', false); vi.mocked(getTrackStats).mockRejectedValue(error); render(); await waitFor(() => { expect(screen.getByText('Track introuvable')).toBeInTheDocument(); }); }); it('should display generic error message on unknown error', async () => { vi.mocked(getTrackStats).mockRejectedValue(new Error('Unknown error')); render(); await waitFor(() => { expect( screen.getByText('Impossible de charger les statistiques'), ).toBeInTheDocument(); }); }); it('should reload stats when trackId changes', async () => { vi.mocked(getTrackStats).mockResolvedValue(mockStats); const { rerender } = render(); await waitFor(() => { expect(getTrackStats).toHaveBeenCalledWith(123); }); vi.mocked(getTrackStats).mockResolvedValue({ ...mockStats, views: 2000, }); rerender(); await waitFor(() => { expect(getTrackStats).toHaveBeenCalledWith(456); }); }); it('should apply custom className', async () => { vi.mocked(getTrackStats).mockResolvedValue(mockStats); render(); await waitFor(() => { const container = screen.getByText('1.0K').closest('div'); expect(container?.className).toContain('custom-class'); }); }); it('should handle zero values', async () => { const zeroStats = { views: 0, likes: 0, comments: 0, total_play_time: 0, downloads: 0, }; vi.mocked(getTrackStats).mockResolvedValue(zeroStats); render(); await waitFor(() => { expect(screen.getByText('0')).toBeInTheDocument(); expect(screen.getByText('0s')).toBeInTheDocument(); }); }); });