import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { TrackDetailPage } from './TrackDetailPage'; import { getTrack } from '../services/trackService'; import { TrackServiceError as TrackUploadError } from '../errors/trackErrors'; import { usePlayerStore } from '@/stores/player'; import { useToast } from '@/hooks/useToast'; import { useParams, useNavigate } from 'react-router-dom'; import type { Track } from '../types/track'; // Mock dependencies vi.mock('../services/trackService'); vi.mock('@/stores/player'); vi.mock('@/hooks/useToast'); vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom'); return { ...actual, useParams: vi.fn(), useNavigate: vi.fn(), }; }); describe('TrackDetailPage', () => { const mockNavigate = vi.fn(); const mockToast = { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), toast: vi.fn(), }; const mockTrack: Track = { id: 1, creator_id: 123, title: 'Test Track', artist: 'Test Artist', album: 'Test Album', duration: 180, genre: 'Rock', year: 2024, file_path: '/uploads/track.mp3', file_size: 1024, format: 'MP3', bitrate: 320, sample_rate: 44100, waveform_path: '/waveforms/track.png', cover_art_path: '/covers/track.jpg', is_public: true, play_count: 100, like_count: 50, created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-01T00:00:00Z', }; const mockPlayerStore = { play: vi.fn(), pause: vi.fn(), currentTrack: null, isPlaying: false, addToQueue: vi.fn(), }; beforeEach(() => { vi.clearAllMocks(); vi.mocked(useParams).mockReturnValue({ id: '1' }); vi.mocked(useNavigate).mockReturnValue(mockNavigate); vi.mocked(useToast).mockReturnValue(mockToast); vi.mocked(usePlayerStore).mockReturnValue(mockPlayerStore as any); }); afterEach(() => { vi.restoreAllMocks(); }); it('should render loading state initially', async () => { vi.mocked(getTrack).mockImplementation( () => new Promise((resolve) => { setTimeout(() => resolve(mockTrack), 100); }), ); render(); expect(screen.getByText(/chargement du track/i)).toBeInTheDocument(); }); it('should render track details after loading', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); expect(screen.getByText('Test Artist')).toBeInTheDocument(); expect(screen.getByText('Test Album')).toBeInTheDocument(); }); expect(screen.getByText('3:00')).toBeInTheDocument(); // 180 seconds expect(screen.getByText('Rock')).toBeInTheDocument(); expect(screen.getByText('2024')).toBeInTheDocument(); expect(screen.getByText('100')).toBeInTheDocument(); // play_count expect(screen.getByText('50')).toBeInTheDocument(); // like_count }); it('should display error message on load failure', async () => { const error = new TrackUploadError('Track not found', 'VALIDATION', false); vi.mocked(getTrack).mockRejectedValue(error); render(); await waitFor(() => { expect(screen.getByText(/track introuvable/i)).toBeInTheDocument(); expect(mockToast.error).toHaveBeenCalledWith('Track not found'); }); }); it('should handle missing track ID', async () => { vi.mocked(useParams).mockReturnValue({ id: undefined }); render(); await waitFor(() => { expect(screen.getByText(/track id is required/i)).toBeInTheDocument(); }); }); it('should play track when play button is clicked', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); const playButton = screen.getByRole('button', { name: /play/i }); await userEvent.click(playButton); expect(mockPlayerStore.play).toHaveBeenCalled(); }); it('should pause track when pause button is clicked if track is playing', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); vi.mocked(usePlayerStore).mockReturnValue({ ...mockPlayerStore, currentTrack: { id: '1' } as any, isPlaying: true, } as any); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); const pauseButton = screen.getByRole('button', { name: /pause/i }); await userEvent.click(pauseButton); expect(mockPlayerStore.pause).toHaveBeenCalled(); }); it('should add track to queue when queue button is clicked', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); const queueButtons = screen.getAllByRole('button'); const queueButton = queueButtons.find((btn) => btn.getAttribute('title')?.includes("file d'attente"), ); if (queueButton) { await userEvent.click(queueButton); expect(mockPlayerStore.addToQueue).toHaveBeenCalled(); expect(mockToast.success).toHaveBeenCalledWith( expect.stringContaining('ajouté à la file'), ); } }); it('should copy share link when share button is clicked', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); const mockWriteText = vi.fn().mockResolvedValue(undefined); Object.assign(navigator, { clipboard: { writeText: mockWriteText, }, }); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); const shareButtons = screen.getAllByRole('button'); const shareButton = shareButtons.find((btn) => btn.getAttribute('title')?.includes('Partager'), ); if (shareButton) { await userEvent.click(shareButton); expect(mockWriteText).toHaveBeenCalled(); expect(mockToast.success).toHaveBeenCalledWith( expect.stringContaining('copié'), ); } }); it('should navigate back when back button is clicked', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); const backButton = screen.getByRole('button', { name: /retour/i }); await userEvent.click(backButton); expect(mockNavigate).toHaveBeenCalledWith(-1); }); it('should display waveform if available', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { const waveform = screen.getByAltText('Waveform'); expect(waveform).toBeInTheDocument(); expect(waveform).toHaveAttribute('src', '/waveforms/track.png'); }); }); it('should display cover art if available', async () => { vi.mocked(getTrack).mockResolvedValue(mockTrack); render(); await waitFor(() => { const coverArt = screen.getByAltText('Test Track'); expect(coverArt).toBeInTheDocument(); expect(coverArt).toHaveAttribute('src', '/covers/track.jpg'); }); }); it('should display placeholder if no cover art', async () => { const trackWithoutCover = { ...mockTrack, cover_art_path: undefined }; vi.mocked(getTrack).mockResolvedValue(trackWithoutCover); render(); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); // Should have Music icon placeholder const musicIcons = screen.getAllByRole('img', { hidden: true }); expect(musicIcons.length).toBeGreaterThan(0); }); });