Files
agent/apps/web/tests/full-flow.test.tsx

146 lines
4.7 KiB
TypeScript

import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { HarnessEvent } from "@agent-studio/shared";
import { describe, expect, it, vi } from "vitest";
import { App } from "../src/App.js";
import {
createInitialHarnessState,
reduceHarnessEvent,
type HarnessClientState,
} from "../src/store/reducer.js";
const base = {
sessionId: "ses_full_flow",
timestamp: "2026-09-08T08:00:00.000Z",
};
function event<T extends HarnessEvent>(value: Omit<T, "eventId" | "sessionId" | "timestamp">): T {
return {
...base,
...value,
eventId: `evt_${value.sequence}`,
} as T;
}
function fullFlowSnapshot(): HarnessClientState {
const events: HarnessEvent[] = [
event({
type: "user.message",
sequence: 1,
payload: { messageId: "msg_user", text: "分析最近 7 天素材并生成报告", attachmentIds: ["att_1"] },
}),
event({ type: "run.started", sequence: 2, runId: "run_1", payload: { modelId: "gpt-test" } }),
event({
type: "skill.requested",
sequence: 3,
runId: "run_1",
payload: { name: "ads-analysis", source: "project" },
}),
event({
type: "skill.loaded",
sequence: 4,
runId: "run_1",
payload: { name: "ads-analysis", source: "project" },
}),
event({
type: "assistant.delta",
sequence: 5,
runId: "run_1",
payload: { messageId: "msg_assistant", delta: "已读取分析方法。" },
}),
event({
type: "tool.requested",
sequence: 6,
runId: "run_1",
payload: {
toolCallId: "tool_metrics",
toolName: "mock_ads_metrics",
arguments: { accountId: "demo-account", days: 7 },
},
}),
event({
type: "tool.completed",
sequence: 7,
runId: "run_1",
payload: {
toolCallId: "tool_metrics",
toolName: "mock_ads_metrics",
result: { spend: 1024.5, revenue: 3088.2, roas: 3.01, conversions: 44 },
},
}),
event({
type: "tool.requested",
sequence: 8,
runId: "run_1",
payload: {
toolCallId: "tool_report",
toolName: "save_report_draft",
arguments: { title: "广告素材分析报告", body: "近 7 天投放表现与下一步建议。" },
},
}),
event({
type: "approval.required",
sequence: 9,
runId: "run_1",
payload: {
approvalId: "ap_full_flow",
toolCallId: "tool_report",
toolName: "save_report_draft",
arguments: { title: "广告素材分析报告", body: "近 7 天投放表现与下一步建议。" },
riskLevel: "medium",
expiresAt: "2099-09-08T08:05:00.000Z",
},
}),
];
const state = events.reduce(reduceHarnessEvent, createInitialHarnessState({
sessionId: base.sessionId,
skills: [{
name: "ads-analysis",
description: "分析广告素材表现",
source: "project",
filePath: ".agents/skills/ads-analysis/SKILL.md",
diagnostics: [],
}],
}));
return { ...state, connection: "open" };
}
describe("complete training flow", () => {
it("renders the message, skill, tools, timeline, and server-owned approval as one flow", async () => {
const user = userEvent.setup();
const approve = vi.fn().mockResolvedValue({ approvalId: "ap_full_flow", status: "approved" });
const snapshot = fullFlowSnapshot();
const store = {
getSnapshot: () => snapshot,
subscribe: () => () => undefined,
sendMessage: vi.fn(),
cancel: vi.fn(),
start: vi.fn().mockResolvedValue(undefined),
getModelConfig: vi.fn().mockResolvedValue({
modelConfig: { configured: true, source: "session", modelId: "gpt-test", supportsImages: true },
}),
approve,
deny: vi.fn(),
};
render(<App store={store} autoStart={false} modelConfigured />);
expect(screen.getByText("分析最近 7 天素材并生成报告")).toBeVisible();
expect(screen.getByText("已读取分析方法。")).toBeVisible();
expect(screen.getByText("分析广告素材表现")).toBeVisible();
const metrics = screen.getByRole("region", { name: "广告指标工具" });
expect(within(metrics).getByText("3.01")).toBeVisible();
const report = screen.getByRole("region", { name: "保存报告草稿工具" });
expect(within(report).getByText("等待审批")).toBeVisible();
expect(screen.getByRole("dialog", { name: "允许写入报告草稿吗?" })).toBeVisible();
expect(screen.getByText("approval.required")).toBeVisible();
await user.click(screen.getByRole("button", { name: "批准并保存" }));
expect(approve).toHaveBeenCalledTimes(1);
expect(approve).toHaveBeenCalledWith("ap_full_flow");
});
});