/** * Integration Test Runner Component * * Superuser-only UI for running comprehensive integration tests. * Requires AAL2 if MFA is enrolled. */ import { useState, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { Progress } from '@/components/ui/progress'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Badge } from '@/components/ui/badge'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { useSuperuserGuard } from '@/hooks/useSuperuserGuard'; import { IntegrationTestRunner as TestRunner, allTestSuites, type TestResult } from '@/lib/integrationTests'; import { Play, Square, Download, ChevronDown, CheckCircle2, XCircle, Clock, SkipForward } from 'lucide-react'; import { toast } from 'sonner'; import { handleError } from '@/lib/errorHandler'; export function IntegrationTestRunner() { const superuserGuard = useSuperuserGuard(); const [selectedSuites, setSelectedSuites] = useState(allTestSuites.map(s => s.id)); const [isRunning, setIsRunning] = useState(false); const [results, setResults] = useState([]); const [runner] = useState(() => new TestRunner((result) => { setResults(prev => { const existing = prev.findIndex(r => r.id === result.id); if (existing >= 0) { const updated = [...prev]; updated[existing] = result; return updated; } return [...prev, result]; }); })); const toggleSuite = useCallback((suiteId: string) => { setSelectedSuites(prev => prev.includes(suiteId) ? prev.filter(id => id !== suiteId) : [...prev, suiteId] ); }, []); const runTests = useCallback(async () => { const suitesToRun = allTestSuites.filter(s => selectedSuites.includes(s.id)); if (suitesToRun.length === 0) { toast.error('Please select at least one test suite'); return; } setIsRunning(true); setResults([]); runner.reset(); toast.info(`Running ${suitesToRun.length} test suite(s)...`); try { await runner.runAllSuites(suitesToRun); const summary = runner.getSummary(); if (summary.failed > 0) { toast.error(`Tests completed with ${summary.failed} failure(s)`); } else { toast.success(`All ${summary.passed} tests passed!`); } } catch (error: unknown) { handleError(error, { action: 'Run integration tests', metadata: { suitesCount: suitesToRun.length } }); toast.error('Test run failed'); } finally { setIsRunning(false); } }, [selectedSuites, runner]); const stopTests = useCallback(() => { runner.stop(); setIsRunning(false); toast.info('Test run stopped'); }, [runner]); const exportResults = useCallback(() => { const summary = runner.getSummary(); const exportData = { timestamp: new Date().toISOString(), summary, results: runner.getResults() }; const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `integration-tests-${Date.now()}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success('Test results exported'); }, [runner]); // Guard is handled by the route/page, no loading state needed here const summary = runner.getSummary(); const totalTests = allTestSuites .filter(s => selectedSuites.includes(s.id)) .reduce((sum, s) => sum + s.tests.length, 0); const progress = totalTests > 0 ? (results.length / totalTests) * 100 : 0; return (
🧪 Integration Test Runner Superuser-only comprehensive testing system. Tests run against real database functions and edge functions. {/* Suite Selection */}

Select Test Suites:

{allTestSuites.map(suite => (
toggleSuite(suite.id)} disabled={isRunning} />

{suite.description}

))}
{/* Controls */}
{isRunning && ( )} {results.length > 0 && !isRunning && ( )}
{/* Progress */} {results.length > 0 && (
Progress: {results.length}/{totalTests} tests {progress.toFixed(0)}%
)} {/* Summary */} {results.length > 0 && (
{summary.passed} passed
{summary.failed} failed
{summary.skipped} skipped
{(summary.totalDuration / 1000).toFixed(2)}s
)}
{/* Results */} {results.length > 0 && ( Test Results
{results.map(result => (
{result.status === 'pass' && } {result.status === 'fail' && } {result.status === 'skip' && } {result.status === 'running' && }

{result.name}

{result.suite}

{result.duration}ms {(result.error || result.details) && ( )}
{result.error && (

{result.error}

)}
{(result.error || result.details) && (
{result.error && result.stack && (

Stack Trace:

                                {result.stack}
                              
)} {result.details && (

Details:

                                {JSON.stringify(result.details, null, 2)}
                              
)}
)}
))}
)}
); }