import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ChatMessages } from './ChatMessages'; // Mock des services vi.mock('@/services/websocket', () => ({ useWebSocket: () => ({ sendMessage: vi.fn(), isConnected: true, }), })); vi.mock('@/stores/chat', () => ({ useChatStore: () => ({ messages: [ { id: '1', content: 'Hello, world!', sender: { id: '1', username: 'user1' }, timestamp: '2024-01-01T10:00:00Z', type: 'text', }, { id: '2', content: 'How are you?', sender: { id: '2', username: 'user2' }, timestamp: '2024-01-01T10:01:00Z', type: 'text', }, ], currentConversation: 'conv-123', isLoading: false, error: null, }), })); const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); const TestWrapper = ({ children }: { children: React.ReactNode }) => { const queryClient = createTestQueryClient(); return ( {children} ); }; describe('ChatMessages Component', () => { beforeEach(() => { vi.clearAllMocks(); }); it('renders messages correctly', () => { render( , ); expect(screen.getByText('Hello, world!')).toBeInTheDocument(); expect(screen.getByText('How are you?')).toBeInTheDocument(); expect(screen.getByText('user1')).toBeInTheDocument(); expect(screen.getByText('user2')).toBeInTheDocument(); }); it('displays message timestamps', () => { render( , ); // Vérifier que les timestamps sont affichés (format peut varier) expect(screen.getByText(/10:00/)).toBeInTheDocument(); expect(screen.getByText(/10:01/)).toBeInTheDocument(); }); it('shows loading state when messages are loading', () => { vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [], currentConversation: 'conv-123', isLoading: true, error: null, }); render( , ); expect(screen.getByText(/loading messages/i)).toBeInTheDocument(); }); it('shows error state when there is an error', () => { vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [], currentConversation: 'conv-123', isLoading: false, error: 'Failed to load messages', }); render( , ); expect(screen.getByText(/failed to load messages/i)).toBeInTheDocument(); }); it('shows empty state when no messages', () => { vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [], currentConversation: 'conv-123', isLoading: false, error: null, }); render( , ); expect(screen.getByText(/no messages yet/i)).toBeInTheDocument(); }); it('handles different message types', () => { vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [ { id: '1', content: 'Text message', sender: { id: '1', username: 'user1' }, timestamp: '2024-01-01T10:00:00Z', type: 'text', }, { id: '2', content: 'https://example.com', sender: { id: '2', username: 'user2' }, timestamp: '2024-01-01T10:01:00Z', type: 'link', }, { id: '3', content: 'Image message', sender: { id: '3', username: 'user3' }, timestamp: '2024-01-01T10:02:00Z', type: 'image', imageUrl: 'https://example.com/image.jpg', }, ], currentConversation: 'conv-123', isLoading: false, error: null, }); render( , ); expect(screen.getByText('Text message')).toBeInTheDocument(); expect(screen.getByText('https://example.com')).toBeInTheDocument(); expect(screen.getByText('Image message')).toBeInTheDocument(); }); it('scrolls to bottom when new messages arrive', async () => { const scrollIntoViewMock = vi.fn(); Element.prototype.scrollIntoView = scrollIntoViewMock; render( , ); await waitFor(() => { expect(scrollIntoViewMock).toHaveBeenCalled(); }); }); it('handles message selection', () => { render( , ); const message = screen.getByText('Hello, world!'); fireEvent.click(message); // Vérifier que le message est sélectionné (selon l'implémentation) expect(message).toHaveClass('selected'); }); it('handles message reactions', () => { render( , ); const message = screen.getByText('Hello, world!'); const reactionButton = message.querySelector( '[data-testid="reaction-button"]', ); if (reactionButton) { fireEvent.click(reactionButton); expect(screen.getByText('👍')).toBeInTheDocument(); } }); it('handles message editing', async () => { const mockUpdateMessage = vi.fn(); vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [ { id: '1', content: 'Hello, world!', sender: { id: '1', username: 'user1' }, timestamp: '2024-01-01T10:00:00Z', type: 'text', canEdit: true, }, ], currentConversation: 'conv-123', isLoading: false, error: null, updateMessage: mockUpdateMessage, }); render( , ); const message = screen.getByText('Hello, world!'); const editButton = message.querySelector('[data-testid="edit-button"]'); if (editButton) { fireEvent.click(editButton); const editInput = screen.getByDisplayValue('Hello, world!'); fireEvent.change(editInput, { target: { value: 'Hello, updated world!' }, }); fireEvent.keyDown(editInput, { key: 'Enter' }); await waitFor(() => { expect(mockUpdateMessage).toHaveBeenCalledWith( '1', 'Hello, updated world!', ); }); } }); it('handles message deletion', async () => { const mockDeleteMessage = vi.fn(); vi.mocked(require('@/stores/chat').useChatStore).mockReturnValue({ messages: [ { id: '1', content: 'Hello, world!', sender: { id: '1', username: 'user1' }, timestamp: '2024-01-01T10:00:00Z', type: 'text', canDelete: true, }, ], currentConversation: 'conv-123', isLoading: false, error: null, deleteMessage: mockDeleteMessage, }); render( , ); const message = screen.getByText('Hello, world!'); const deleteButton = message.querySelector('[data-testid="delete-button"]'); if (deleteButton) { fireEvent.click(deleteButton); const confirmButton = screen.getByText('Delete'); fireEvent.click(confirmButton); await waitFor(() => { expect(mockDeleteMessage).toHaveBeenCalledWith('1'); }); } }); it('handles keyboard navigation', () => { render( , ); const container = screen.getByTestId('chat-messages'); fireEvent.keyDown(container, { key: 'ArrowUp' }); fireEvent.keyDown(container, { key: 'ArrowDown' }); fireEvent.keyDown(container, { key: 'Enter' }); fireEvent.keyDown(container, { key: 'Escape' }); // Vérifier que les événements sont gérés correctement expect(container).toBeInTheDocument(); }); it('handles window resize', () => { render( , ); // Simuler un redimensionnement de fenêtre fireEvent.resize(window); // Vérifier que le composant gère le redimensionnement expect(screen.getByTestId('chat-messages')).toBeInTheDocument(); }); });