import { MantineProvider } from "@mantine/core"; import { act, cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import { useState } from "react"; import { afterEach, expect, test, vi } from "vitest"; import { fixtureTask } from "../fixtures"; import { ActivityTimeline } from "./ActivityTimeline"; import { ManualTransitionConfirmation, type TerminalStatus } from "./TaskDetail"; afterEach(() => { cleanup(); vi.restoreAllMocks(); }); test("clears a manual-transition confirmation only after success", async () => { const onTransition = vi.fn().mockResolvedValue({ ok: true }); render(); expect(screen.getByRole("alert")).toHaveTextContent("Mark task completed?"); fireEvent.click(screen.getByRole("button", { name: "Confirm" })); await waitFor(() => expect(onTransition).toHaveBeenCalledWith("completed", expect.any(String))); await waitFor(() => expect(screen.queryByRole("alert")).not.toBeInTheDocument()); await waitFor(() => expect(screen.getByRole("heading", { name: "Task title" })).toHaveFocus()); }); test("retains a manual-transition confirmation after failure", async () => { const onTransition = vi.fn().mockResolvedValue({ ok: false }); render(); fireEvent.click(screen.getByRole("button", { name: "Confirm" })); await waitFor(() => expect(onTransition).toHaveBeenCalledWith("completed", expect.any(String))); expect(screen.getByRole("alert")).toHaveTextContent("Mark task completed?"); }); test("reuses an action ID for retryable failures and rotates it after a stale task", async () => { const onTransition = vi.fn() .mockResolvedValueOnce({ ok: false }) .mockResolvedValueOnce({ ok: false, rotateActionId: true }) .mockResolvedValueOnce({ ok: false }); render(); const confirm = screen.getByRole("button", { name: "Confirm" }); fireEvent.click(confirm); await waitFor(() => expect(onTransition).toHaveBeenCalledTimes(1)); const firstActionId = onTransition.mock.calls[0][1]; fireEvent.click(confirm); await waitFor(() => expect(onTransition).toHaveBeenCalledTimes(2)); expect(onTransition.mock.calls[1][1]).toBe(firstActionId); fireEvent.click(confirm); await waitFor(() => expect(onTransition).toHaveBeenCalledTimes(3)); expect(onTransition.mock.calls[2][1]).not.toBe(firstActionId); }); test("covers terminal, active, and unavailable timeline metrics", () => { { const task = fixtureTask({ status: "completed", turns: [{ ...fixtureTask().turns![0], result: { status: "completed", summary: "Done.", updatedAt: "2026-08-30T08:18:32.000Z", }, }], usage: { status: "available", turns: { "turn-one": { status: "available", totalTokens: 1_324_567, estimatedCostUsd: 0.12 } }, }, }); const { container } = render( , ); const stats = container.querySelector(".taskchef-turn-stats") as HTMLElement; expect(stats).not.toBeNull(); expect(container.querySelector(".taskchef-turn-heading .taskchef-status-badge")) .toHaveTextContent("completed"); expect(screen.queryByText(/^Turn ref:/)).not.toBeInTheDocument(); const cells = [...stats.children] as HTMLElement[]; expect(cells).toHaveLength(4); expect(within(cells[0]).getByLabelText(/^Turn update time:/i)).toBeVisible(); expect(cells[0].querySelector(".tabler-icon-history")).not.toBeNull(); expect(within(cells[1]).getByLabelText(/completed turn reported wall-clock elapsed time/i)) .toHaveTextContent("18m 32s"); expect(cells[1].querySelector(".tabler-icon-hourglass")).not.toBeNull(); expect(within(cells[2]).getByLabelText("1,324,567 tokens")).toHaveTextContent("1.32M"); expect(cells[2].querySelector(".tabler-icon-stack-2")).not.toBeNull(); expect(within(cells[3]).getByLabelText("Estimated cost $0.12")).toHaveTextContent("0.12"); expect(within(cells[3]).getByLabelText("Estimated cost $0.12")).not.toHaveTextContent("$"); expect(cells[3].querySelector(".tabler-icon-currency-dollar")).not.toBeNull(); cleanup(); } { let now = Date.parse("2026-08-30T08:18:32.000Z"); let tick: (() => void) | undefined; vi.spyOn(Date, "now").mockImplementation(() => now); vi.spyOn(window, "setInterval").mockImplementation(((handler: TimerHandler) => { tick = handler as () => void; return 1; }) as typeof window.setInterval); vi.spyOn(window, "clearInterval").mockImplementation(() => undefined); const task = fixtureTask(); render(); expect(screen.getByLabelText(/current turn reported wall-clock elapsed time/i)) .toHaveTextContent("18m 32s"); expect(screen.getByLabelText("Tokens pending")).toHaveTextContent("Pending"); expect(screen.getByLabelText("Estimated cost pending")).toHaveTextContent("Pending"); act(() => { now += 2_000; tick?.(); }); expect(screen.getByLabelText(/current turn reported wall-clock elapsed time/i)) .toHaveTextContent("18m 34s"); cleanup(); vi.restoreAllMocks(); } { const task = fixtureTask({ status: "completed", turns: [{ ...fixtureTask().turns![0], startedAt: "2026-08-30T08:18:33.000Z", result: { status: "completed", summary: "Done.", updatedAt: "2026-08-30T08:18:32.000Z", }, }], usage: { status: "unavailable", turns: { "turn-one": { status: "unavailable", reason: "Manual dashboard turns do not have usage boundaries.", }, }, }, }); render(); expect(screen.getByLabelText(/elapsed unavailable.*reported wall-clock/i)) .toHaveTextContent("n/a"); expect(screen.getByText("Manual dashboard turns do not have usage boundaries.")).toBeVisible(); expect(screen.getByLabelText(/tokens unavailable: manual dashboard turns/i)).toBeVisible(); } }); function ConfirmationHarness({ onTransition }: { onTransition: (status: TerminalStatus, actionId: string) => Promise<{ ok: boolean; rotateActionId?: boolean }> }) { const [opened, setOpened] = useState(true); return (

Task title

{opened && ( setOpened(false)} onTransition={onTransition} status="completed" /> )}
); }