Files
leocrm/frontend/src/__tests__/calendar/KanbanBoard.test.tsx
T
A0-Orchestrator 7350739554 T08b: Frontend Calendar UI (month view, kanban, ICS, resources, sharing)
- 8 calendar components (MonthView, KanbanBoard, AppointmentModal, TaskDetailPanel, IcsControls, ResourceBooking, SharingSettings + API client)

- 2 pages (/calendar, /calendar/kanban) + zustand store

- 17 vitest tests (MonthView 5, KanbanBoard 6, AppointmentModal 6) all passing

- i18n: calendar namespace in en/de (104 lines each, +exportSuccess key)

- TS strict mode pass, npm run build pass
2026-06-30 11:35:08 +02:00

93 lines
3.4 KiB
TypeScript

import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { KanbanBoard } from '@/components/calendar/KanbanBoard';
import type { CalendarEntry } from '@/api/calendar';
function makeTask(id: string, title: string, status: CalendarEntry['status']): CalendarEntry {
return {
id,
calendar_id: 'cal-1',
entry_type: 'task',
subtype: 'normal',
title,
all_day: false,
priority: 'medium',
status,
created_by: 'u-1',
due_date: '2026-07-01T00:00:00.000Z',
};
}
const board = {
open: [makeTask('t-1', 'Recherche', 'open')],
in_progress: [makeTask('t-2', 'Implementierung', 'in_progress')],
done: [makeTask('t-3', 'Tests', 'done')],
cancelled: [],
};
describe('KanbanBoard', () => {
it('renders three columns (open / in_progress / done)', () => {
render(<KanbanBoard board={board} onMove={vi.fn()} />);
expect(screen.getByTestId('kanban-column-open')).toBeInTheDocument();
expect(screen.getByTestId('kanban-column-in_progress')).toBeInTheDocument();
expect(screen.getByTestId('kanban-column-done')).toBeInTheDocument();
});
it('renders task cards inside the correct columns', () => {
render(<KanbanBoard board={board} onMove={vi.fn()} />);
expect(screen.getByTestId('kanban-card-t-1').textContent).toContain('Recherche');
expect(screen.getByTestId('kanban-card-t-2').textContent).toContain('Implementierung');
expect(screen.getByTestId('kanban-card-t-3').textContent).toContain('Tests');
});
it('shows column counts', () => {
render(<KanbanBoard board={board} onMove={vi.fn()} />);
expect(screen.getByTestId('kanban-count-open').textContent).toBe('1');
expect(screen.getByTestId('kanban-count-in_progress').textContent).toBe('1');
expect(screen.getByTestId('kanban-count-done').textContent).toBe('1');
});
it('shows empty state when there are no tasks', () => {
render(
<KanbanBoard
board={{ open: [], in_progress: [], done: [], cancelled: [] }}
onMove={vi.fn()}
/>
);
expect(screen.getByTestId('kanban-empty')).toBeInTheDocument();
});
it('drag-and-drop into another column fires onMove with target status', () => {
const onMove = vi.fn();
render(<KanbanBoard board={board} onMove={onMove} />);
const card = screen.getByTestId('kanban-card-t-1');
const targetCol = screen.getByTestId('kanban-column-done');
const dataTransfer = {
setData: vi.fn(),
getData: (k: string) => {
if (k === 'application/x-kanban-entry') return 't-1';
if (k === 'application/x-kanban-source') return 'open';
return '';
},
types: ['application/x-kanban-entry', 'application/x-kanban-source'],
effectAllowed: '',
dropEffect: '',
};
fireEvent.dragStart(card, { dataTransfer });
fireEvent.dragOver(targetCol, { dataTransfer });
fireEvent.drop(targetCol, { dataTransfer });
expect(onMove).toHaveBeenCalledTimes(1);
const [movedEntry, target] = onMove.mock.calls[0];
expect(movedEntry.id).toBe('t-1');
expect(target).toBe('done');
});
it('clicking a card fires onSelect', () => {
const onSelect = vi.fn();
render(<KanbanBoard board={board} onMove={vi.fn()} onSelect={onSelect} />);
fireEvent.click(screen.getByTestId('kanban-card-t-2'));
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 't-2' }));
});
});