This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
orchestrator/agent-team/web/src/components/TaskDrawer.test.tsx

154 lines
4.3 KiB
TypeScript
Raw Normal View History

import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { TaskDrawer } from "./TaskDrawer";
import type { TaskDetail } from "../api";
const DETAIL: TaskDetail = {
ok: true,
thread_id: "aaaa1111bbbb",
short_id: "aaaa1111",
task: "remediate CVE-2026-1",
status: "done",
current_phase: "done",
partial: false,
timeline: [
{
from_phase: null,
to_phase: "intake",
entered_at: "2026-06-23T00:00:00+00:00",
exited_at: "2026-06-23T00:00:01+00:00",
duration_s: 1,
status: "active",
note: null,
cost_usd: 0,
},
{
from_phase: "intake",
to_phase: "plan",
entered_at: "2026-06-23T00:00:01+00:00",
exited_at: null,
duration_s: null,
status: "active",
note: null,
cost_usd: 0.12,
},
],
qa_history: [{ turn: 0, answer: "scope it" }],
review_verdicts: [],
plan: { summary: "do the thing" },
cost_by_stage: { plan: 0.12 },
total_usd: 0.12,
};
describe("TaskDrawer", () => {
it("renders the timeline steps with the task description and spend", () => {
render(
<TaskDrawer
detail={DETAIL}
loading={false}
error={null}
onClose={() => {}}
/>,
);
expect(screen.getByText("remediate CVE-2026-1")).toBeInTheDocument();
expect(screen.getByText("intake")).toBeInTheDocument();
expect(screen.getByText("plan")).toBeInTheDocument();
expect(screen.getByText("in progress")).toBeInTheDocument(); // open final step
expect(screen.getByText(/spend \$0\.12/)).toBeInTheDocument();
});
it("shows a partial-history banner when partial", () => {
render(
<TaskDrawer
detail={{ ...DETAIL, partial: true }}
loading={false}
error={null}
onClose={() => {}}
/>,
);
expect(screen.getByText(/Partial history/)).toBeInTheDocument();
});
it("renders nothing when idle (no detail, not loading)", () => {
const { container } = render(
<TaskDrawer
detail={null}
loading={false}
error={null}
onClose={() => {}}
/>,
);
expect(container).toBeEmptyDOMElement();
});
it("renders review findings as markdown (bold, not raw **)", () => {
const d: TaskDetail = {
...DETAIL,
review_verdicts: [
{
verdict: "request_changes",
round_index: 1,
outcome: "loop_back",
findings: "**Missing phase** and\n1. run the tests",
},
],
};
render(
<TaskDrawer detail={d} loading={false} error={null} onClose={() => {}} />,
);
expect(screen.getByText("Missing phase").tagName).toBe("STRONG");
expect(screen.getByText("run the tests").closest("li")).not.toBeNull();
expect(screen.getByText("request changes")).toBeInTheDocument();
});
it("renders the plan as summary + phase/step lists", () => {
const d: TaskDetail = {
...DETAIL,
plan: {
summary: "Add a smoke test",
phases: [
{ name: "Recon", steps: ["read conftest", "read test_graph"] },
],
},
};
render(
<TaskDrawer detail={d} loading={false} error={null} onClose={() => {}} />,
);
expect(screen.getByText("Recon")).toBeInTheDocument();
expect(screen.getByText("read conftest").closest("li")).not.toBeNull();
});
it("renders both string and dict Q&A entries", () => {
const d: TaskDetail = {
...DETAIL,
qa_history: [
"plain answer text",
{ turn: 1, answer: "dict answer text" },
],
};
render(
<TaskDrawer detail={d} loading={false} error={null} onClose={() => {}} />,
);
expect(screen.getByText("plain answer text")).toBeInTheDocument();
expect(screen.getByText("dict answer text")).toBeInTheDocument();
});
it("does NOT render raw HTML embedded in agent text (no XSS)", () => {
const d: TaskDetail = {
...DETAIL,
review_verdicts: [
{
verdict: "approve",
round_index: 1,
outcome: "build",
findings: 'before <img src=x onerror="alert(1)"> after',
},
],
};
const { container } = render(
<TaskDrawer detail={d} loading={false} error={null} onClose={() => {}} />,
);
expect(container.querySelector("img")).toBeNull();
});
});