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 { CommentSection } from './CommentSection'; import { getComments, createComment, CommentError, } from '../services/commentService'; import { useAuthStore } from '@/stores/auth'; import { useToast } from '@/hooks/useToast'; import type { TrackComment } from '../services/commentService'; // Mock dependencies vi.mock('../services/commentService'); vi.mock('@/stores/auth'); vi.mock('@/hooks/useToast'); describe('CommentSection', () => { const mockToast = { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), toast: vi.fn(), }; const mockUser = { id: 123, username: 'testuser', email: 'test@example.com', }; const mockComments: TrackComment[] = [ { id: 1, track_id: 1, user_id: 123, content: 'Great track!', is_edited: false, created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-01T00:00:00Z', user: { id: 123, username: 'testuser', }, }, { id: 2, track_id: 1, user_id: 456, content: 'Amazing!', is_edited: false, created_at: '2024-01-02T00:00:00Z', updated_at: '2024-01-02T00:00:00Z', user: { id: 456, username: 'otheruser', }, }, ]; beforeEach(() => { vi.clearAllMocks(); vi.mocked(useAuthStore).mockReturnValue({ user: mockUser, isAuthenticated: true, isLoading: false, error: null, login: vi.fn(), register: vi.fn(), logout: vi.fn(), refreshUser: vi.fn(), clearError: vi.fn(), setLoading: vi.fn(), checkAuthStatus: vi.fn(), }); vi.mocked(useToast).mockReturnValue(mockToast); }); afterEach(() => { vi.restoreAllMocks(); }); it('should render comment section', async () => { vi.mocked(getComments).mockResolvedValue({ comments: mockComments, total: 2, page: 1, limit: 20, }); render(); await waitFor(() => { expect(screen.getByText('Commentaires (2)')).toBeInTheDocument(); }); }); it('should display loading state', () => { vi.mocked(getComments).mockImplementation( () => new Promise((resolve) => { setTimeout(() => { resolve({ comments: [], total: 0, page: 1, limit: 20, }); }, 100); }), ); render(); expect(screen.getByRole('status')).toBeInTheDocument(); }); it('should display empty state when no comments', async () => { vi.mocked(getComments).mockResolvedValue({ comments: [], total: 0, page: 1, limit: 20, }); render(); await waitFor(() => { expect( screen.getByText(/aucun commentaire pour le moment/i), ).toBeInTheDocument(); }); }); it('should display comments', async () => { vi.mocked(getComments).mockResolvedValue({ comments: mockComments, total: 2, page: 1, limit: 20, }); render(); await waitFor(() => { expect(screen.getByText('Great track!')).toBeInTheDocument(); expect(screen.getByText('Amazing!')).toBeInTheDocument(); }); }); it('should create a new comment', async () => { const user = userEvent.setup(); const newComment: TrackComment = { id: 3, track_id: 1, user_id: 123, content: 'New comment', is_edited: false, created_at: '2024-01-03T00:00:00Z', updated_at: '2024-01-03T00:00:00Z', user: { id: 123, username: 'testuser', }, }; vi.mocked(getComments).mockResolvedValue({ comments: mockComments, total: 2, page: 1, limit: 20, }); vi.mocked(createComment).mockResolvedValue(newComment); render(); await waitFor(() => { expect( screen.getByPlaceholderText('Écrivez un commentaire...'), ).toBeInTheDocument(); }); const textarea = screen.getByPlaceholderText('Écrivez un commentaire...'); await user.type(textarea, 'New comment'); const submitButton = screen.getByRole('button', { name: /commenter/i }); await user.click(submitButton); await waitFor(() => { expect(createComment).toHaveBeenCalledWith(1, 'New comment', undefined); expect(mockToast.success).toHaveBeenCalledWith('Commentaire ajouté'); }); }); it('should load more comments', async () => { const user = userEvent.setup(); const moreComments: TrackComment[] = [ { id: 3, track_id: 1, user_id: 123, content: 'More comments', is_edited: false, created_at: '2024-01-03T00:00:00Z', updated_at: '2024-01-03T00:00:00Z', user: { id: 123, username: 'testuser', }, }, ]; vi.mocked(getComments) .mockResolvedValueOnce({ comments: mockComments, total: 3, page: 1, limit: 20, }) .mockResolvedValueOnce({ comments: moreComments, total: 3, page: 2, limit: 20, }); render(); await waitFor(() => { expect(screen.getByText('Great track!')).toBeInTheDocument(); }); const loadMoreButton = screen.getByRole('button', { name: /charger plus/i, }); await user.click(loadMoreButton); await waitFor(() => { expect(getComments).toHaveBeenCalledWith(1, 2, 20); }); }); it('should handle error when loading comments', async () => { const error = new CommentError('Failed to load comments', 'NETWORK', true); vi.mocked(getComments).mockRejectedValue(error); render(); await waitFor(() => { expect(mockToast.error).toHaveBeenCalledWith('Failed to load comments'); }); }); it('should refresh comments', async () => { const user = userEvent.setup(); vi.mocked(getComments).mockResolvedValue({ comments: mockComments, total: 2, page: 1, limit: 20, }); render(); await waitFor(() => { expect(screen.getByText('Great track!')).toBeInTheDocument(); }); const refreshButton = screen.getByRole('button', { name: /actualiser/i }); await user.click(refreshButton); await waitFor(() => { expect(getComments).toHaveBeenCalledWith(1, 1, 20); }); }); });