diff --git a/frontend/src/__tests__/ai/SessionList.test.tsx b/frontend/src/__tests__/ai/SessionList.test.tsx index 2dfa3b5..523cec1 100644 --- a/frontend/src/__tests__/ai/SessionList.test.tsx +++ b/frontend/src/__tests__/ai/SessionList.test.tsx @@ -1,8 +1,19 @@ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SessionList } from '@/components/ai/SessionList'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); + +function renderWithProvider(ui: React.ReactElement) { + return render( + {ui} + ); +} + const mockSessions = [ { id: 's1', title: 'First Chat', folder_id: null, agent_id: null, is_sidebar: false, created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-01T00:00:00Z' }, { id: 's2', title: 'Second Chat', folder_id: 'f1', agent_id: null, is_sidebar: false, created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-01T00:00:00Z' }, @@ -46,21 +57,21 @@ beforeEach(() => { describe('SessionList', () => { it('renders session list with sessions', async () => { - render(); + renderWithProvider(); await waitFor(() => { expect(screen.getByText('First Chat')).toBeInTheDocument(); }); }); it('renders folder names', async () => { - render(); + renderWithProvider(); await waitFor(() => { expect(screen.getByText('My Folder')).toBeInTheDocument(); }); }); it('renders sessions inside folders', async () => { - render(); + renderWithProvider(); await waitFor(() => { expect(screen.getByText('Second Chat')).toBeInTheDocument(); }); @@ -68,7 +79,7 @@ describe('SessionList', () => { it('calls onSelectSession when session clicked', async () => { const onSelect = vi.fn(); - render(); + renderWithProvider(); await waitFor(() => { expect(screen.getByText('First Chat')).toBeInTheDocument(); }); @@ -79,13 +90,13 @@ describe('SessionList', () => { it('shows loading state initially', () => { fetchSessionsMock.mockReturnValue(new Promise(() => {})); fetchFoldersMock.mockReturnValue(new Promise(() => {})); - render(); + renderWithProvider(); expect(screen.getByText('Laden...')).toBeInTheDocument(); }); it('displays error when fetch fails', async () => { fetchSessionsMock.mockRejectedValueOnce(new Error('Failed to load')); - render(); + renderWithProvider(); await waitFor(() => { expect(screen.getByText('Failed to load')).toBeInTheDocument(); }); diff --git a/frontend/src/__tests__/calendar/CalendarPage.test.tsx b/frontend/src/__tests__/calendar/CalendarPage.test.tsx index d2c5ce5..6458dff 100644 --- a/frontend/src/__tests__/calendar/CalendarPage.test.tsx +++ b/frontend/src/__tests__/calendar/CalendarPage.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; vi.mock('@/api/calendar', () => ({ fetchCalendars: vi.fn().mockResolvedValue([ @@ -52,37 +53,55 @@ vi.mock('@/components/ui/Toast', () => ({ useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }), })); +vi.mock('@/api/savedFilters', () => ({ + useSavedFilters: () => ({ data: [], isLoading: false, isError: false }), + useCreateSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), + useDeleteSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + import { CalendarPage } from '@/pages/Calendar'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); + +function renderPage() { + return render( + + + + ); +} + describe('CalendarPage', () => { it('renders calendar page container', async () => { - render(); + renderPage(); expect(screen.getByTestId('calendar-page')).toBeInTheDocument(); }); it('renders calendar tree pane', async () => { - render(); + renderPage(); await waitFor(() => { expect(screen.getByTestId('calendar-tree-pane')).toBeInTheDocument(); }); }); it('renders calendar view pane', async () => { - render(); + renderPage(); await waitFor(() => { expect(screen.getByTestId('calendar-view-pane')).toBeInTheDocument(); }); }); it('renders calendar detail pane', async () => { - render(); + renderPage(); await waitFor(() => { expect(screen.getByTestId('calendar-detail-pane')).toBeInTheDocument(); }); }); it('renders CalendarTree component', async () => { - render(); + renderPage(); // CalendarTree is rendered inside both desktop and mobile panes await waitFor(() => { expect(screen.getAllByTestId('calendar-tree-stub').length).toBeGreaterThan(0); @@ -90,19 +109,19 @@ describe('CalendarPage', () => { }); it('renders MonthView by default', async () => { - render(); + renderPage(); await waitFor(() => { expect(screen.getByTestId('month-view-stub')).toBeInTheDocument(); }); }); it('renders mobile calendar tree pane', async () => { - render(); + renderPage(); expect(screen.getByTestId('mobile-calendar-tree-pane')).toBeInTheDocument(); }); it('renders mobile calendar view pane only after navigation', async () => { - render(); + renderPage(); // Mobile view starts on 'tree', so view pane should not be visible initially expect(screen.queryByTestId('mobile-calendar-view-pane')).not.toBeInTheDocument(); }); diff --git a/frontend/src/__tests__/dashboard/Dashboard.test.tsx b/frontend/src/__tests__/dashboard/Dashboard.test.tsx index df4a2a3..14954da 100644 --- a/frontend/src/__tests__/dashboard/Dashboard.test.tsx +++ b/frontend/src/__tests__/dashboard/Dashboard.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { DashboardPage } from '@/pages/Dashboard'; vi.mock('@/api/hooks', () => ({ @@ -22,62 +23,83 @@ vi.mock('@/api/hooks', () => ({ }), })); +vi.mock('@/api/dashboard', () => ({ + useDashboardWidgets: () => ({ + data: { items: [], total: 0 }, + isError: false, + }), +})); + +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); + +function renderPage() { + return render( + + + + + + ); +} + describe('DashboardPage', () => { it('renders dashboard page', () => { - render(); + renderPage(); expect(screen.getByTestId('dashboard-page')).toBeInTheDocument(); }); it('renders page title', () => { - render(); + renderPage(); expect(screen.getByText('Dashboard')).toBeInTheDocument(); }); it('renders stat card for companies', () => { - render(); + renderPage(); expect(screen.getByTestId('stat-companies')).toBeInTheDocument(); }); it('renders stat card for contacts', () => { - render(); + renderPage(); expect(screen.getByTestId('stat-contacts')).toBeInTheDocument(); }); it('renders stat card for active this week', () => { - render(); + renderPage(); expect(screen.getByTestId('stat-active-week')).toBeInTheDocument(); }); it('renders stat card for new this month', () => { - render(); + renderPage(); expect(screen.getByTestId('stat-new-month')).toBeInTheDocument(); }); it('renders correct company count in stat card', () => { - render(); + renderPage(); const statCompanies = screen.getByTestId('stat-companies'); expect(statCompanies).toHaveTextContent('24'); }); it('renders correct contact count in stat card', () => { - render(); + renderPage(); const statContacts = screen.getByTestId('stat-contacts'); expect(statContacts).toHaveTextContent('156'); }); it('renders activity feed', () => { - render(); + renderPage(); expect(screen.getByTestId('activity-feed')).toBeInTheDocument(); }); it('renders activity feed with user names from audit log', () => { - render(); + renderPage(); expect(screen.getByText('anna.schmidt')).toBeInTheDocument(); expect(screen.getByText('max.mustermann')).toBeInTheDocument(); }); it('renders activity feed with action descriptions', () => { - render(); + renderPage(); expect(screen.getByText(/create/)).toBeInTheDocument(); expect(screen.getByText(/update/)).toBeInTheDocument(); expect(screen.getByText(/delete/)).toBeInTheDocument(); diff --git a/frontend/src/__tests__/dms/DmsPage.test.tsx b/frontend/src/__tests__/dms/DmsPage.test.tsx index f3eca79..c438122 100644 --- a/frontend/src/__tests__/dms/DmsPage.test.tsx +++ b/frontend/src/__tests__/dms/DmsPage.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; vi.mock('@/api/dms', () => ({ fetchFolders: vi.fn().mockResolvedValue([ @@ -11,16 +12,36 @@ vi.mock('@/api/dms', () => ({ createFolder: vi.fn().mockResolvedValue({ id: 'f3', name: 'Neuer Ordner', parent_id: null, created_by: 'u1' }), deleteFile: vi.fn().mockResolvedValue(undefined), searchFiles: vi.fn().mockResolvedValue({ files: [], total: 0 }), + fetchFilesByFolder: vi.fn().mockResolvedValue([]), + fetchAllFiles: vi.fn().mockResolvedValue([]), + getSharedWithMe: vi.fn().mockResolvedValue([]), + bulkMoveFiles: vi.fn().mockResolvedValue(undefined), + bulkDeleteFiles: vi.fn().mockResolvedValue(undefined), + updateFolder: vi.fn().mockResolvedValue(undefined), })); vi.mock('@/components/ui/Toast', () => ({ useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() }), })); +vi.mock('@/api/savedFilters', () => ({ + useSavedFilters: () => ({ data: [], isLoading: false, isError: false }), + useCreateSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), + useDeleteSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + import { DmsPage } from '@/pages/Dms'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); + function renderWithRouter() { - return render(); + return render( + + + + ); } beforeEach(() => { @@ -33,55 +54,36 @@ describe('DmsPage', () => { expect(screen.getByTestId('dms-page')).toBeInTheDocument(); }); - it('renders folder tree container', async () => { + it('renders source tree pane', async () => { renderWithRouter(); await waitFor(() => { - expect(screen.getByTestId('folder-tree-container')).toBeInTheDocument(); + expect(screen.getByTestId('dms-source-pane')).toBeInTheDocument(); }); }); - it('renders file grid container', async () => { + it('renders file explorer pane', async () => { renderWithRouter(); await waitFor(() => { - expect(screen.getByTestId('file-grid-container')).toBeInTheDocument(); + expect(screen.getByTestId('dms-explorer-pane')).toBeInTheDocument(); }); }); it('renders folder tree with folders after loading', async () => { renderWithRouter(); await waitFor(() => { - expect(screen.getByText('Vertraege')).toBeInTheDocument(); - expect(screen.getByText('Rechnungen')).toBeInTheDocument(); + // Use getAllByText because folder names appear in both desktop and mobile views + expect(screen.getAllByText('Vertraege').length).toBeGreaterThan(0); + expect(screen.getAllByText('Rechnungen').length).toBeGreaterThan(0); }); }); - it('shows upload button and toggles upload dropzone', async () => { - renderWithRouter(); - const uploadBtn = screen.getByText('Hochladen'); - expect(uploadBtn).toBeInTheDocument(); - fireEvent.click(uploadBtn); - await waitFor(() => { - expect(screen.getByTestId('upload-dropzone')).toBeInTheDocument(); - }); - }); - - it('shows new folder button and toggles form', async () => { - renderWithRouter(); - const folderBtn = screen.getByText('Neuer Ordner'); - expect(folderBtn).toBeInTheDocument(); - fireEvent.click(folderBtn); - await waitFor(() => { - expect(screen.getByTestId('new-folder-form')).toBeInTheDocument(); - }); - }); + // REMOVED: 'shows upload button and toggles upload dropzone' — upload is now a plugin toolbar action, not a direct button with text 'Hochladen' + // REMOVED: 'shows new folder button and toggles form' — new folder is now a plugin toolbar action, not a direct button with text 'Neuer Ordner' it('renders trash link', async () => { renderWithRouter(); expect(screen.getByText('Papierkorb')).toBeInTheDocument(); }); - it('renders search input', async () => { - renderWithRouter(); - expect(screen.getByLabelText('Dateien durchsuchen')).toBeInTheDocument(); - }); + // REMOVED: 'renders search input' — search is now a plugin toolbar action, not a labeled input directly in DmsPage }); diff --git a/frontend/src/__tests__/dms/UploadDropzone.test.tsx b/frontend/src/__tests__/dms/UploadDropzone.test.tsx index ce61f0c..fe52303 100644 --- a/frontend/src/__tests__/dms/UploadDropzone.test.tsx +++ b/frontend/src/__tests__/dms/UploadDropzone.test.tsx @@ -26,7 +26,7 @@ describe('UploadDropzone', () => { it('renders drag-and-drop instruction text', () => { render(); - expect(screen.getByText('Dateien hierher ziehen oder klicken zum Auswaehlen')).toBeInTheDocument(); + expect(screen.getByText('Dateien hierher ziehen oder klicken zum Auswählen')).toBeInTheDocument(); }); it('opens file picker on click', () => { diff --git a/frontend/src/__tests__/mail/MailPage.test.tsx b/frontend/src/__tests__/mail/MailPage.test.tsx index 91736a2..f0fed88 100644 --- a/frontend/src/__tests__/mail/MailPage.test.tsx +++ b/frontend/src/__tests__/mail/MailPage.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; vi.mock('@/api/mail', () => ({ decodeMimeHeader: (s: string) => s, @@ -38,10 +39,24 @@ vi.mock('@/components/ui/Toast', () => ({ useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() }), })); +vi.mock('@/api/savedFilters', () => ({ + useSavedFilters: () => ({ data: [], isLoading: false, isError: false }), + useCreateSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), + useDeleteSavedFilter: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + import { MailPage } from '@/pages/Mail'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); + function renderWithRouter() { - return render(); + return render( + + + + ); } beforeEach(() => { @@ -77,26 +92,9 @@ describe('MailPage', () => { }); }); - it('renders compose button', async () => { - renderWithRouter(); - await waitFor(() => { - expect(screen.getByTestId('compose-btn')).toBeInTheDocument(); - }); - }); - - it('renders shared mailbox selector', async () => { - renderWithRouter(); - await waitFor(() => { - expect(screen.getByTestId('shared-mailbox-selector')).toBeInTheDocument(); - }); - }); - - it('renders mail search bar', async () => { - renderWithRouter(); - await waitFor(() => { - expect(screen.getByTestId('mail-search-bar')).toBeInTheDocument(); - }); - }); + // REMOVED: 'renders compose button' — compose is now a plugin toolbar action, not a direct page element + // REMOVED: 'renders shared mailbox selector' — account selection integrated into folder tree, selector no longer exists + // REMOVED: 'renders mail search bar' — search is now a plugin toolbar action it('renders folders after loading', async () => { renderWithRouter(); @@ -114,23 +112,8 @@ describe('MailPage', () => { }); }); - it('shows compose modal when compose button is clicked', async () => { - renderWithRouter(); - await waitFor(() => { - const btn = screen.getByTestId('compose-btn'); - fireEvent.click(btn); - expect(screen.getByTestId('compose-modal')).toBeInTheDocument(); - }); - }); - - it('shows compose toolbar with bold and italic buttons', async () => { - renderWithRouter(); - await waitFor(() => { - const btn = screen.getByTestId('compose-btn'); - fireEvent.click(btn); - expect(screen.getByTestId('compose-toolbar')).toBeInTheDocument(); - }); - }); + // REMOVED: 'shows compose modal when compose button is clicked' — compose now opens as a window via windowStore, not a modal in the page + // REMOVED: 'shows compose toolbar with bold and italic buttons' — compose form is in a separate window, not directly in MailPage it('renders mail detail empty state initially', async () => { renderWithRouter(); @@ -150,14 +133,5 @@ describe('MailPage', () => { }); }); - it('shows reply and forward buttons in mail detail', async () => { - renderWithRouter(); - await waitFor(() => { - expect(screen.getByText('Test Subject')).toBeInTheDocument(); - }); - fireEvent.click(screen.getByText('Test Subject')); - await waitFor(() => { - expect(screen.getAllByTestId('mail-detail-toolbar').length).toBeGreaterThan(0); - }); - }); + // REMOVED: 'shows reply and forward buttons in mail detail' — reply/forward are now plugin toolbar actions, not elements within mail-detail. mail-detail-toolbar testid no longer exists. }); diff --git a/frontend/src/__tests__/permissions/ShareDialog.test.tsx b/frontend/src/__tests__/permissions/ShareDialog.test.tsx index 40002ae..84c1250 100644 --- a/frontend/src/__tests__/permissions/ShareDialog.test.tsx +++ b/frontend/src/__tests__/permissions/ShareDialog.test.tsx @@ -93,7 +93,7 @@ describe('ShareDialog', () => { render(); const userIdInput = screen.getByLabelText('Benutzer-ID'); fireEvent.change(userIdInput, { target: { value: 'u5' } }); - const addBtn = screen.getByRole('button', { name: 'Freigabe hinzufuegen' }); + const addBtn = screen.getByRole('button', { name: 'Freigabe hinzufügen' }); fireEvent.click(addBtn); await waitFor(() => { expect(shareFile).toHaveBeenCalledWith('file1', { user_id: 'u5', permission: 'read' }); diff --git a/frontend/src/__tests__/settings/SettingsSystem.test.tsx b/frontend/src/__tests__/settings/SettingsSystem.test.tsx index 727cd62..57f11f6 100644 --- a/frontend/src/__tests__/settings/SettingsSystem.test.tsx +++ b/frontend/src/__tests__/settings/SettingsSystem.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SettingsSystemPage } from '@/pages/SettingsSystem'; const mockSettings = { @@ -36,8 +37,47 @@ vi.mock('@/components/ui/Toast', () => ({ useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }), })); +vi.mock('@/api/users', () => ({ + useMenuOrder: () => ({ data: { items: [], order: [] }, isLoading: false, isError: false }), + useUpdateMenuOrder: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + +vi.mock('@/api/settings', () => ({ + useSystemSettings: () => ({ data: mockSettings, isLoading: false }), + useUpdateSystemSettings: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + +vi.mock('@/api/plugins', () => ({ + usePlugins: () => ({ data: [], isLoading: false, isError: false, error: null, refetch: vi.fn() }), + useInstallPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }), + useActivatePlugin: () => ({ mutateAsync: vi.fn(), isPending: false }), + useDeactivatePlugin: () => ({ mutateAsync: vi.fn(), isPending: false }), + useUninstallPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }), + useUploadPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }), + useInstallPluginFromUrl: () => ({ mutateAsync: vi.fn(), isPending: false }), +})); + +vi.mock('@/pages/SettingsMenuOrder', () => ({ + SettingsMenuOrderPage: () =>
, +})); + +vi.mock('@/pages/SettingsTheme', () => ({ + SettingsThemePage: () =>
, +})); + +vi.mock('@/pages/SettingsPlugins', () => ({ + SettingsPluginsPage: () =>
, +})); + function renderPage() { - return render(); + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + return render( + + + + ); } describe('SettingsSystemPage', () => { @@ -46,25 +86,8 @@ describe('SettingsSystemPage', () => { expect(screen.getByTestId('settings-system-page')).toBeInTheDocument(); }); - it('renders company name input with loaded value', () => { - renderPage(); - expect(screen.getByTestId('settings-company-name')).toBeInTheDocument(); - expect(screen.getByDisplayValue('Test GmbH')).toBeInTheDocument(); - }); - - it('renders save button', () => { - renderPage(); - expect(screen.getByTestId('settings-system-save')).toBeInTheDocument(); - }); - - it('renders currency and tax selects', () => { - renderPage(); - const selects = screen.getAllByRole('combobox'); - expect(selects.length).toBeGreaterThanOrEqual(2); - }); - - it('renders payment terms input', () => { - renderPage(); - expect(screen.getByDisplayValue('14')).toBeInTheDocument(); - }); + // REMOVED: 'renders company name input with loaded value' — form fields moved to sub-pages (SettingsStammdaten etc.), no longer directly in SettingsSystem + // REMOVED: 'renders save button' — save button moved to sub-pages + // REMOVED: 'renders currency and tax selects' — selects moved to sub-pages + // REMOVED: 'renders payment terms input' — input moved to sub-pages }); diff --git a/frontend/src/components/comm/blocks/HtmlBlock.tsx b/frontend/src/components/comm/blocks/HtmlBlock.tsx index 184ee56..0927e84 100644 --- a/frontend/src/components/comm/blocks/HtmlBlock.tsx +++ b/frontend/src/components/comm/blocks/HtmlBlock.tsx @@ -13,7 +13,13 @@ const HtmlBlock: React.FC = ({ block }) => { return null; } - const sanitized = DOMPurify.sanitize(rawHtml); + // Replace javascript: URLs in href attributes before sanitizing + const safeHtml = rawHtml.replace( + /href\s*=\s*(["'])\s*javascript:[^"']*\1/gi, + 'href=$1#$1' + ); + + const sanitized = DOMPurify.sanitize(safeHtml); return (
{ expect(screen.getByText(/unknown/)).toBeInTheDocument(); }); - it('renders nothing when manifests have not loaded yet', () => { + it('renders loading spinner when manifests have not loaded yet', () => { usePluginStore.setState({ manifests: [], loaded: false, @@ -104,7 +104,8 @@ describe('PluginRouteRenderer', () => { ); - expect(container.innerHTML).toBe(''); + // Component now shows a loading spinner instead of empty HTML + expect(container.querySelector('[role="status"]')).toBeInTheDocument(); }); it('matches the first route when multiple match', () => { diff --git a/frontend/src/pages/SettingsPlugins.tsx b/frontend/src/pages/SettingsPlugins.tsx index 1e23156..171ef24 100644 --- a/frontend/src/pages/SettingsPlugins.tsx +++ b/frontend/src/pages/SettingsPlugins.tsx @@ -180,7 +180,7 @@ export function SettingsPluginsPage() { const [confirmUninstall, setConfirmUninstall] = useState(null); const [confirmRemoveData, setConfirmRemoveData] = useState(false); - const plugins: Plugin[] = data ?? []; + const plugins: Plugin[] = Array.isArray(data) ? data : (data?.plugins ?? []); const handleInstall = async (plugin: Plugin) => { try { diff --git a/frontend/src/pages/__tests__/Reports.test.tsx b/frontend/src/pages/__tests__/Reports.test.tsx index 2761447..d3d0ad8 100644 --- a/frontend/src/pages/__tests__/Reports.test.tsx +++ b/frontend/src/pages/__tests__/Reports.test.tsx @@ -65,6 +65,7 @@ vi.mock('lucide-react', () => { Building2: Icon, Calendar: Icon, CalendarDays: Icon, + ChevronDown: Icon, Download: Icon, FileText: Icon, Loader2: Icon,