test(05-04): add failing tests for calendar settings panel
- Three test cases: source list with name/type/visibility, visibility toggle calls updateSource, form validation - Mocks calendar-api functions following existing test patterns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
|||||||
|
import { cleanup, render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||||
|
import { afterEach, 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.addSource': 'Add source',
|
||||||
|
'calendar.sourceEmpty': 'No calendar sources configured. Add a source to display events.',
|
||||||
|
'calendar.connectionSuccess': 'Connection successful',
|
||||||
|
'calendar.connectionError': 'Connection failed. Please check the URL and credentials.',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
categoryCalendar: 'Calendar',
|
||||||
|
sourceDeleteConfirm: 'Are you sure you want to delete this calendar source?',
|
||||||
|
sourceDeleteCta: 'Delete source',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return map[ns]?.[key] ?? key;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock calendar-api
|
||||||
|
const mockFetchSources = vi.fn();
|
||||||
|
const mockUpdateSource = vi.fn();
|
||||||
|
const mockAddSource = vi.fn();
|
||||||
|
const mockDeleteSource = vi.fn();
|
||||||
|
const mockTestSource = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('@/lib/calendar-api', () => ({
|
||||||
|
fetchSources: (...args: unknown[]) => mockFetchSources(...args),
|
||||||
|
updateSource: (...args: unknown[]) => mockUpdateSource(...args),
|
||||||
|
addSource: (...args: unknown[]) => mockAddSource(...args),
|
||||||
|
deleteSource: (...args: unknown[]) => mockDeleteSource(...args),
|
||||||
|
testSource: (...args: unknown[]) => mockTestSource(...args),
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CalendarSettingsPanel', () => {
|
||||||
|
it('lists two sources with name, type badge, and visibility toggle', async () => {
|
||||||
|
mockFetchSources.mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: 's1',
|
||||||
|
name: 'Work Calendar',
|
||||||
|
type: 'caldav',
|
||||||
|
isVisible: true,
|
||||||
|
color: '#ff0000',
|
||||||
|
lastSyncError: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 's2',
|
||||||
|
name: 'Holidays',
|
||||||
|
type: 'ics',
|
||||||
|
isVisible: false,
|
||||||
|
color: '#00ff00',
|
||||||
|
lastSyncError: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { CalendarSettingsPanel } = await import(
|
||||||
|
'@/components/settings/calendar-settings-panel'
|
||||||
|
);
|
||||||
|
render(<CalendarSettingsPanel />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Work Calendar')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText('Holidays')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Type badges
|
||||||
|
expect(screen.getByText('CalDAV')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('ICS')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Visibility toggles (checkboxes or switches)
|
||||||
|
const toggles = screen.getAllByRole('switch');
|
||||||
|
expect(toggles).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls updateSource with isVisible when toggling a source', async () => {
|
||||||
|
mockFetchSources.mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: 's1',
|
||||||
|
name: 'Work Calendar',
|
||||||
|
type: 'caldav',
|
||||||
|
isVisible: true,
|
||||||
|
color: '#ff0000',
|
||||||
|
lastSyncError: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
mockUpdateSource.mockResolvedValue({
|
||||||
|
id: 's1',
|
||||||
|
name: 'Work Calendar',
|
||||||
|
type: 'caldav',
|
||||||
|
isVisible: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { CalendarSettingsPanel } = await import(
|
||||||
|
'@/components/settings/calendar-settings-panel'
|
||||||
|
);
|
||||||
|
render(<CalendarSettingsPanel />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Work Calendar')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggle = screen.getByRole('switch');
|
||||||
|
fireEvent.click(toggle);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockUpdateSource).toHaveBeenCalledWith('s1', { isVisible: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires name, type, and url before enabling add form submit', async () => {
|
||||||
|
mockFetchSources.mockResolvedValue([]);
|
||||||
|
|
||||||
|
const { CalendarSettingsPanel } = await import(
|
||||||
|
'@/components/settings/calendar-settings-panel'
|
||||||
|
);
|
||||||
|
render(<CalendarSettingsPanel />);
|
||||||
|
|
||||||
|
// Wait for empty state then click add button
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText('No calendar sources configured. Add a source to display events.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click the add source button
|
||||||
|
const addButton = screen.getByText('Add source');
|
||||||
|
fireEvent.click(addButton);
|
||||||
|
|
||||||
|
// Find the submit button in the form
|
||||||
|
await waitFor(() => {
|
||||||
|
const submitButton = screen.getByTestId('source-form-submit');
|
||||||
|
expect(submitButton).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user