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