Files
breakpilot-lehrer/website/app/admin/docs/_components/TestingTab.tsx
Benjamin Admin 0b37c5e692 [split-required] Split website + studio-v2 monoliths (Phase 3 continued)
Website (14 monoliths split):
- compliance/page.tsx (1,519 → 9), docs/audit (1,262 → 20)
- quality (1,231 → 16), alerts (1,203 → 10), docs (1,202 → 11)
- i18n.ts (1,173 → 8 language files)
- unity-bridge (1,094 → 12), backlog (1,087 → 6)
- training (1,066 → 8), rag (1,063 → 8)
- Deleted index_original.ts (4,899 LOC dead backup)

Studio-v2 (5 monoliths split):
- meet/page.tsx (1,481 → 9), messages (1,166 → 9)
- AlertsB2BContext.tsx (1,165 → 5 modules)
- alerts-b2b/page.tsx (1,019 → 6), korrektur/archiv (1,001 → 6)

All existing imports preserved. Zero new TypeScript errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:52:36 +02:00

172 lines
8.0 KiB
TypeScript

'use client'
import { useState } from 'react'
import { PROJECT_BASE_PATH } from '../data'
const testDocLinks = [
{ file: 'docs/testing/README.md', label: 'Test-Uebersicht', desc: 'Teststrategie & Coverage-Ziele' },
{ file: 'docs/testing/QUICKSTART.md', label: 'Quickstart', desc: 'Schnellstart fuer Tests' },
{ file: 'docs/testing/INTEGRATION_TESTS.md', label: 'Integrationstests', desc: 'API & DB Tests' },
]
const coverageTargets = [
{ component: 'Go Consent Service', target: '80%', current: '~75%', color: 'green' },
{ component: 'Python Backend', target: '70%', current: '~65%', color: 'yellow' },
{ component: 'Critical Paths (Auth, OAuth)', target: '95%', current: '~90%', color: 'green' },
]
const testCommands = [
{ label: 'Go Tests (alle)', cmd: 'cd consent-service && go test -v ./...', lang: 'Go' },
{ label: 'Go Tests mit Coverage', cmd: 'cd consent-service && go test -cover ./...', lang: 'Go' },
{ label: 'Python Tests (alle)', cmd: 'cd backend && source venv/bin/activate && pytest -v', lang: 'Python' },
{ label: 'Python Tests mit Coverage', cmd: 'cd backend && pytest --cov=. --cov-report=html', lang: 'Python' },
]
export default function TestingTab() {
const [copiedEndpoint, setCopiedEndpoint] = useState<string | null>(null)
const copyToClipboard = (text: string, id: string) => {
navigator.clipboard.writeText(text)
setCopiedEndpoint(id)
setTimeout(() => setCopiedEndpoint(null), 2000)
}
return (
<div className="space-y-6">
{/* Quick Links to Docs */}
<div className="bg-white rounded-xl border border-slate-200 p-6">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-slate-900">Test-Dokumentation</h2>
<a
href={`vscode://file/${PROJECT_BASE_PATH}/docs/testing/README.md`}
className="flex items-center gap-2 text-sm bg-blue-50 text-blue-700 px-3 py-2 rounded-lg hover:bg-blue-100 transition-colors"
>
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"/>
</svg>
Vollstaendige Docs in VS Code
</a>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{testDocLinks.map((doc) => (
<a
key={doc.file}
href={`vscode://file/${PROJECT_BASE_PATH}/${doc.file}`}
className="p-4 bg-slate-50 rounded-lg hover:bg-slate-100 transition-colors"
>
<div className="font-medium text-slate-900">{doc.label}</div>
<div className="text-sm text-slate-500">{doc.desc}</div>
</a>
))}
</div>
</div>
{/* Test Pyramid */}
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Test-Pyramide</h2>
<div className="bg-slate-900 rounded-lg p-6 text-center">
<pre className="text-green-400 font-mono text-sm whitespace-pre inline-block text-left">{` /\\
/ \\ E2E (10%)
/----\\
/ \\ Integration (20%)
/--------\\
/ \\ Unit Tests (70%)
/--------------\\`}</pre>
</div>
</div>
{/* Coverage Ziele */}
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Coverage-Ziele</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{coverageTargets.map((item) => (
<div key={item.component} className="p-4 bg-slate-50 rounded-lg">
<div className="font-medium text-slate-900">{item.component}</div>
<div className="flex items-center gap-2 mt-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className={`h-full ${item.color === 'green' ? 'bg-green-500' : 'bg-yellow-500'}`}
style={{ width: item.current.replace('~', '') }}
/>
</div>
<span className="text-sm text-slate-600">{item.current}</span>
</div>
<div className="text-xs text-slate-500 mt-1">Ziel: {item.target}</div>
</div>
))}
</div>
</div>
{/* Test Commands */}
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Test-Befehle</h2>
<div className="space-y-4">
{testCommands.map((item, idx) => (
<div key={idx} className="flex items-center gap-4 p-3 bg-slate-50 rounded-lg">
<span className={`text-xs px-2 py-0.5 rounded-full ${item.lang === 'Go' ? 'bg-cyan-100 text-cyan-700' : 'bg-yellow-100 text-yellow-700'}`}>
{item.lang}
</span>
<div className="text-sm text-slate-600 w-40">{item.label}</div>
<code className="flex-1 text-sm font-mono bg-slate-900 text-green-400 px-3 py-2 rounded">
{item.cmd}
</code>
<button
onClick={() => copyToClipboard(item.cmd, `test-${idx}`)}
className="text-slate-400 hover:text-slate-600"
>
{copiedEndpoint === `test-${idx}` ? (
<span className="text-xs text-green-600">Copied!</span>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
)}
</button>
</div>
))}
</div>
</div>
{/* Test-Struktur */}
<div className="bg-white rounded-xl border border-slate-200 p-6">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Test-Struktur</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Go Tests */}
<div>
<h3 className="font-medium text-slate-900 mb-2 flex items-center gap-2">
<span className="text-xs px-2 py-0.5 rounded-full bg-cyan-100 text-cyan-700">Go</span>
Consent Service
</h3>
<div className="bg-slate-900 rounded-lg p-4 font-mono text-sm text-green-400">
<div>consent-service/</div>
<div className="ml-4">internal/</div>
<div className="ml-8">handlers/handlers_test.go</div>
<div className="ml-8">services/auth_service_test.go</div>
<div className="ml-8">services/oauth_service_test.go</div>
<div className="ml-8">services/totp_service_test.go</div>
<div className="ml-8">middleware/middleware_test.go</div>
</div>
</div>
{/* Python Tests */}
<div>
<h3 className="font-medium text-slate-900 mb-2 flex items-center gap-2">
<span className="text-xs px-2 py-0.5 rounded-full bg-yellow-100 text-yellow-700">Python</span>
Backend
</h3>
<div className="bg-slate-900 rounded-lg p-4 font-mono text-sm text-green-400">
<div>backend/</div>
<div className="ml-4">tests/</div>
<div className="ml-8">test_consent_client.py</div>
<div className="ml-8">test_gdpr_api.py</div>
<div className="ml-8">test_dsms_webui.py</div>
<div className="ml-4">conftest.py</div>
</div>
</div>
</div>
</div>
</div>
)
}