feat(frontend): add auto test functionality to debug drawer

Introduce a new auto test mode in the debug drawer, allowing users to run automated tests with mock data. This includes the addition of AutoTestPanel, AutoTestLivePanel, and AutoTestRunBar components for managing test cases and displaying results. The debug mode can now switch between manual and auto testing, enhancing the debugging experience.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xin Wang
2026-08-07 16:09:18 +08:00
parent 36117b976e
commit 2f755969d3
2 changed files with 1292 additions and 26 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,12 @@ import {
X,
} from "lucide-react";
import {
AutoTestLivePanel,
AutoTestRunBar,
DebugModeTabs,
useAutoTestRunner,
} from "@/components/assistant-editor/debug-auto-test";
import { NetworkQualityIndicator } from "@/components/network-quality-indicator";
import { ClientMessageDialog } from "@/components/client-message-dialog";
import { AuraVisualizer } from "@/components/ui/aura-visualizer";
@@ -67,6 +73,7 @@ type VizStyle = "aura" | "nebula" | "bars" | "wave";
// 调试面板顶部主视图:聊天记录 / 视频流
type DebugView = "chat" | "video";
type DebugInputMode = "mic" | "text";
type DebugMode = "manual" | "auto";
type PendingDebugImage = {
file: File;
previewUrl: string;
@@ -175,6 +182,9 @@ export function DebugDrawer({
}) {
const preview = useVoicePreview(assistantId, onNodeActive);
const camera = useCameraPreview();
const autoTest = useAutoTestRunner();
const stopAutoTest = autoTest.stop;
const [debugMode, setDebugMode] = useState<DebugMode>("manual");
const [showTranscript, setShowTranscript] = useState(false);
const [vizStyle, setVizStyle] = useState<VizStyle>("aura");
const [view, setView] = useState<DebugView>("chat");
@@ -218,6 +228,20 @@ export function DebugDrawer({
[camera, preview],
);
// 切到自动测试时停掉手动会话,避免两边抢占麦克风/对话区
const handleModeChange = useCallback(
(mode: DebugMode) => {
setDebugMode(mode);
if (mode === "auto" && recording) {
preview.disconnect();
}
if (mode === "manual") {
stopAutoTest();
}
},
[preview, recording, stopAutoTest],
);
return (
<aside
className={overlay
@@ -240,6 +264,8 @@ export function DebugDrawer({
<div className="shrink-0 text-sm font-medium text-foreground">
</div>
{debugMode === "manual" && (
<>
<NetworkQualityIndicator
quality={preview.networkQuality}
status={preview.status}
@@ -248,6 +274,8 @@ export function DebugDrawer({
status={preview.status}
micWarning={preview.micWarning}
/>
</>
)}
</div>
<div className="flex shrink-0 items-center gap-2">
<ClientToolsPopover tools={preview.clientTools} />
@@ -261,7 +289,7 @@ export function DebugDrawer({
onChange={setDynamicVariableValues}
/>
)}
{SHOW_VOICE_VIZ && view === "chat" && (
{debugMode === "manual" && SHOW_VOICE_VIZ && view === "chat" && (
<>
{!showTranscript && (
<SegmentedIconGroup label="可视化样式">
@@ -297,11 +325,17 @@ export function DebugDrawer({
)}
</div>
</div>
<div className="shrink-0 border-b border-hairline px-5 py-2.5">
<div className="shrink-0 space-y-2.5 border-b border-hairline px-5 py-2.5">
<DebugModeTabs mode={debugMode} onChange={handleModeChange} />
{debugMode === "manual" && (
<div className="flex h-10 min-w-0 items-center rounded-[1.4rem] border border-hairline-strong bg-background px-2">
<CameraDeviceField camera={camera} onSelect={selectCamera} />
</div>
)}
</div>
{debugMode === "auto" ? (
<AutoTestPanel assistantId={assistantId} autoTest={autoTest} />
) : (
<DebugVoicePanel
view={view}
onViewChange={setView}
@@ -315,10 +349,48 @@ export function DebugDrawer({
dynamicVariables={resolvedDynamicVariables}
dynamicVariablesError={dynamicVariablesError}
/>
)}
</aside>
);
}
function AutoTestPanel({
assistantId,
autoTest,
}: {
assistantId: string | null;
autoTest: ReturnType<typeof useAutoTestRunner>;
}) {
const {
state,
selectedCase,
selectCase,
clearCase,
start,
stop,
rerun,
} = autoTest;
return (
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
<AutoTestLivePanel testCase={selectedCase} state={state} />
</div>
<div className="shrink-0 border-t border-hairline bg-card p-3">
<AutoTestRunBar
state={state}
assistantId={assistantId}
onSelectCase={selectCase}
onClearCase={clearCase}
onStart={start}
onStop={stop}
onRerun={rerun}
/>
</div>
</div>
);
}
function ClientToolsPopover({ tools }: { tools: ClientToolDefinition[] }) {
const [copiedToolName, setCopiedToolName] = useState<string | null>(null);