Files
leocrm/frontend/src/__tests__/calendar/MonthView.test.tsx
T

139 lines
4.3 KiB
TypeScript
Raw Normal View History

import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { MonthView } from '@/components/calendar/MonthView';
import type { CalendarEntry } from '@/api/calendar';
const sampleEntries: CalendarEntry[] = [
{
id: 'a-1',
calendar_id: 'cal-1',
entry_type: 'appointment',
subtype: 'normal',
title: 'Team-Meeting',
start_at: '2026-06-15T09:00:00.000Z',
end_at: '2026-06-15T10:00:00.000Z',
all_day: false,
priority: 'medium',
status: 'open',
created_by: 'u-1',
},
{
id: 'a-2',
calendar_id: 'cal-1',
entry_type: 'appointment',
subtype: 'normal',
title: 'Kundentermin',
start_at: '2026-06-22T14:00:00.000Z',
end_at: '2026-06-22T15:00:00.000Z',
all_day: false,
priority: 'high',
status: 'open',
created_by: 'u-1',
},
];
describe('MonthView', () => {
it('renders the month grid with header', () => {
render(
<MonthView
visibleMonth={new Date(2026, 5, 1)}
entries={[]}
onCreateAt={vi.fn()}
onEditEntry={vi.fn()}
onMoveEntry={vi.fn()}
/>
);
expect(screen.getByTestId('month-view')).toBeInTheDocument();
expect(screen.getByTestId('month-view-grid')).toBeInTheDocument();
// Header includes month name and year
expect(screen.getByTestId('month-view-header').textContent).toContain('2026');
});
it('renders all 42 day cells (6 weeks × 7 days)', () => {
render(
<MonthView
visibleMonth={new Date(2026, 5, 1)}
entries={[]}
onCreateAt={vi.fn()}
onEditEntry={vi.fn()}
onMoveEntry={vi.fn()}
/>
);
const cells = screen.getAllByRole('button', { hidden: false }).filter((el) =>
el.getAttribute('data-testid')?.startsWith('month-cell-')
);
expect(cells.length).toBe(42);
});
it('renders an entry chip for each appointment and clicking it fires onEditEntry', () => {
const onEdit = vi.fn();
render(
<MonthView
visibleMonth={new Date(2026, 5, 1)}
entries={sampleEntries}
onCreateAt={vi.fn()}
onEditEntry={onEdit}
onMoveEntry={vi.fn()}
/>
);
expect(screen.getByTestId('month-entry-a-1')).toBeInTheDocument();
expect(screen.getByTestId('month-entry-a-2')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('month-entry-a-1'));
expect(onEdit).toHaveBeenCalledWith(
expect.objectContaining({ id: 'a-1', title: 'Team-Meeting' })
);
});
it('clicking an empty cell fires onCreateAt with the day Date', () => {
const onCreate = vi.fn();
render(
<MonthView
visibleMonth={new Date(2026, 5, 1)}
entries={[]}
onCreateAt={onCreate}
onEditEntry={vi.fn()}
onMoveEntry={vi.fn()}
/>
);
// Pick the first cell of the grid
const cells = screen.getAllByRole('button', { hidden: false }).filter((el) =>
el.getAttribute('data-testid')?.startsWith('month-cell-')
);
fireEvent.click(cells[0]);
expect(onCreate).toHaveBeenCalledTimes(1);
const arg = onCreate.mock.calls[0][0] as Date;
expect(arg).toBeInstanceOf(Date);
});
it('drag-and-drop fires onMoveEntry with the new start time-of-day preserved', () => {
const onMove = vi.fn();
const { container } = render(
<MonthView
visibleMonth={new Date(2026, 5, 1)}
entries={sampleEntries}
onCreateAt={vi.fn()}
onEditEntry={vi.fn()}
onMoveEntry={onMove}
/>
);
const entryEl = screen.getByTestId('month-entry-a-1');
const cells = container.querySelectorAll('[data-testid^="month-cell-"]');
// Pick a cell that's clearly different from June 15
const targetCell = cells[20] as HTMLElement;
const dataTransfer = {
setData: vi.fn(),
getData: (k: string) => (k === 'application/x-calendar-entry' ? 'a-1' : ''),
types: ['application/x-calendar-entry'],
effectAllowed: '',
dropEffect: '',
};
fireEvent.dragStart(entryEl, { dataTransfer });
fireEvent.dragOver(targetCell, { dataTransfer });
fireEvent.drop(targetCell, { dataTransfer });
expect(onMove).toHaveBeenCalledTimes(1);
const [, newStart] = onMove.mock.calls[0];
expect(newStart).toBeInstanceOf(Date);
});
});