test(05-04): add failing tests for calendar widget

- Three test cases: event rendering with color dots, no-events empty state, no-sources empty state
- Mocks calendar-api and next-intl following existing test patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 15:21:37 +02:00
parent 694bcff4a4
commit 2d8699e45c
@@ -0,0 +1,120 @@
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
// Mock next-intl
vi.mock('next-intl', () => ({
useTranslations: (ns: string) => (key: string) => {
const map: Record<string, Record<string, string>> = {
widgets: {
'calendar.name': 'Calendar',
'calendar.description': 'Upcoming events',
'calendar.emptyNoSources': 'No calendar sources configured',
'calendar.emptyNoEvents': 'No upcoming events',
'calendar.loading': 'Loading...',
},
};
return map[ns]?.[key] ?? key;
},
}));
// Mock calendar-api
const mockFetchEvents = vi.fn();
const mockFetchSources = vi.fn();
vi.mock('@/lib/calendar-api', () => ({
fetchEvents: (...args: unknown[]) => mockFetchEvents(...args),
fetchSources: (...args: unknown[]) => mockFetchSources(...args),
}));
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
describe('CalendarWidget', () => {
beforeEach(() => {
// Default: sources exist so we get past the source check
mockFetchSources.mockResolvedValue([
{ id: 's1', name: 'Work', type: 'caldav', isVisible: true, color: '#ff0000' },
]);
});
it('renders event titles, times, and source color dots for two events', async () => {
mockFetchEvents.mockResolvedValue([
{
id: 'e1',
sourceId: 's1',
title: 'Team Meeting',
start: '2026-06-24T10:00:00Z',
end: '2026-06-24T11:00:00Z',
allDay: false,
color: '#ff0000',
},
{
id: 'e2',
sourceId: 's1',
title: 'Lunch Break',
start: '2026-06-24T12:00:00Z',
end: '2026-06-24T13:00:00Z',
allDay: false,
color: '#00ff00',
},
]);
const { CalendarWidget } = await import('./calendar-widget');
render(
<CalendarWidget
instanceId="cal-1"
config={{}}
isEditMode={false}
/>,
);
// Wait for events to load
await waitFor(() => {
expect(screen.getByText('Team Meeting')).toBeInTheDocument();
});
expect(screen.getByText('Lunch Break')).toBeInTheDocument();
// Source color dots should be rendered (8px circles)
const colorDots = screen.getAllByTestId('event-color-dot');
expect(colorDots).toHaveLength(2);
});
it('renders no-events message when sources exist but no events returned', async () => {
mockFetchEvents.mockResolvedValue([]);
const { CalendarWidget } = await import('./calendar-widget');
render(
<CalendarWidget
instanceId="cal-2"
config={{}}
isEditMode={false}
/>,
);
await waitFor(() => {
expect(screen.getByText('No upcoming events')).toBeInTheDocument();
});
});
it('renders no-sources message when no sources configured', async () => {
mockFetchSources.mockResolvedValue([]);
mockFetchEvents.mockResolvedValue([]);
const { CalendarWidget } = await import('./calendar-widget');
render(
<CalendarWidget
instanceId="cal-3"
config={{}}
isEditMode={false}
/>,
);
await waitFor(() => {
expect(
screen.getByText('No calendar sources configured'),
).toBeInTheDocument();
});
});
});