diff --git a/src/components/admin/IntegrationTestRunner.tsx b/src/components/admin/IntegrationTestRunner.tsx
index 19a72e79..4cc2c415 100644
--- a/src/components/admin/IntegrationTestRunner.tsx
+++ b/src/components/admin/IntegrationTestRunner.tsx
@@ -14,8 +14,8 @@ 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 { IntegrationTestRunner as TestRunner, allTestSuites, type TestResult, formatResultsAsMarkdown, formatSingleTestAsMarkdown } from '@/lib/integrationTests';
+import { Play, Square, Download, ChevronDown, CheckCircle2, XCircle, Clock, SkipForward, Copy, ClipboardX } from 'lucide-react';
import { toast } from 'sonner';
import { handleError } from '@/lib/errorHandler';
@@ -105,6 +105,38 @@ export function IntegrationTestRunner() {
toast.success('Test results exported');
}, [runner]);
+ const copyAllResults = useCallback(async () => {
+ const summary = runner.getSummary();
+ const results = runner.getResults();
+
+ const markdown = formatResultsAsMarkdown(results, summary);
+
+ await navigator.clipboard.writeText(markdown);
+ toast.success('All test results copied to clipboard');
+ }, [runner]);
+
+ const copyFailedTests = useCallback(async () => {
+ const summary = runner.getSummary();
+ const failedResults = runner.getResults().filter(r => r.status === 'fail');
+
+ if (failedResults.length === 0) {
+ toast.info('No failed tests to copy');
+ return;
+ }
+
+ const markdown = formatResultsAsMarkdown(failedResults, summary, true);
+
+ await navigator.clipboard.writeText(markdown);
+ toast.success(`${failedResults.length} failed test(s) copied to clipboard`);
+ }, [runner]);
+
+ const copyTestResult = useCallback(async (result: TestResult) => {
+ const markdown = formatSingleTestAsMarkdown(result);
+
+ await navigator.clipboard.writeText(markdown);
+ toast.success('Test result copied to clipboard');
+ }, []);
+
// Guard is handled by the route/page, no loading state needed here
const summary = runner.getSummary();
@@ -166,10 +198,22 @@ export function IntegrationTestRunner() {
)}
{results.length > 0 && !isRunning && (
-
+ <>
+
+
+ {summary.failed > 0 && (
+
+ )}
+ >
)}
@@ -236,6 +280,14 @@ export function IntegrationTestRunner() {
{result.duration}ms
+
{(result.error || result.details) && (