import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { TrackListContainer } from './TrackListContainer'; import type { Track } from '../../player/types'; // Mock useTrackList const mockUseTrackList = vi.fn(); vi.mock('../hooks/useTrackList', () => ({ useTrackList: (options: unknown) => mockUseTrackList(options), })); // Mock useSearchParams const mockSetSearchParams = vi.fn(); const mockSearchParams = new URLSearchParams(); vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom'); return { ...actual, useSearchParams: () => [mockSearchParams, mockSetSearchParams], }; }); const mockTracks: Track[] = [ { id: 1, title: 'Track 1', artist: 'Artist 1', album: 'Album 1', duration: 180, url: 'https://example.com/track1.mp3', genre: 'Rock', }, { id: 2, title: 'Track 2', artist: 'Artist 2', album: 'Album 2', duration: 240, url: 'https://example.com/track2.mp3', genre: 'Pop', }, ]; const defaultTrackListReturn = { tracks: mockTracks, filteredTracks: mockTracks, displayMode: 'list' as const, sortOptions: { field: 'title' as const, order: 'asc' as const }, filterOptions: {}, isLoading: false, error: null, pagination: { page: 1, limit: 20 }, total: 2, totalPages: 1, setTracks: vi.fn(), setDisplayMode: vi.fn(), setSortField: vi.fn(), setSortOrder: vi.fn(), setFilterOptions: vi.fn(), clearFilters: vi.fn(), setPagination: vi.fn(), setPage: vi.fn(), setLimit: vi.fn(), setSearchQuery: vi.fn(), addTrack: vi.fn(), removeTrack: vi.fn(), updateTrack: vi.fn(), loadTracks: vi.fn(), refreshTracks: vi.fn(), searchQuery: '', }; describe('TrackListContainer', () => { beforeEach(() => { vi.clearAllMocks(); mockUseTrackList.mockReturnValue(defaultTrackListReturn); }); it('should render track list container', () => { render(); expect( screen.getByRole('group', { name: 'Options de tri' }), ).toBeInTheDocument(); }); it('should display filters when showFilters is true', () => { render(); expect(screen.getByLabelText('Rechercher des pistes')).toBeInTheDocument(); }); it('should not display filters when showFilters is false', () => { render(); expect( screen.queryByLabelText('Rechercher des pistes'), ).not.toBeInTheDocument(); }); it('should display sort when showSort is true', () => { render(); expect( screen.getByLabelText('Sélectionner le champ de tri'), ).toBeInTheDocument(); }); it('should not display sort when showSort is false', () => { render(); expect( screen.queryByLabelText('Sélectionner le champ de tri'), ).not.toBeInTheDocument(); }); it('should display view toggle when showViewToggle is true', () => { render(); expect(screen.getByLabelText('Vue liste')).toBeInTheDocument(); expect(screen.getByLabelText('Vue grille')).toBeInTheDocument(); }); it('should not display view toggle when showViewToggle is false', () => { render(); expect(screen.queryByLabelText('Vue liste')).not.toBeInTheDocument(); }); it('should display list view by default', () => { render(); // TrackList devrait être rendu (on peut vérifier via les tracks) expect(mockUseTrackList).toHaveBeenCalled(); }); it('should switch to grid view when displayMode changes', () => { const { rerender } = render(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, displayMode: 'grid', }); rerender(); expect(mockUseTrackList).toHaveBeenCalled(); }); it('should display pagination when showPagination is true and totalPages > 1', () => { mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, totalPages: 3, }); render(); expect( screen.getByRole('navigation', { name: /pagination/i }), ).toBeInTheDocument(); }); it('should not display pagination when showPagination is false', () => { mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, totalPages: 3, }); render(); expect( screen.queryByRole('navigation', { name: /pagination/i }), ).not.toBeInTheDocument(); }); it('should not display pagination when totalPages <= 1', () => { mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, totalPages: 1, }); render(); expect( screen.queryByRole('navigation', { name: /pagination/i }), ).not.toBeInTheDocument(); }); it('should call onTrackClick when track is clicked', async () => { const mockOnTrackClick = vi.fn(); render(); // Le clic sera géré par TrackList/TrackGrid // On vérifie que le callback est passé expect(mockOnTrackClick).toBeDefined(); }); it('should call onTrackPlay when track play button is clicked', async () => { const mockOnTrackPlay = vi.fn(); render(); // Le clic sera géré par TrackList/TrackGrid // On vérifie que le callback est passé expect(mockOnTrackPlay).toBeDefined(); }); it('should handle filter changes', async () => { const mockSetFilterOptions = vi.fn(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, setFilterOptions: mockSetFilterOptions, }); render(); // Les changements de filtres seront gérés par TrackFilters expect(mockSetFilterOptions).toBeDefined(); }); it('should handle sort changes', async () => { const mockSetSortField = vi.fn(); const mockSetSortOrder = vi.fn(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, setSortField: mockSetSortField, setSortOrder: mockSetSortOrder, }); render(); // Les changements de tri seront gérés par TrackSort expect(mockSetSortField).toBeDefined(); expect(mockSetSortOrder).toBeDefined(); }); it('should handle view mode changes', async () => { const mockSetDisplayMode = vi.fn(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, setDisplayMode: mockSetDisplayMode, }); render(); // Les changements de vue seront gérés par ViewToggle expect(mockSetDisplayMode).toBeDefined(); }); it('should display error state when error occurs', () => { const mockRefreshTracks = vi.fn(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, error: new Error('Test error'), filteredTracks: [], isLoading: false, refreshTracks: mockRefreshTracks, }); render(); const errorMessages = screen.getAllByText('Erreur lors du chargement'); expect(errorMessages.length).toBeGreaterThan(0); expect(screen.getByLabelText('Réessayer')).toBeInTheDocument(); }); it('should call refreshTracks when retry button is clicked', async () => { const user = userEvent.setup(); const mockRefreshTracks = vi.fn(); mockUseTrackList.mockReturnValue({ ...defaultTrackListReturn, error: new Error('Test error'), filteredTracks: [], isLoading: false, refreshTracks: mockRefreshTracks, }); render(); const retryButton = screen.getByLabelText('Réessayer'); await user.click(retryButton); expect(mockRefreshTracks).toHaveBeenCalledTimes(1); }); it('should handle track selection when showSelection is true', () => { render(); // La sélection sera gérée par TrackList expect(mockUseTrackList).toHaveBeenCalled(); }); it('should pass available genres to filters', () => { const availableGenres = ['Rock', 'Pop', 'Jazz']; render( , ); // Les genres disponibles seront passés à TrackFilters expect(mockUseTrackList).toHaveBeenCalled(); }); it('should pass available artists to filters', () => { const availableArtists = ['Artist 1', 'Artist 2']; render( , ); // Les artistes disponibles seront passés à TrackFilters expect(mockUseTrackList).toHaveBeenCalled(); }); it('should apply custom className', () => { const { container } = render( , ); expect(container.firstChild).toHaveClass('custom-class'); }); it('should use useTrackList with correct options', () => { render( , ); expect(mockUseTrackList).toHaveBeenCalledWith( expect.objectContaining({ useService: false, autoLoad: false, persistFilters: true, persistSort: true, syncUrlParams: true, storageKeyPrefix: 'customPrefix', }), ); }); });