import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { TaskDetailModal } from '../TaskDetailModal'; import { api } from '../../../lib/api'; import { Thought } from '../../../lib/types'; const task: Thought = { id: 'th_1', userId: 'usr_1', content: 'Fenster putzen', tags: [], priority: 'normal', source: 'manual', status: 'converted', isCompleted: false, kanbanStatus: 'todo', dueDate: '2026-10-30T14:30', createdAt: '2026-10-29T08:00:00Z', }; const columns = [ { id: 'todo', name: 'Zu erledigen' }, { id: 'done', name: 'Erledigt', isDone: true }, ]; const noop = vi.fn(async () => {}); const props = (over: Partial> = {}) => ({ task, isOpen: true, onClose: vi.fn(), activeColumns: columns, taskLists: [], onUpdateTitle: noop, onUpdateStatus: noop, onUpdatePriority: noop, onUpdateDueDate: vi.fn(async () => {}), onUpdateTaskList: noop, onUpdateNotes: noop, onToggleSubtask: noop, onAddSubtask: noop, onDeleteSubtask: noop, onConvertToDoc: vi.fn(), onDeleteTask: vi.fn(), ...over, }); describe('TaskDetailModal', () => { it('opens and closes without breaking the hook order', () => { const { rerender, container } = render(); expect(container.firstChild).toBeNull(); rerender(); expect(screen.getByText('Aufgabe bearbeiten')).toBeTruthy(); rerender(); rerender(); expect(screen.getByText('Aufgabe bearbeiten')).toBeTruthy(); }); it('shows date and time separately and removes only the time', () => { const onUpdateDueDate = vi.fn(async () => {}); render(); expect((screen.getByLabelText('Fälligkeitsdatum') as HTMLInputElement).value).toBe('2026-10-30'); expect((screen.getByLabelText('Uhrzeit (optional)') as HTMLInputElement).value).toBe('14:30'); fireEvent.click(screen.getByTitle(/Uhrzeit entfernen/)); expect(onUpdateDueDate).toHaveBeenCalledWith(task, '2026-10-30'); }); it('is view-only for tasks in lists shared with viewer rights', () => { render(); expect(screen.getByText('Aufgabe ansehen')).toBeTruthy(); expect((screen.getByLabelText('Fälligkeitsdatum') as HTMLInputElement).closest('fieldset')?.disabled).toBe(true); }); it('toggles watching a task via POST and DELETE /tasks/{id}/watch', async () => { const watchSpy = vi.spyOn(api, 'watchTask').mockResolvedValue({ watching: true }); const unwatchSpy = vi.spyOn(api, 'unwatchTask').mockResolvedValue({ watching: false }); render(); const btn = screen.getByRole('button', { name: 'Beobachten' }); fireEvent.click(btn); await waitFor(() => expect(watchSpy).toHaveBeenCalledWith('th_1')); expect(screen.getByRole('button', { name: 'Nicht mehr beobachten' })).toBeTruthy(); fireEvent.click(screen.getByRole('button', { name: 'Nicht mehr beobachten' })); await waitFor(() => expect(unwatchSpy).toHaveBeenCalledWith('th_1')); expect(screen.getByRole('button', { name: 'Beobachten' })).toBeTruthy(); }); });