import React from 'react'; import { render, screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BrowserRouter } from 'react-router-dom'; import TaskList from '../../components/Task/TaskList'; import TaskModal from '../../components/Task/TaskModal'; import { Task } from '../../entities/Task'; import { Project } from '../../entities/Project'; // Mock data const mockTasks: Task[] = [ { id: 1, name: 'Complete project setup', status: 'not_started', priority: 'high', created_at: '2024-01-01T00:00:00.000Z' }, { id: 2, name: 'Write documentation', status: 'in_progress', priority: 'medium', due_date: '2024-12-25', created_at: '2024-01-02T00:00:00.000Z' } ]; const mockProjects: Project[] = [ { id: 1, name: 'Test Project', active: true } ]; // Integration test wrapper component const TaskWorkflowWrapper: React.FC<{ tasks: Task[]; onTaskUpdate: (task: Task) => void; onTaskDelete: (taskId: number) => void; }> = ({ tasks, onTaskUpdate, onTaskDelete }) => { const [isModalOpen, setIsModalOpen] = React.useState(false); const [selectedTask, setSelectedTask] = React.useState(null); const handleTaskClick = (task: Task) => { setSelectedTask(task); setIsModalOpen(true); }; const handleModalSave = (updatedTask: Task) => { onTaskUpdate(updatedTask); setIsModalOpen(false); }; const handleModalClose = () => { setIsModalOpen(false); setSelectedTask(null); }; const handleModalDelete = (taskId: number) => { onTaskDelete(taskId); setIsModalOpen(false); setSelectedTask(null); }; const CustomTaskList: React.FC = (props) => (
{props.tasks.map((task: Task) => (
handleTaskClick(task)} style={{ cursor: 'pointer', padding: '10px', border: '1px solid #ccc', margin: '5px' }}>
{task.name}
Status: {task.status}
Priority: {task.priority}
))}
); return (
{isModalOpen && selectedTask && ( ({ id: 999, name, active: true })} /> )}
); }; describe('Task Workflows Integration Tests', () => { let mockOnTaskUpdate: jest.Mock; let mockOnTaskDelete: jest.Mock; beforeEach(() => { mockOnTaskUpdate = jest.fn(); mockOnTaskDelete = jest.fn(); jest.clearAllMocks(); // Mock fetch for tags service (global.fetch as jest.Mock).mockResolvedValue({ ok: true, json: async () => ({ tags: [] }) }); }); it('completes full task lifecycle workflow', async () => { const user = userEvent.setup(); render( ); // 1. View tasks in list expect(screen.getByText('Complete project setup')).toBeInTheDocument(); expect(screen.getByText('Write documentation')).toBeInTheDocument(); // 2. Click on a task to open modal const firstTask = screen.getByText('Complete project setup'); await act(async () => { await user.click(firstTask); }); // 3. Modal should open with task details await waitFor(() => { expect(screen.getByDisplayValue('Complete project setup')).toBeInTheDocument(); }); // 4. Edit task name const nameInput = screen.getByDisplayValue('Complete project setup'); await act(async () => { await user.clear(nameInput); await user.type(nameInput, 'Updated project setup'); }); // 5. Save changes const saveButton = screen.getByText(/save/i); await act(async () => { await user.click(saveButton); }); // 6. Verify onTaskUpdate was called expect(mockOnTaskUpdate).toHaveBeenCalledWith( expect.objectContaining({ id: 1, name: 'Updated project setup' }) ); }); it('handles task deletion workflow', async () => { const user = userEvent.setup(); render( ); // 1. Click on a task to open modal const firstTask = screen.getByText('Complete project setup'); await act(async () => { await user.click(firstTask); }); // 2. Click delete button await waitFor(() => { expect(screen.getByText(/delete/i)).toBeInTheDocument(); }); const deleteButton = screen.getByText(/delete/i); await act(async () => { await user.click(deleteButton); }); // 3. Confirm deletion await waitFor(() => { expect(screen.getByText(/are you sure/i)).toBeInTheDocument(); }); const confirmButton = screen.getByText(/confirm/i); await act(async () => { await user.click(confirmButton); }); // 4. Verify onTaskDelete was called expect(mockOnTaskDelete).toHaveBeenCalledWith(1); }); it('handles task status and priority updates', async () => { const user = userEvent.setup(); render( ); // Open task modal const secondTask = screen.getByText('Write documentation'); await act(async () => { await user.click(secondTask); }); await waitFor(() => { expect(screen.getByDisplayValue('Write documentation')).toBeInTheDocument(); }); // Change status and priority using dropdowns const statusDropdown = screen.getByText(/status/i); expect(statusDropdown).toBeInTheDocument(); const priorityDropdown = screen.getByText(/priority/i); expect(priorityDropdown).toBeInTheDocument(); // Save changes const saveButton = screen.getByText(/save/i); await act(async () => { await user.click(saveButton); }); // Verify update was called expect(mockOnTaskUpdate).toHaveBeenCalled(); }); it('handles due date modifications', async () => { const user = userEvent.setup(); render( ); // Open task with due date const taskWithDueDate = screen.getByText('Write documentation'); await act(async () => { await user.click(taskWithDueDate); }); await waitFor(() => { expect(screen.getByDisplayValue('2024-12-25')).toBeInTheDocument(); }); // Update due date const dueDateInput = screen.getByDisplayValue('2024-12-25'); await act(async () => { await user.clear(dueDateInput); await user.type(dueDateInput, '2024-12-31'); }); // Save changes const saveButton = screen.getByText(/save/i); await act(async () => { await user.click(saveButton); }); // Verify update with new due date expect(mockOnTaskUpdate).toHaveBeenCalledWith( expect.objectContaining({ id: 2, due_date: '2024-12-31' }) ); }); it('handles modal cancellation without saving', async () => { const user = userEvent.setup(); render( ); // Open task modal const firstTask = screen.getByText('Complete project setup'); await act(async () => { await user.click(firstTask); }); await waitFor(() => { expect(screen.getByDisplayValue('Complete project setup')).toBeInTheDocument(); }); // Make changes const nameInput = screen.getByDisplayValue('Complete project setup'); await act(async () => { await user.clear(nameInput); await user.type(nameInput, 'Changed but not saved'); }); // Cancel instead of saving const cancelButton = screen.getByText(/cancel/i); await act(async () => { await user.click(cancelButton); }); // Verify no update was called expect(mockOnTaskUpdate).not.toHaveBeenCalled(); }); it('handles empty task list state', () => { render( ); // Should render empty container expect(screen.queryByText('Complete project setup')).not.toBeInTheDocument(); expect(screen.queryByText('Write documentation')).not.toBeInTheDocument(); }); it('maintains task selection state during editing', async () => { const user = userEvent.setup(); render( ); // Open first task const firstTask = screen.getByText('Complete project setup'); await act(async () => { await user.click(firstTask); }); await waitFor(() => { expect(screen.getByDisplayValue('Complete project setup')).toBeInTheDocument(); }); // Verify correct task is loaded expect(screen.getByDisplayValue('Complete project setup')).toBeInTheDocument(); expect(screen.queryByDisplayValue('Write documentation')).not.toBeInTheDocument(); }); it('handles multiple rapid task interactions', async () => { const user = userEvent.setup(); render( ); // Rapidly interact with tasks const firstTask = screen.getByText('Complete project setup'); const secondTask = screen.getByText('Write documentation'); // Click first task multiple times await act(async () => { await user.click(firstTask); }); await waitFor(() => { expect(screen.getByDisplayValue('Complete project setup')).toBeInTheDocument(); }); // Close modal const cancelButton = screen.getByText(/cancel/i); await act(async () => { await user.click(cancelButton); }); // Should close modal properly await waitFor(() => { expect(screen.queryByDisplayValue('Complete project setup')).not.toBeInTheDocument(); }); }); });