veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx

339 lines
9.7 KiB
TypeScript
Raw Normal View History

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(<TrackListContainer />);
2025-12-13 02:34:34 +00:00
expect(
screen.getByRole('group', { name: 'Options de tri' }),
).toBeInTheDocument();
});
it('should display filters when showFilters is true', () => {
render(<TrackListContainer showFilters={true} />);
expect(screen.getByLabelText('Rechercher des pistes')).toBeInTheDocument();
});
it('should not display filters when showFilters is false', () => {
render(<TrackListContainer showFilters={false} />);
2025-12-13 02:34:34 +00:00
expect(
screen.queryByLabelText('Rechercher des pistes'),
).not.toBeInTheDocument();
});
it('should display sort when showSort is true', () => {
render(<TrackListContainer showSort={true} />);
2025-12-13 02:34:34 +00:00
expect(
screen.getByLabelText('Sélectionner le champ de tri'),
).toBeInTheDocument();
});
it('should not display sort when showSort is false', () => {
render(<TrackListContainer showSort={false} />);
2025-12-13 02:34:34 +00:00
expect(
screen.queryByLabelText('Sélectionner le champ de tri'),
).not.toBeInTheDocument();
});
it('should display view toggle when showViewToggle is true', () => {
render(<TrackListContainer showViewToggle={true} />);
expect(screen.getByLabelText('Vue liste')).toBeInTheDocument();
expect(screen.getByLabelText('Vue grille')).toBeInTheDocument();
});
it('should not display view toggle when showViewToggle is false', () => {
render(<TrackListContainer showViewToggle={false} />);
expect(screen.queryByLabelText('Vue liste')).not.toBeInTheDocument();
});
it('should display list view by default', () => {
render(<TrackListContainer />);
// 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(<TrackListContainer />);
2025-12-13 02:34:34 +00:00
mockUseTrackList.mockReturnValue({
...defaultTrackListReturn,
displayMode: 'grid',
});
2025-12-13 02:34:34 +00:00
rerender(<TrackListContainer />);
2025-12-13 02:34:34 +00:00
expect(mockUseTrackList).toHaveBeenCalled();
});
it('should display pagination when showPagination is true and totalPages > 1', () => {
mockUseTrackList.mockReturnValue({
...defaultTrackListReturn,
totalPages: 3,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showPagination={true} />);
2025-12-13 02:34:34 +00:00
expect(
screen.getByRole('navigation', { name: /pagination/i }),
).toBeInTheDocument();
});
it('should not display pagination when showPagination is false', () => {
mockUseTrackList.mockReturnValue({
...defaultTrackListReturn,
totalPages: 3,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showPagination={false} />);
2025-12-13 02:34:34 +00:00
expect(
screen.queryByRole('navigation', { name: /pagination/i }),
).not.toBeInTheDocument();
});
it('should not display pagination when totalPages <= 1', () => {
mockUseTrackList.mockReturnValue({
...defaultTrackListReturn,
totalPages: 1,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showPagination={true} />);
2025-12-13 02:34:34 +00:00
expect(
screen.queryByRole('navigation', { name: /pagination/i }),
).not.toBeInTheDocument();
});
it('should call onTrackClick when track is clicked', async () => {
const mockOnTrackClick = vi.fn();
render(<TrackListContainer onTrackClick={mockOnTrackClick} />);
2025-12-13 02:34:34 +00:00
// 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(<TrackListContainer onTrackPlay={mockOnTrackPlay} />);
2025-12-13 02:34:34 +00:00
// 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,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showFilters={true} />);
2025-12-13 02:34:34 +00:00
// 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,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showSort={true} />);
2025-12-13 02:34:34 +00:00
// 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,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer showViewToggle={true} />);
2025-12-13 02:34:34 +00:00
// 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,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer />);
2025-12-13 02:34:34 +00:00
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,
});
2025-12-13 02:34:34 +00:00
render(<TrackListContainer />);
2025-12-13 02:34:34 +00:00
const retryButton = screen.getByLabelText('Réessayer');
await user.click(retryButton);
2025-12-13 02:34:34 +00:00
expect(mockRefreshTracks).toHaveBeenCalledTimes(1);
});
it('should handle track selection when showSelection is true', () => {
render(<TrackListContainer showSelection={true} />);
2025-12-13 02:34:34 +00:00
// 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(
<TrackListContainer
showFilters={true}
availableGenres={availableGenres}
2025-12-13 02:34:34 +00:00
/>,
);
2025-12-13 02:34:34 +00:00
// Les genres disponibles seront passés à TrackFilters
expect(mockUseTrackList).toHaveBeenCalled();
});
it('should pass available artists to filters', () => {
const availableArtists = ['Artist 1', 'Artist 2'];
render(
<TrackListContainer
showFilters={true}
availableArtists={availableArtists}
2025-12-13 02:34:34 +00:00
/>,
);
2025-12-13 02:34:34 +00:00
// Les artistes disponibles seront passés à TrackFilters
expect(mockUseTrackList).toHaveBeenCalled();
});
it('should apply custom className', () => {
2025-12-13 02:34:34 +00:00
const { container } = render(
<TrackListContainer className="custom-class" />,
);
expect(container.firstChild).toHaveClass('custom-class');
});
it('should use useTrackList with correct options', () => {
render(
<TrackListContainer
useService={false}
autoLoad={false}
persistFilters={true}
persistSort={true}
syncUrlParams={true}
storageKeyPrefix="customPrefix"
2025-12-13 02:34:34 +00:00
/>,
);
2025-12-13 02:34:34 +00:00
expect(mockUseTrackList).toHaveBeenCalledWith(
expect.objectContaining({
useService: false,
autoLoad: false,
persistFilters: true,
persistSort: true,
syncUrlParams: true,
storageKeyPrefix: 'customPrefix',
2025-12-13 02:34:34 +00:00
}),
);
});
});