mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 20:43:41 +00:00
test: add page tests for AdminUsers, Reports, Login
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7f7817ebb
commit
ba3f930e4d
3 changed files with 274 additions and 0 deletions
94
frontend/src/pages/__tests__/AdminUsersPage.test.tsx
Normal file
94
frontend/src/pages/__tests__/AdminUsersPage.test.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from '@/test/mocks/server'
|
||||
import { renderWithProviders } from '@/test/utils'
|
||||
import { AdminUsersPage } from '@/pages/AdminUsersPage'
|
||||
|
||||
describe('AdminUsersPage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('access_token', 'test-token')
|
||||
})
|
||||
|
||||
it('renders user list with user data', async () => {
|
||||
renderWithProviders(<AdminUsersPage />, { initialRoute: '/admin/users' })
|
||||
|
||||
await waitFor(() => {
|
||||
// mockUserResponse has username 'admin_user', email 'admin@dak.de', role 'admin'
|
||||
expect(screen.getByText('admin_user')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
expect(screen.getByText('admin@dak.de')).toBeInTheDocument()
|
||||
expect(screen.getByText('Admin')).toBeInTheDocument()
|
||||
// "Aktiv" appears both as table header and badge; check for at least 2
|
||||
const aktivElements = screen.getAllByText('Aktiv')
|
||||
expect(aktivElements.length).toBeGreaterThanOrEqual(2)
|
||||
})
|
||||
|
||||
it('shows "Neuen Benutzer erstellen" button and opens dialog', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderWithProviders(<AdminUsersPage />, { initialRoute: '/admin/users' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Neuen Benutzer erstellen')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Click the create button to open the dialog
|
||||
await user.click(screen.getByText('Neuen Benutzer erstellen'))
|
||||
|
||||
// Dialog should open with form fields
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Benutzername')).toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByLabelText('E-Mail')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Passwort')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('handles empty user list', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/users', () => {
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<AdminUsersPage />, { initialRoute: '/admin/users' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Benutzer gefunden.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows loading state', () => {
|
||||
server.use(
|
||||
http.get('/api/admin/users', async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<AdminUsersPage />, { initialRoute: '/admin/users' })
|
||||
|
||||
// Heading is always visible
|
||||
expect(screen.getByText('Benutzer')).toBeInTheDocument()
|
||||
|
||||
// During loading, user data should not be visible yet
|
||||
expect(screen.queryByText('admin_user')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Keine Benutzer gefunden.')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows error state on API failure', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/users', () => {
|
||||
return new HttpResponse(null, { status: 500 })
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<AdminUsersPage />, { initialRoute: '/admin/users' })
|
||||
|
||||
// When the query fails, users defaults to [] so it should show empty state
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Benutzer gefunden.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
94
frontend/src/pages/__tests__/LoginPage.test.tsx
Normal file
94
frontend/src/pages/__tests__/LoginPage.test.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from '@/test/mocks/server'
|
||||
import { renderWithProviders } from '@/test/utils'
|
||||
import { LoginPage } from '@/pages/LoginPage'
|
||||
|
||||
// Mock window.matchMedia for useTheme hook
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation((query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
})
|
||||
|
||||
describe('LoginPage', () => {
|
||||
beforeEach(() => {
|
||||
// LoginPage is for unauthenticated users — do NOT set access_token
|
||||
localStorage.removeItem('access_token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
|
||||
// Override /api/auth/me to return 401 for unauthenticated state
|
||||
server.use(
|
||||
http.get('/api/auth/me', () => {
|
||||
return new HttpResponse(null, { status: 401 })
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('renders email and password input fields', () => {
|
||||
renderWithProviders(<LoginPage />, { initialRoute: '/login' })
|
||||
|
||||
expect(screen.getByLabelText('E-Mail')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Passwort')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders submit button', () => {
|
||||
renderWithProviders(<LoginPage />, { initialRoute: '/login' })
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Anmelden' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows error message on failed login', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
// Override login endpoint to return 401
|
||||
server.use(
|
||||
http.post('/api/auth/login', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Invalid credentials' },
|
||||
{ status: 401 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<LoginPage />, { initialRoute: '/login' })
|
||||
|
||||
// Fill in valid form data
|
||||
await user.type(screen.getByLabelText('E-Mail'), 'wrong@dak.de')
|
||||
await user.type(screen.getByLabelText('Passwort'), 'wrongpassword')
|
||||
|
||||
// Submit the form
|
||||
await user.click(screen.getByRole('button', { name: 'Anmelden' }))
|
||||
|
||||
// Should show error message for 401
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Ungueltige Anmeldedaten')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows validation errors for empty form submission', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
renderWithProviders(<LoginPage />, { initialRoute: '/login' })
|
||||
|
||||
// Click submit without filling in any fields
|
||||
await user.click(screen.getByRole('button', { name: 'Anmelden' }))
|
||||
|
||||
// Zod validation errors should appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Bitte geben Sie eine gueltige E-Mail-Adresse ein')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
expect(screen.getByText('Passwort ist erforderlich')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
86
frontend/src/pages/__tests__/ReportsPage.test.tsx
Normal file
86
frontend/src/pages/__tests__/ReportsPage.test.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
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 { ReportsPage } from '@/pages/ReportsPage'
|
||||
|
||||
describe('ReportsPage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('access_token', 'test-token')
|
||||
})
|
||||
|
||||
it('renders report list with report metadata', async () => {
|
||||
renderWithProviders(<ReportsPage />, { initialRoute: '/reports' })
|
||||
|
||||
await waitFor(() => {
|
||||
// mockReportMeta has jahr: 2026, kw: 6
|
||||
expect(screen.getByText('2026')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
expect(screen.getByText('KW 6')).toBeInTheDocument()
|
||||
|
||||
// Total count should show in the header "Bisherige Berichte (1)"
|
||||
expect(screen.getByText('Bisherige Berichte (1)')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows "Bericht generieren" button and form fields', async () => {
|
||||
renderWithProviders(<ReportsPage />, { initialRoute: '/reports' })
|
||||
|
||||
await waitFor(() => {
|
||||
// "Bericht generieren" appears as card title and button text; use button role
|
||||
expect(screen.getByRole('button', { name: /Bericht generieren/ })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Admin user sees the generation form with Jahr and KW inputs
|
||||
expect(screen.getByLabelText('Jahr')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('Kalenderwoche')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('handles empty report list', async () => {
|
||||
server.use(
|
||||
http.get('/api/reports/list', () => {
|
||||
return HttpResponse.json({ items: [], total: 0 })
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<ReportsPage />, { initialRoute: '/reports' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Berichte vorhanden.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows loading state', () => {
|
||||
server.use(
|
||||
http.get('/api/reports/list', async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
return HttpResponse.json({ items: [], total: 0 })
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<ReportsPage />, { initialRoute: '/reports' })
|
||||
|
||||
// The heading is always visible
|
||||
expect(screen.getByText('Berichte')).toBeInTheDocument()
|
||||
|
||||
// During loading, the empty state message or report data should not be visible
|
||||
expect(screen.queryByText('Keine Berichte vorhanden.')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('KW 6')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows error on API failure', async () => {
|
||||
server.use(
|
||||
http.get('/api/reports/list', () => {
|
||||
return new HttpResponse(null, { status: 500 })
|
||||
}),
|
||||
)
|
||||
|
||||
renderWithProviders(<ReportsPage />, { initialRoute: '/reports' })
|
||||
|
||||
// When query fails, reports defaults to [] so empty state appears
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keine Berichte vorhanden.')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue