mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:51:12 -05:00
Add cleanup verification UI
Implements a CleanupReport UI component to display detailed cleanup results after test runs, including tables cleaned, records deleted per table, duration, and errors. Integrates with IntegrationTestRunner to show cleanup summary post-run and exports a compact inline view.
This commit is contained in:
@@ -18,6 +18,7 @@ import { IntegrationTestRunner as TestRunner, allTestSuites, type TestResult, fo
|
||||
import { Play, Square, Download, ChevronDown, CheckCircle2, XCircle, Clock, SkipForward, Copy, ClipboardX } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { handleError } from '@/lib/errorHandler';
|
||||
import { CleanupReport } from '@/components/ui/cleanup-report';
|
||||
|
||||
export function IntegrationTestRunner() {
|
||||
const superuserGuard = useSuperuserGuard();
|
||||
@@ -252,6 +253,11 @@ export function IntegrationTestRunner() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Cleanup Report */}
|
||||
{!isRunning && summary.cleanup && (
|
||||
<CleanupReport summary={summary.cleanup} />
|
||||
)}
|
||||
|
||||
{/* Results */}
|
||||
{results.length > 0 && (
|
||||
<Card>
|
||||
|
||||
221
src/components/ui/cleanup-report.tsx
Normal file
221
src/components/ui/cleanup-report.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Cleanup Verification Report Component
|
||||
*
|
||||
* Displays detailed results of test data cleanup after integration tests complete.
|
||||
* Shows tables cleaned, records deleted, errors, and verification status.
|
||||
*/
|
||||
|
||||
import { CheckCircle2, XCircle, AlertCircle, Database, Trash2, Clock } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import type { CleanupSummary } from '@/lib/integrationTests/testCleanup';
|
||||
|
||||
interface CleanupReportProps {
|
||||
summary: CleanupSummary;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function CleanupReport({ summary, className = '' }: CleanupReportProps) {
|
||||
const successCount = summary.results.filter(r => !r.error).length;
|
||||
const errorCount = summary.results.filter(r => r.error).length;
|
||||
const successRate = summary.results.length > 0
|
||||
? (successCount / summary.results.length) * 100
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<Card className={`border-border ${className}`}>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Trash2 className="h-5 w-5 text-muted-foreground" />
|
||||
Test Data Cleanup Report
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="space-y-4">
|
||||
{/* Summary Stats */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-muted-foreground">Total Deleted</p>
|
||||
<p className="text-2xl font-bold text-foreground">
|
||||
{summary.totalDeleted.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-muted-foreground">Tables Cleaned</p>
|
||||
<p className="text-2xl font-bold text-foreground">
|
||||
{successCount}/{summary.results.length}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-muted-foreground">Duration</p>
|
||||
<p className="text-2xl font-bold text-foreground flex items-center gap-1">
|
||||
<Clock className="h-4 w-4" />
|
||||
{(summary.totalDuration / 1000).toFixed(1)}s
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-muted-foreground">Status</p>
|
||||
<Badge
|
||||
variant={summary.success ? "default" : "destructive"}
|
||||
className="text-base font-semibold"
|
||||
>
|
||||
{summary.success ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
Complete
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1">
|
||||
<XCircle className="h-4 w-4" />
|
||||
Failed
|
||||
</span>
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Success Rate Progress */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Success Rate</span>
|
||||
<span className="font-medium text-foreground">{successRate.toFixed(1)}%</span>
|
||||
</div>
|
||||
<Progress value={successRate} className="h-2" />
|
||||
</div>
|
||||
|
||||
{/* Table-by-Table Results */}
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-semibold text-foreground flex items-center gap-2">
|
||||
<Database className="h-4 w-4" />
|
||||
Cleanup Details
|
||||
</h3>
|
||||
|
||||
<div className="space-y-1 max-h-64 overflow-y-auto border border-border rounded-md">
|
||||
{summary.results.map((result, index) => (
|
||||
<div
|
||||
key={`${result.table}-${index}`}
|
||||
className="flex items-center justify-between p-3 hover:bg-accent/50 transition-colors border-b border-border last:border-b-0"
|
||||
>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
{result.error ? (
|
||||
<XCircle className="h-4 w-4 text-destructive flex-shrink-0" />
|
||||
) : result.deleted > 0 ? (
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 dark:text-green-400 flex-shrink-0" />
|
||||
) : (
|
||||
<AlertCircle className="h-4 w-4 text-muted-foreground flex-shrink-0" />
|
||||
)}
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-mono text-sm text-foreground truncate">
|
||||
{result.table}
|
||||
</p>
|
||||
{result.error && (
|
||||
<p className="text-xs text-destructive truncate">
|
||||
{result.error}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 flex-shrink-0">
|
||||
<Badge
|
||||
variant={result.deleted > 0 ? "default" : "secondary"}
|
||||
className="font-mono"
|
||||
>
|
||||
{result.deleted} deleted
|
||||
</Badge>
|
||||
<span className="text-xs text-muted-foreground font-mono w-16 text-right">
|
||||
{result.duration}ms
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Summary (if any) */}
|
||||
{errorCount > 0 && (
|
||||
<div className="p-3 bg-destructive/10 border border-destructive/20 rounded-md">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertCircle className="h-5 w-5 text-destructive flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-destructive">
|
||||
{errorCount} {errorCount === 1 ? 'table' : 'tables'} failed to clean
|
||||
</p>
|
||||
<p className="text-xs text-destructive/80 mt-1">
|
||||
Check error messages above for details. Test data may remain in database.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Success Message */}
|
||||
{summary.success && summary.totalDeleted > 0 && (
|
||||
<div className="p-3 bg-green-500/10 border border-green-500/20 rounded-md">
|
||||
<div className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-5 w-5 text-green-600 dark:text-green-400 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-green-700 dark:text-green-300">
|
||||
Cleanup completed successfully
|
||||
</p>
|
||||
<p className="text-xs text-green-600 dark:text-green-400 mt-1">
|
||||
All test data has been removed from the database.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* No Data Message */}
|
||||
{summary.success && summary.totalDeleted === 0 && (
|
||||
<div className="p-3 bg-muted border border-border rounded-md">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertCircle className="h-5 w-5 text-muted-foreground flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-muted-foreground">
|
||||
No test data found
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Database is already clean or no test data was created during this run.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact version for inline display in test results
|
||||
*/
|
||||
export function CleanupReportCompact({ summary }: CleanupReportProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-3 p-3 bg-accent/50 rounded-md border border-border">
|
||||
<Trash2 className="h-5 w-5 text-muted-foreground flex-shrink-0" />
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
Cleanup: {summary.totalDeleted} records deleted
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{summary.results.filter(r => !r.error).length}/{summary.results.length} tables cleaned
|
||||
{' • '}
|
||||
{(summary.totalDuration / 1000).toFixed(1)}s
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{summary.success ? (
|
||||
<CheckCircle2 className="h-5 w-5 text-green-600 dark:text-green-400 flex-shrink-0" />
|
||||
) : (
|
||||
<XCircle className="h-5 w-5 text-destructive flex-shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user