feat: add project-aware agent workspace
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s

This commit is contained in:
2026-10-22 18:52:18 +08:00
parent c6a0e74e72
commit 2d8506fea0
191 changed files with 12373 additions and 3556 deletions
@@ -1,7 +1,9 @@
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { AnswerUiView } from "./answer-ui-view";
import { parseAnswerUiBlock } from "./contract";
vi.mock("echarts-for-react", () => ({ default: () => <div aria-label="测试图形" /> }));
function block(type: string, props: Record<string, unknown>) {
const parsed = parseAnswerUiBlock({
@@ -14,7 +16,19 @@ function block(type: string, props: Record<string, unknown>) {
return parsed;
}
describe("local interactive answer renderer", () => {
it("renders through json-render and lets readers inspect exact chart data", () => {
it("offers a named metric quote action and retains the exact value, unit and source", async () => {
const user = userEvent.setup();
const ask = vi.fn();
render(<AnswerUiView block={block("Metric", {
label: "当前压力", value: "0.3000", unit: "MPa", source: "已加载记录",
})} onAsk={ask} />);
await user.click(screen.getByRole("button", { name: "针对当前压力提问" }));
expect(ask).toHaveBeenCalledWith({
title: "当前压力", text: "当前压力:0.3000MPa\n来源:已加载记录\n引用内容块:test",
});
});
it("renders through json-render and lets readers inspect exact chart data with keyboard tabs", async () => {
const user = userEvent.setup();
render(
<AnswerUiView
block={block("Chart", {
@@ -26,10 +40,14 @@ describe("local interactive answer renderer", () => {
})}
/>
);
fireEvent.click(screen.getByRole("button", { name: /^数据$/ }));
await user.click(screen.getByRole("tab", { name: /^数据$/ }));
expect(screen.getByRole("table")).toHaveTextContent("0.3");
expect(screen.getByRole("table")).toHaveTextContent("09:00");
expect(screen.getByRole("button", { name: /^数据$/ })).toHaveAttribute("aria-pressed", "true");
expect(screen.getByRole("tab", { name: /^数据$/ })).toHaveAttribute("aria-selected", "true");
await user.keyboard("{ArrowLeft}");
expect(screen.getByRole("tab", { name: /^柱状$/ })).toHaveFocus();
expect(screen.getByRole("tab", { name: /^柱状$/ })).toHaveAttribute("aria-selected", "true");
expect(screen.queryByRole("table")).not.toBeInTheDocument();
});
it("runs map location only after a user click and reports missing IDs", async () => {
const locate = vi
@@ -54,6 +72,35 @@ describe("local interactive answer renderer", () => {
feature_ids: ["DEMO-P001", "DEMO-P002"]
});
});
it("filters and sorts loaded rows, then quotes the selected original values without sending", async () => {
const user = userEvent.setup();
const ask = vi.fn();
const submit = vi.fn();
render(<AnswerUiView block={block("Table", {
title: "监测记录", columns: ["监测点", "压力"],
rows: [["南站", "0.32"], ["北站", "0.21"], ["南站二号", "0.26"]]
})} onAsk={ask} onSubmit={submit} />);
await user.type(screen.getByRole("textbox", { name: "搜索监测记录" }), "南站");
expect(screen.getByText("2 / 3 行")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "按压力排序" }));
const rows = screen.getAllByRole("row");
expect(rows[1]).toHaveTextContent("南站二号");
await user.click(screen.getByRole("checkbox", { name: "选择第 3 行" }));
await user.click(screen.getByRole("button", { name: "针对这部分提问" }));
expect(ask).toHaveBeenCalledWith({ title: "监测记录", text: expect.stringContaining("监测点:南站二号;压力:0.26") });
expect(submit).not.toHaveBeenCalled();
});
it("keeps form values after a failed submission", async () => {
const user = userEvent.setup();
render(<AnswerUiView block={block("FollowUpForm", {
title: "现场事实", description: "用户填写",
fields: [{ name: "detail", label: "记录", kind: "text", required: true }]
})} onSubmit={async () => { throw new Error("暂未发送"); }} />);
await user.type(screen.getByLabelText("记录(必填)"), "北站现场记录");
await user.click(screen.getByRole("button", { name: "发送补充资料" }));
expect(await screen.findByRole("status")).toHaveTextContent("暂未发送");
expect(screen.getByLabelText("记录(必填)")).toHaveValue("北站现场记录");
});
it("starts blank, requires input and sends one user message without claiming evidence is saved", async () => {
let complete!: () => void;
const submit = vi.fn(