| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- import { fireEvent, render, screen } from "@testing-library/react";
- import { describe, expect, it, vi } from "vitest";
- import { InspectorComparison } from "@/components/inspector/InspectorComparison";
- import { Inspector } from "@/components/inspector/Inspector";
- import { storyItem } from "@/lib/layout";
- import { makeStory } from "@/tests/fixtures";
- import type { InspectorWorkbenchProjection, SourceBinding } from "@/lib/types";
- const binding: SourceBinding = {
- id: "result-1",
- sourceId: "event:4607",
- role: "output",
- selector: { kind: "json-pointer", path: "/output/content/results/0" },
- transform: { kind: "direct" },
- evidence: { availability: "returned-to-agent", adoption: "not-recorded", confidence: "exact" },
- resolution: "resolved",
- };
- const payload: InspectorWorkbenchProjection = {
- schemaVersion: "inspector-source-v2",
- detailRef: "event:4607",
- cardKind: "query",
- completeness: "complete",
- businessProjection: {
- detailKind: "activity",
- businessSections: [{ id: "results", title: "返回结果", items: [{ label: "Case 1", value: "命中内容" }] }],
- },
- modules: [{
- id: "results",
- title: "返回结果",
- businessPath: "/businessSections/0",
- presentation: "items",
- rows: [{ id: "case-1", label: "Case 1", businessSelector: "/businessSections/0/items/0/value", bindingIds: [binding.id] }],
- bindings: [binding],
- runtimeRefs: ["event:4607"],
- defaultOpen: true,
- }],
- sources: {
- "event:4607": {
- id: "event:4607",
- kind: "runtime-event",
- label: "search_script_decode_case",
- locator: { table: "script_build_event + script_build_event_body", eventId: 4607, eventName: "search_script_decode_case" },
- status: "ok",
- completeness: "complete",
- resultState: "present",
- selectedValues: [{ bindingId: binding.id, path: "/output/content/results/0", value: "命中内容" }],
- rawRecord: { output: { content: { results: ["命中内容"] } } },
- },
- },
- notices: [],
- };
- describe("InspectorComparison", () => {
- it("returns from the source comparison to the readable business Inspector", () => {
- const onModeChange = vi.fn();
- const item = storyItem(makeStory("event:4607", "planning-analysis"), 1);
- render(<Inspector item={item} mode="lineage" onModeChange={onModeChange} onClose={vi.fn()} workbench={payload} />);
- fireEvent.click(screen.getByRole("button", { name: "返回业务详情" }));
- expect(onModeChange).toHaveBeenCalledWith("business");
- });
- it("renders business fields from the same projection used by the readable Inspector", () => {
- const planPayload: InspectorWorkbenchProjection = {
- ...payload,
- detailRef: "round:1:plan",
- cardKind: "implementation-plan",
- businessProjection: {
- detailKind: "agent-decision",
- blocks: [{
- type: "implementation-plan",
- title: "本轮实施路线",
- mode: "竞争",
- reasoning: "比较两条路线。",
- routes: [{ pathIndex: 1, target: "完整段落骨架", method: "产生脉络", emphasis: "先保证结构清楚" }],
- }],
- },
- modules: [{
- id: "block-1",
- title: "本轮实施路线",
- businessPath: "/blocks/0",
- presentation: "plan",
- rows: [
- { id: "mode", label: "规划方式", businessSelector: "/blocks/0/mode", bindingIds: [binding.id] },
- { id: "target", label: "路线 1 · 作用范围", businessSelector: "/blocks/0/routes/0/target", bindingIds: [binding.id] },
- { id: "method", label: "路线 1 · 实施方法", businessSelector: "/blocks/0/routes/0/method", bindingIds: [binding.id] },
- { id: "emphasis", label: "路线 1 · 路线侧重", businessSelector: "/blocks/0/routes/0/emphasis", bindingIds: [binding.id] },
- ],
- bindings: [binding],
- runtimeRefs: [],
- defaultOpen: true,
- }],
- };
- const { container } = render(<InspectorComparison data={planPayload} />);
- expect(screen.getByText("竞争")).toBeInTheDocument();
- expect(screen.getByText("完整段落骨架")).toBeInTheDocument();
- expect(screen.getByText("产生脉络")).toBeInTheDocument();
- expect(screen.getByText("先保证结构清楚")).toBeInTheDocument();
- expect(container.querySelector(".comparisonItemBusiness")?.textContent).not.toContain("pathIndex");
- });
- it("renders one physical row across all three columns", () => {
- const { container } = render(<InspectorComparison data={payload} />);
- expect(screen.getByText("业务详情中的内容")).toBeInTheDocument();
- expect(screen.getByText("这一项的数据依据")).toBeInTheDocument();
- expect(screen.getByText("对应的原始字段或原文")).toBeInTheDocument();
- expect(screen.getByText("返回结果")).toBeInTheDocument();
- expect(screen.getAllByText("S1").length).toBeGreaterThanOrEqual(2);
- expect(screen.getByText("已返回给 Agent,是否采用未记录")).toBeInTheDocument();
- expect(screen.getByText("script_build_event + script_build_event_body")).toBeInTheDocument();
- expect(container.querySelectorAll(".comparisonItemRow")).toHaveLength(1);
- });
- it("shows the exact selected source value without opening raw JSON", () => {
- render(<InspectorComparison data={payload} />);
- expect(screen.getAllByText("命中内容").length).toBeGreaterThanOrEqual(2);
- expect(screen.getByText("完整原始记录")).toBeInTheDocument();
- expect(screen.getByText("/output/content/results/0").closest("details")).not.toHaveAttribute("open");
- expect(screen.queryByText("found")).not.toBeInTheDocument();
- });
- it("keeps structured business rows readable instead of exposing object keys", () => {
- const decisionPayload: InspectorWorkbenchProjection = {
- ...payload,
- cardKind: "data-decision",
- businessProjection: {
- detailKind: "agent-decision",
- blocks: [{
- type: "items",
- title: "参与判断",
- items: [{ label: "解构 Case", value: "返回了 2 条可用案例", note: "已读取" }],
- }],
- },
- modules: [{
- ...payload.modules[0],
- id: "block-1",
- title: "参与判断",
- businessPath: "/blocks/0",
- rows: [{ id: "source-1", label: "解构 Case", businessSelector: "/blocks/0/items/0", bindingIds: [binding.id] }],
- }],
- };
- const { container } = render(<InspectorComparison data={decisionPayload} />);
- const business = container.querySelector(".comparisonItemBusiness");
- expect(business).toHaveTextContent("解构 Case");
- expect(business).toHaveTextContent("返回了 2 条可用案例");
- expect(business).toHaveTextContent("说明");
- expect(business?.textContent).not.toMatch(/\blabel\b|\bvalue\b|\bnote\b/);
- });
- it("synchronizes binding highlight within the already aligned row", () => {
- const onChange = vi.fn();
- const { container } = render(<InspectorComparison data={payload} onActiveBindingChange={onChange} />);
- fireEvent.click(screen.getByRole("button", { name: /已返回给 Agent/ }));
- expect(container.querySelectorAll('[data-binding-id="result-1"]').length).toBeGreaterThanOrEqual(2);
- expect(container.querySelector(".comparisonItemRow.is-active")).toBeInTheDocument();
- expect(onChange).toHaveBeenLastCalledWith("result-1");
- });
- it("unmounts module contents when collapsed", () => {
- render(<InspectorComparison data={payload} />);
- fireEvent.click(screen.getByRole("button", { name: /返回结果/ }));
- expect(screen.queryByText("命中内容")).not.toBeInTheDocument();
- });
- it("explains a successful empty result instead of calling it missing", () => {
- const empty: InspectorWorkbenchProjection = {
- ...payload,
- sources: {
- "event:4607": { ...payload.sources["event:4607"], resultState: "empty", rawRecord: [] },
- },
- };
- render(<InspectorComparison data={empty} />);
- expect(screen.getByText("查询完成,返回 0 条")).toBeInTheDocument();
- expect(screen.getByText("这不是记录缺失。")).toBeInTheDocument();
- });
- it("hands focus control to an upper prompt or artifact overlay", () => {
- const item = storyItem(makeStory("event:4607", "planning-analysis"), 1);
- const onClose = vi.fn();
- const view = (topOverlayOpen: boolean) => <><button type="button" data-testid="upper-overlay-control">上层弹窗按钮</button><Inspector item={item} onClose={onClose} workbench={payload} topOverlayOpen={topOverlayOpen} /></>;
- const { rerender } = render(view(false));
- const outside = screen.getByTestId("upper-overlay-control");
- outside.focus();
- expect(screen.getByRole("dialog")).toContainElement(document.activeElement as HTMLElement);
- rerender(view(true));
- const upperOverlayControl = screen.getByTestId("upper-overlay-control");
- upperOverlayControl.focus();
- expect(document.activeElement).toBe(upperOverlayControl);
- });
- });
|