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
Adam Moussa 8530074551 ci(agent-team-web): wire Prettier + ESLint + Playwright and enable the CI steps (#65)
- Prettier 3.6.2: .prettierrc.json + .prettierignore + format/format:check
  scripts; formatted the web/ tree.
- ESLint 9 flat config (@eslint/js + typescript-eslint + react-hooks +
  react-refresh, eslint-config-prettier last so Prettier owns formatting);
  lint script; src/components/ui/** opts out of react-refresh (shadcn primitives
  co-export their cva variants). 0 problems.
- Playwright 1.61.1: playwright.config.ts (build+preview webServer) + an e2e
  Board-renders smoke against a mocked /api; test:e2e script. Reusable workflow
  installs chromium itself.
- ci-web.yaml: required-scripts -> format:check,lint,build,test,test:e2e;
  run-format-check/lint/e2e -> true; e2e-browser chromium.

All five scripts verified green locally.
2026-06-25 10:56:48 -04:00

153 lines
4.3 KiB
TypeScript

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();
});
});