('recharts')
+ return {
+ ...actual,
+ ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ PieChart: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ BarChart: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ Pie: ({ data }: { data?: Array<{ name: string; value: number }> }) => (
+
+ {data?.map((entry) => (
+ {entry.name}
+ ))}
+
+ ),
+ Bar: () => ,
+ Cell: () => ,
+ XAxis: () => ,
+ YAxis: () => ,
+ CartesianGrid: () => ,
+ Tooltip: () => ,
+ Legend: () => ,
+ }
+})
+
+describe('DashboardPage', () => {
+ beforeEach(() => {
+ localStorage.setItem('access_token', 'test-token')
+ })
+
+ it('renders KPI cards with correct values once loaded', async () => {
+ renderWithProviders()
+
+ await waitFor(() => {
+ expect(screen.getByText('Fälle gesamt')).toBeInTheDocument()
+ })
+
+ // Check all four KPI card titles
+ expect(screen.getByText('Offene ICD')).toBeInTheDocument()
+ expect(screen.getByText('Offene Codierung')).toBeInTheDocument()
+ expect(screen.getByText('Gutachten gesamt')).toBeInTheDocument()
+
+ // Check KPI values from mockDashboardKPIs (142, 18, 7, 89)
+ expect(screen.getByText('142')).toBeInTheDocument()
+ expect(screen.getByText('18')).toBeInTheDocument()
+ expect(screen.getByText('7')).toBeInTheDocument()
+ expect(screen.getByText('89')).toBeInTheDocument()
+ })
+
+ it('shows year selector', async () => {
+ renderWithProviders()
+
+ // The Select trigger should show the current year
+ const currentYear = new Date().getFullYear()
+ await waitFor(() => {
+ expect(screen.getByText(String(currentYear))).toBeInTheDocument()
+ })
+
+ // The heading should be present
+ expect(screen.getByText('Dashboard')).toBeInTheDocument()
+ })
+
+ it('shows loading skeleton initially', () => {
+ // Delay the API response so we can observe the loading state
+ server.use(
+ http.get('/api/reports/dashboard', async () => {
+ await new Promise((resolve) => setTimeout(resolve, 500))
+ return HttpResponse.json({ kpis: {}, weekly: [] })
+ }),
+ )
+
+ renderWithProviders()
+
+ // During loading, the KPI titles should not be visible yet
+ expect(screen.queryByText('Fälle gesamt')).not.toBeInTheDocument()
+
+ // The heading is always visible
+ expect(screen.getByText('Dashboard')).toBeInTheDocument()
+ })
+
+ it('filters 0-value Fallgruppen from pie chart data', async () => {
+ // The mock data has: onko: 65, ortho: 42, kardio: 20, neuro: 15
+ // FALLGRUPPEN_LABELS maps onko->Onkologie, kardio->Kardiologie
+ // ortho and neuro are not in FALLGRUPPEN_LABELS so they use the raw key
+ // All values are > 0, so all should appear in the pie chart
+ // sd, intensiv, galle are NOT in mock data, so they should not appear
+ renderWithProviders()
+
+ await waitFor(() => {
+ expect(screen.getByText('Fälle gesamt')).toBeInTheDocument()
+ })
+
+ // The chart section title should appear
+ expect(screen.getByText('Fallgruppen')).toBeInTheDocument()
+
+ // Onkologie and Kardiologie should appear (mapped from FALLGRUPPEN_LABELS)
+ expect(screen.getByText('Onkologie')).toBeInTheDocument()
+ expect(screen.getByText('Kardiologie')).toBeInTheDocument()
+
+ // ortho and neuro use raw keys since they are not in FALLGRUPPEN_LABELS
+ expect(screen.getByText('ortho')).toBeInTheDocument()
+ expect(screen.getByText('neuro')).toBeInTheDocument()
+
+ // Schilddrüse should NOT appear (sd is not in mock data)
+ expect(screen.queryByText('Schilddrüse')).not.toBeInTheDocument()
+ // Intensivmedizin should NOT appear (intensiv is not in mock data)
+ expect(screen.queryByText('Intensivmedizin')).not.toBeInTheDocument()
+ // Gallenblase should NOT appear (galle is not in mock data)
+ expect(screen.queryByText('Gallenblase')).not.toBeInTheDocument()
+ })
+
+ it('handles error state when API returns 500', async () => {
+ server.use(
+ http.get('/api/reports/dashboard', () => {
+ return new HttpResponse(null, { status: 500 })
+ }),
+ )
+
+ renderWithProviders()
+
+ // When data is null and loading is false, it shows the fallback message
+ await waitFor(() => {
+ expect(screen.getByText('Keine Daten verfügbar.')).toBeInTheDocument()
+ })
+ })
+})
diff --git a/frontend/src/pages/__tests__/DisclosuresPage.test.tsx b/frontend/src/pages/__tests__/DisclosuresPage.test.tsx
new file mode 100644
index 0000000..5ca8797
--- /dev/null
+++ b/frontend/src/pages/__tests__/DisclosuresPage.test.tsx
@@ -0,0 +1,74 @@
+import { describe, it, expect, beforeEach } from 'vitest'
+import { screen, waitFor } from '@testing-library/react'
+import { http, HttpResponse } from 'msw'
+import { server } from '@/test/mocks/server'
+import { renderWithProviders } from '@/test/utils'
+import { DisclosuresPage } from '@/pages/DisclosuresPage'
+
+describe('DisclosuresPage', () => {
+ beforeEach(() => {
+ localStorage.setItem('access_token', 'test-token')
+ })
+
+ it('renders disclosure requests list', async () => {
+ renderWithProviders()
+
+ await waitFor(() => {
+ // The mock handler returns [mockDisclosureRequest] with fall_id '2026-06-onko-A123456789'
+ expect(screen.getByText('Fall 2026-06-onko-A123456789')).toBeInTheDocument()
+ })
+
+ // Should show requester username
+ expect(screen.getByText('dak_user')).toBeInTheDocument()
+
+ // Should show the reason
+ expect(screen.getByText('Benötige vollständige Patientendaten für Rückruf.')).toBeInTheDocument()
+
+ // Should show the status badge
+ expect(screen.getByText('pending')).toBeInTheDocument()
+ })
+
+ it('shows approve and reject buttons', async () => {
+ renderWithProviders()
+
+ await waitFor(() => {
+ expect(screen.getByText('Genehmigen')).toBeInTheDocument()
+ })
+
+ expect(screen.getByText('Ablehnen')).toBeInTheDocument()
+ })
+
+ it('handles empty disclosure list', async () => {
+ server.use(
+ http.get('/api/admin/disclosure-requests', () => {
+ return HttpResponse.json([])
+ }),
+ )
+
+ renderWithProviders()
+
+ await waitFor(() => {
+ expect(screen.getByText('Keine offenen Anfragen.')).toBeInTheDocument()
+ })
+ })
+
+ it('shows loading state', () => {
+ // Delay the API response to observe loading state
+ server.use(
+ http.get('/api/admin/disclosure-requests', async () => {
+ await new Promise((resolve) => setTimeout(resolve, 500))
+ return HttpResponse.json([])
+ }),
+ )
+
+ renderWithProviders()
+
+ // The heading is always visible
+ expect(screen.getByText('Freigabe-Anfragen')).toBeInTheDocument()
+
+ // During loading, the "Keine offenen Anfragen." message should not be visible yet
+ expect(screen.queryByText('Keine offenen Anfragen.')).not.toBeInTheDocument()
+ // Nor should any disclosure item be visible
+ expect(screen.queryByText('Genehmigen')).not.toBeInTheDocument()
+ })
+})