Files
breakpilot-lehrer/website/__tests__/compliance/DependencyMap.test.tsx
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

312 lines
8.4 KiB
TypeScript

/**
* DependencyMap Component Tests
*
* Tests fuer die Control-Requirement Mapping Visualisierung
*/
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import DependencyMap from '../../components/compliance/charts/DependencyMap'
// Mock-Daten
const mockRequirements = [
{
id: 'req-1',
article: 'Art. 32',
title: 'Sicherheit der Verarbeitung',
regulation_code: 'GDPR',
},
{
id: 'req-2',
article: 'Art. 9',
title: 'Risikomanagement',
regulation_code: 'AIACT',
},
{
id: 'req-3',
article: 'Art. 25',
title: 'Datenschutz durch Technikgestaltung',
regulation_code: 'GDPR',
},
]
const mockControls = [
{
id: 'ctrl-1',
control_id: 'PRIV-001',
title: 'Verarbeitungsverzeichnis',
domain: 'priv',
status: 'pass',
},
{
id: 'ctrl-2',
control_id: 'CRYPTO-001',
title: 'Verschluesselung',
domain: 'crypto',
status: 'pass',
},
{
id: 'ctrl-3',
control_id: 'AI-001',
title: 'KI-Risikobewertung',
domain: 'ai',
status: 'partial',
},
]
const mockMappings = [
{ requirement_id: 'req-1', control_id: 'PRIV-001', coverage_level: 'full' as const },
{ requirement_id: 'req-1', control_id: 'CRYPTO-001', coverage_level: 'partial' as const },
{ requirement_id: 'req-2', control_id: 'AI-001', coverage_level: 'full' as const },
{ requirement_id: 'req-3', control_id: 'PRIV-001', coverage_level: 'planned' as const },
]
describe('DependencyMap', () => {
it('should render the component with data', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// Statistik-Header sollte sichtbar sein
expect(screen.getByText('Abdeckung')).toBeInTheDocument()
expect(screen.getByText('Vollstaendig')).toBeInTheDocument()
expect(screen.getByText('Teilweise')).toBeInTheDocument()
expect(screen.getByText('Geplant')).toBeInTheDocument()
})
it('should render empty state when no data', () => {
render(
<DependencyMap
requirements={[]}
controls={[]}
mappings={[]}
/>
)
expect(screen.getByText(/Keine Mappings vorhanden/i)).toBeInTheDocument()
})
it('should show correct coverage statistics', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// 2 full, 1 partial, 1 planned
expect(screen.getByText('2')).toBeInTheDocument() // full mappings
expect(screen.getByText('1')).toBeInTheDocument() // partial/planned mappings
})
it('should display control IDs in matrix header', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
expect(screen.getByText('PRIV-001')).toBeInTheDocument()
expect(screen.getByText('CRYPTO-001')).toBeInTheDocument()
expect(screen.getByText('AI-001')).toBeInTheDocument()
})
it('should display requirement articles', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
expect(screen.getByText(/GDPR Art. 32/i)).toBeInTheDocument()
expect(screen.getByText(/AIACT Art. 9/i)).toBeInTheDocument()
})
it('should support German language', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
lang="de"
/>
)
expect(screen.getByText('Alle Verordnungen')).toBeInTheDocument()
expect(screen.getByText('Alle Domains')).toBeInTheDocument()
})
it('should support English language', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
lang="en"
/>
)
expect(screen.getByText('All Regulations')).toBeInTheDocument()
expect(screen.getByText('All Domains')).toBeInTheDocument()
expect(screen.getByText('Coverage')).toBeInTheDocument()
})
it('should call onControlClick when control is clicked', () => {
const onControlClick = vi.fn()
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
onControlClick={onControlClick}
/>
)
const controlHeader = screen.getByText('PRIV-001')
fireEvent.click(controlHeader)
expect(onControlClick).toHaveBeenCalledWith(mockControls[0])
})
it('should call onRequirementClick when requirement is clicked', () => {
const onRequirementClick = vi.fn()
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
onRequirementClick={onRequirementClick}
/>
)
// Finde den Requirement-Text
const reqElement = screen.getByText(/Sicherheit der Verarbeitung/i)
fireEvent.click(reqElement.closest('div[class*="cursor-pointer"]')!)
expect(onRequirementClick).toHaveBeenCalledWith(mockRequirements[0])
})
it('should filter by regulation', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
const regSelect = screen.getByDisplayValue('Alle Verordnungen')
fireEvent.change(regSelect, { target: { value: 'AIACT' } })
// Nur AIACT Requirements sollten sichtbar sein
expect(screen.queryByText(/GDPR Art. 32/i)).not.toBeInTheDocument()
expect(screen.getByText(/AIACT Art. 9/i)).toBeInTheDocument()
})
it('should filter by domain', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
const domainSelect = screen.getByDisplayValue('Alle Domains')
fireEvent.change(domainSelect, { target: { value: 'priv' } })
// Nur Privacy Controls sollten sichtbar sein
expect(screen.getByText('PRIV-001')).toBeInTheDocument()
expect(screen.queryByText('CRYPTO-001')).not.toBeInTheDocument()
})
it('should switch between matrix and connection view', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// Standard ist Matrix-Ansicht
expect(screen.getByText('Matrix')).toBeInTheDocument()
// Wechsle zu Verbindungs-Ansicht
fireEvent.click(screen.getByText('Verbindungen'))
// Connection-View Text sollte erscheinen
expect(screen.getByText(/Waehlen Sie ein Control/i)).toBeInTheDocument()
})
it('should highlight connected items when control is selected', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// Klicke auf einen Control
fireEvent.click(screen.getByText('PRIV-001'))
// Der Control sollte highlighted sein (bg-primary-100 Klasse)
const controlElement = screen.getByText('PRIV-001').closest('div')
expect(controlElement?.className).toContain('bg-primary-100')
})
it('should show legend with coverage levels', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
expect(screen.getByText('Vollstaendig abgedeckt')).toBeInTheDocument()
expect(screen.getByText('Teilweise abgedeckt')).toBeInTheDocument()
expect(screen.getByText('Geplant')).toBeInTheDocument()
})
it('should calculate coverage percentage correctly', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// 3 von 3 Requirements haben mindestens ein Mapping = 100%
expect(screen.getByText('100.0%')).toBeInTheDocument()
})
it('should show control counts in connection view', () => {
render(
<DependencyMap
requirements={mockRequirements}
controls={mockControls}
mappings={mockMappings}
/>
)
// Wechsle zu Verbindungs-Ansicht
fireEvent.click(screen.getByText('Verbindungen'))
// PRIV-001 hat 2 Verbindungen
const privControl = screen.getAllByText('PRIV-001')[0].closest('button')
expect(privControl?.textContent).toContain('2')
})
})