230 lines
6.5 KiB
TypeScript
230 lines
6.5 KiB
TypeScript
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: () => <div data-testid="loading-spinner">Loading...</div>,
|
|
}));
|
|
vi.mock('@/components/ui/alert', () => ({
|
|
Alert: ({ children, className }: any) => (
|
|
<div className={className}>{children}</div>
|
|
),
|
|
AlertDescription: ({ children }: any) => <div>{children}</div>,
|
|
}));
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display stats in horizontal layout', async () => {
|
|
vi.mocked(getTrackStats).mockResolvedValue(mockStats);
|
|
|
|
render(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} variant="vertical" />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('1.0K')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('should show labels when showLabels is true', async () => {
|
|
vi.mocked(getTrackStats).mockResolvedValue(mockStats);
|
|
|
|
render(<TrackStatsDisplay trackId={123} showLabels={true} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={999} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
await waitFor(() => {
|
|
expect(getTrackStats).toHaveBeenCalledWith(123);
|
|
});
|
|
|
|
vi.mocked(getTrackStats).mockResolvedValue({
|
|
...mockStats,
|
|
views: 2000,
|
|
});
|
|
|
|
rerender(<TrackStatsDisplay trackId={456} />);
|
|
|
|
await waitFor(() => {
|
|
expect(getTrackStats).toHaveBeenCalledWith(456);
|
|
});
|
|
});
|
|
|
|
it('should apply custom className', async () => {
|
|
vi.mocked(getTrackStats).mockResolvedValue(mockStats);
|
|
|
|
render(<TrackStatsDisplay trackId={123} className="custom-class" />);
|
|
|
|
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(<TrackStatsDisplay trackId={123} />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('0')).toBeInTheDocument();
|
|
expect(screen.getByText('0s')).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|