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 { TrackDelete } from './TrackDelete'; import { deleteTrack } from '../services/trackService'; import { TrackServiceError as TrackUploadError } from '../errors/trackErrors'; import { useToast } from '@/hooks/useToast'; // Mock dependencies vi.mock('../services/trackService'); vi.mock('@/hooks/useToast'); describe('TrackDelete', () => { const mockToast = { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), toast: vi.fn(), }; const mockOnDelete = vi.fn(); beforeEach(() => { vi.clearAllMocks(); vi.mocked(useToast).mockReturnValue(mockToast); }); afterEach(() => { vi.restoreAllMocks(); }); it('should render delete button', () => { render(); expect(screen.getByText('Supprimer')).toBeInTheDocument(); }); it('should open dialog when button is clicked', async () => { render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); }); it('should display confirmation message in dialog', async () => { render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect( screen.getByText(/cette action est irréversible/i), ).toBeInTheDocument(); }); }); it('should display track title if provided', async () => { render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Test Track')).toBeInTheDocument(); }); }); it('should call deleteTrack and onDelete on confirm', async () => { vi.mocked(deleteTrack).mockResolvedValue(undefined); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect(deleteTrack).toHaveBeenCalledWith(1); expect(mockToast.success).toHaveBeenCalledWith( 'Track supprimé avec succès', ); expect(mockOnDelete).toHaveBeenCalled(); }); }); it('should close dialog after successful deletion', async () => { vi.mocked(deleteTrack).mockResolvedValue(undefined); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect( screen.queryByText('Supprimer le track ?'), ).not.toBeInTheDocument(); }); }); it('should handle delete error', async () => { const error = new TrackUploadError('Delete failed', 'SERVER', true); vi.mocked(deleteTrack).mockRejectedValue(error); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect(mockToast.error).toHaveBeenCalledWith('Delete failed'); }); // Dialog should remain open on error expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); it('should close dialog when cancel is clicked', async () => { render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const cancelButton = screen.getByText('Annuler'); await userEvent.click(cancelButton); await waitFor(() => { expect( screen.queryByText('Supprimer le track ?'), ).not.toBeInTheDocument(); }); expect(deleteTrack).not.toHaveBeenCalled(); }); it('should disable confirm button while deleting', async () => { vi.mocked(deleteTrack).mockImplementation( () => new Promise((resolve) => { setTimeout(() => resolve(undefined), 100); }), ); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); // Check for loading state await waitFor(() => { expect(screen.getByText(/suppression en cours/i)).toBeInTheDocument(); }); }); it('should accept custom trigger', () => { const customTrigger = ; render(); expect(screen.getByText('Custom Delete')).toBeInTheDocument(); expect(screen.queryByText('Supprimer')).not.toBeInTheDocument(); }); it('should handle 401 unauthorized error', async () => { const error = new TrackUploadError('Unauthorized', 'VALIDATION', false); vi.mocked(deleteTrack).mockRejectedValue(error); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect(mockToast.error).toHaveBeenCalledWith('Unauthorized'); }); }); it('should handle 403 forbidden error', async () => { const error = new TrackUploadError('Forbidden', 'VALIDATION', false); vi.mocked(deleteTrack).mockRejectedValue(error); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect(mockToast.error).toHaveBeenCalledWith('Forbidden'); }); }); it('should handle 404 not found error', async () => { const error = new TrackUploadError('Track not found', 'VALIDATION', false); vi.mocked(deleteTrack).mockRejectedValue(error); render(); const deleteButton = screen.getByText('Supprimer'); await userEvent.click(deleteButton); await waitFor(() => { expect(screen.getByText('Supprimer le track ?')).toBeInTheDocument(); }); const confirmButton = screen.getByText('Supprimer'); await userEvent.click(confirmButton); await waitFor(() => { expect(mockToast.error).toHaveBeenCalledWith('Track not found'); }); }); });