Expand test case fixed-input editor and batch case detail split view.

Unify multi-turn behaviors (reply/tool), overall expectation, and input-mode picker while keeping non-text modes as coming soon; batch completed/running use a half-and-half detail panel below the top bar.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xin Wang
2026-08-09 17:08:53 +08:00
co-authored by Cursor
parent e988f9c133
commit ba6c7c6be3
11 changed files with 2363 additions and 687 deletions
@@ -1,7 +1,8 @@
"use client";
/**
* 批量测试用例详情抽屉。
* 批量测试用例详情面板。
* 与 prompt mode DebugDrawer overlay 相同:作为右侧 flex 半屏,无遮罩/模糊。
* 当前展示前端 mock 数据,后续可直接替换为真实执行事件与校验结果。
*/
@@ -12,21 +13,15 @@ import {
MessageSquareText,
Target,
TriangleAlert,
X,
} from "lucide-react";
import { BatchCaseStatusBadge } from "@/components/batch-test/batch-case-status";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import type { BatchRunCase } from "@/data/batch-run";
import { cn } from "@/lib/utils";
type BatchCaseDetailDrawerProps = {
item: BatchRunCase | null;
item: BatchRunCase;
assistantName: string;
onClose: () => void;
};
@@ -37,80 +32,72 @@ export function BatchCaseDetailDrawer({
onClose,
}: BatchCaseDetailDrawerProps) {
return (
<Sheet
open={Boolean(item)}
onOpenChange={(open) => {
if (!open) onClose();
}}
>
<SheetContent
side="right"
className="w-[min(92vw,620px)] border-hairline bg-card p-0 sm:max-w-[620px]"
>
{item && (
<>
<SheetHeader className="border-b border-hairline px-6 py-5 pr-16">
<p className="text-xs font-medium tracking-[0.08em] text-muted-soft uppercase">
用例详情
</p>
<SheetTitle className="text-lg leading-7">
{item.name}
</SheetTitle>
<SheetDescription>
被测助手:{assistantName} · 当前为前端 Mock 结果
</SheetDescription>
</SheetHeader>
<aside className="flex h-full min-w-0 flex-1 flex-col overflow-hidden border-l border-hairline bg-card">
<div className="flex min-h-14 shrink-0 items-center gap-3 border-b border-hairline px-4 py-3">
<button
type="button"
aria-label="关闭用例详情"
title="关闭"
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-hairline-strong bg-card text-muted-foreground shadow-sm transition-colors hover:text-foreground"
onClick={onClose}
>
<X size={16} />
</button>
<div className="min-w-0 flex-1">
<h2 className="truncate text-sm font-medium text-foreground">
{item.name}
</h2>
<p className="truncate text-xs text-muted-soft">
被测助手:{assistantName} · Mock 结果
</p>
</div>
<BatchCaseStatusBadge status={item.status} />
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-6">
<div className="space-y-6">
<section className="flex items-center justify-between rounded-2xl border border-hairline bg-canvas-soft px-4 py-3.5">
<div>
<p className="text-xs text-muted-soft">当前状态</p>
<p className="mt-1 text-sm text-muted-foreground">
{statusDescription(item)}
</p>
</div>
<BatchCaseStatusBadge status={item.status} />
</section>
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-5">
<div className="space-y-5">
<section className="rounded-2xl border border-hairline bg-canvas-soft px-4 py-3.5">
<p className="text-xs text-muted-soft">当前状态</p>
<p className="mt-1 text-sm text-muted-foreground">
{statusDescription(item)}
</p>
</section>
<DetailSection
icon={<Target size={16} />}
title="预期结果"
value={item.expected}
/>
<DetailSection
icon={<Target size={16} />}
title="预期结果"
value={item.expected}
/>
{(item.status === "waiting" || item.status === "running") && (
<ExecutionTimeline status={item.status} />
)}
{(item.status === "waiting" || item.status === "running") && (
<ExecutionTimeline status={item.status} />
)}
{(item.status === "pass" || item.status === "fail") && (
<DetailSection
icon={<MessageSquareText size={16} />}
title="实际回复"
value={item.actual}
/>
)}
{(item.status === "pass" || item.status === "fail") && (
<DetailSection
icon={<MessageSquareText size={16} />}
title="实际回复"
value={item.actual}
/>
)}
{item.status === "fail" && item.failReason && (
<DetailSection
icon={<TriangleAlert size={16} />}
title="失败原因(LLM 判断)"
value={item.failReason}
tone="destructive"
/>
)}
{item.status === "fail" && item.failReason && (
<DetailSection
icon={<TriangleAlert size={16} />}
title="失败原因(LLM 判断)"
value={item.failReason}
tone="destructive"
/>
)}
{item.status === "skipped" && (
<div className="rounded-2xl border border-dashed border-hairline-strong px-4 py-4 text-sm leading-6 text-muted-foreground">
该用例未执行,因此没有实际回复与判断结果。
</div>
)}
</div>
{item.status === "skipped" && (
<div className="rounded-2xl border border-dashed border-hairline-strong px-4 py-4 text-sm leading-6 text-muted-foreground">
该用例未执行,因此没有实际回复与判断结果。
</div>
</>
)}
</SheetContent>
</Sheet>
)}
</div>
</div>
</aside>
);
}