mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 20:43:41 +00:00
test: add page tests for Dashboard, Disclosures, AdminAudit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
36ffe43f18
commit
e7f7817ebb
3 changed files with 302 additions and 0 deletions
80
frontend/src/pages/__tests__/AdminAuditPage.test.tsx
Normal file
80
frontend/src/pages/__tests__/AdminAuditPage.test.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
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 { AdminAuditPage } from '@/pages/AdminAuditPage'
|
||||
|
||||
describe('AdminAuditPage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('access_token', 'test-token')
|
||||
})
|
||||
|
||||
it('renders audit log entries', async () => {
|
||||
renderWithProviders(<AdminAuditPage />)
|
||||
|
||||
await waitFor(() => {
|
||||
// mockAuditLogEntry has action 'case.update'
|
||||
expect(screen.getByText('case.update')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Should show entity info: entity_type 'case' + entity_id '1'
|
||||
expect(screen.getByText('case #1')).toBeInTheDocument()
|
||||
|
||||
// Should show user_id = 1
|
||||
expect(screen.getByText('1')).toBeInTheDocument()
|
||||
|
||||
// Should show the IP address
|
||||
expect(screen.getByText('10.10.181.104')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows filter controls', async () => {
|
||||
renderWithProviders(<AdminAuditPage />)
|
||||
|
||||
// Filter section title
|
||||
expect(screen.getByText('Filter')).toBeInTheDocument()
|
||||
|
||||
// Filter input labels
|
||||
expect(screen.getByLabelText('Benutzer-ID')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Aktion')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Datum von')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Datum bis')).toBeInTheDocument()
|
||||
|
||||
// Filter buttons
|
||||
expect(screen.getByText('Filtern')).toBeInTheDocument()
|
||||
expect(screen.getByText('Zurücksetzen')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('handles empty audit log list', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', () => {
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<AdminAuditPage />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Audit-Einträge gefunden.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows loading state', () => {
|
||||
// Delay the API response to observe loading state
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<AdminAuditPage />)
|
||||
|
||||
// The heading is always visible
|
||||
expect(screen.getByText('Audit-Log')).toBeInTheDocument()
|
||||
|
||||
// During loading, neither the empty message nor table data should appear
|
||||
expect(screen.queryByText('Keine Audit-Einträge gefunden.')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('case.update')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
148
frontend/src/pages/__tests__/DashboardPage.test.tsx
Normal file
148
frontend/src/pages/__tests__/DashboardPage.test.tsx
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import { describe, it, expect, beforeEach, vi } 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 { DashboardPage } from '@/pages/DashboardPage'
|
||||
|
||||
// Recharts ResponsiveContainer needs ResizeObserver as a proper constructor
|
||||
class ResizeObserverStub {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
|
||||
|
||||
// Mock recharts to avoid SVG rendering issues in jsdom
|
||||
// We render a simplified version that outputs the data as text
|
||||
vi.mock('recharts', async () => {
|
||||
const actual = await vi.importActual<typeof import('recharts')>('recharts')
|
||||
return {
|
||||
...actual,
|
||||
ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-testid="responsive-container">{children}</div>
|
||||
),
|
||||
PieChart: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-testid="pie-chart">{children}</div>
|
||||
),
|
||||
BarChart: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-testid="bar-chart">{children}</div>
|
||||
),
|
||||
Pie: ({ data }: { data?: Array<{ name: string; value: number }> }) => (
|
||||
<div data-testid="pie">
|
||||
{data?.map((entry) => (
|
||||
<span key={entry.name}>{entry.name}</span>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
Bar: () => <div data-testid="bar" />,
|
||||
Cell: () => <div />,
|
||||
XAxis: () => <div />,
|
||||
YAxis: () => <div />,
|
||||
CartesianGrid: () => <div />,
|
||||
Tooltip: () => <div />,
|
||||
Legend: () => <div />,
|
||||
}
|
||||
})
|
||||
|
||||
describe('DashboardPage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('access_token', 'test-token')
|
||||
})
|
||||
|
||||
it('renders KPI cards with correct values once loaded', async () => {
|
||||
renderWithProviders(<DashboardPage />)
|
||||
|
||||
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(<DashboardPage />)
|
||||
|
||||
// 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(<DashboardPage />)
|
||||
|
||||
// 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(<DashboardPage />)
|
||||
|
||||
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(<DashboardPage />)
|
||||
|
||||
// When data is null and loading is false, it shows the fallback message
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Daten verfügbar.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
74
frontend/src/pages/__tests__/DisclosuresPage.test.tsx
Normal file
74
frontend/src/pages/__tests__/DisclosuresPage.test.tsx
Normal file
|
|
@ -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(<DisclosuresPage />)
|
||||
|
||||
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(<DisclosuresPage />)
|
||||
|
||||
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(<DisclosuresPage />)
|
||||
|
||||
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(<DisclosuresPage />)
|
||||
|
||||
// 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()
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue