import { MantineProvider } from "@mantine/core";
import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
import { afterEach, describe, expect, test, vi } from "vitest";
import { fixtureTask } from "../fixtures";
import { TaskCard } from "./TaskCard";
import { TaskCardStats } from "./TaskCardStats";
afterEach(cleanup);
function renderStats(task = fixtureTask()) {
return render();
}
describe("task card stats", () => {
test("renders the four metrics in order with compact values and full accessibility", () => {
const { container } = renderStats(fixtureTask({
status: "completed",
turns: undefined,
reportedWork: { terminalTurns: 1, validTurns: 1, totalMilliseconds: 1_112_000 },
usage: {
status: "available",
updatedAt: "2026-08-30T09:00:00.000Z",
task: {
totalTokens: 1_324_567,
estimatedCostUsd: 12.3449,
sourceUpdatedAt: "2026-08-30T09:00:00.000Z",
},
},
}));
const stats = container.querySelector(".taskchef-card-stats") as HTMLElement;
expect(stats).not.toBeNull();
const cells = [...stats.children] as HTMLElement[];
expect(cells).toHaveLength(4);
expect(within(cells[0]).getByLabelText(/updated time/i)).toBeVisible();
expect(within(cells[1]).getByLabelText(/reported work 18m 32s/i)).toBeVisible();
expect(within(cells[2]).getByLabelText(/1,324,567 tokens/i)).toHaveTextContent("1.32M tokens");
expect(within(cells[3]).getByLabelText(/estimated cost \$12\.34/i)).toHaveTextContent("12.34");
expect(within(cells[3]).getByLabelText(/estimated cost \$12\.34/i)).not.toHaveTextContent("$");
expect(within(cells[2]).getByRole("status")).toHaveTextContent(/1,324,567 tokens.*estimated cost \$12\.34/i);
});
test("keeps known tokens when cost is missing and never substitutes zero", () => {
renderStats(fixtureTask({
status: "completed",
usage: { status: "available", task: { totalTokens: 1_324_567, estimatedCostUsd: null } },
}));
expect(screen.getByLabelText(/1,324,567 tokens/i)).toHaveTextContent("1.32M tokens");
expect(screen.getByLabelText(/estimated cost unavailable/i)).toHaveTextContent("n/a");
});
test("shows the requested estimated-cost label to keyboard users", () => {
vi.useFakeTimers();
renderStats(fixtureTask({
status: "completed",
usage: { status: "available", task: { totalTokens: 1_324_567, estimatedCostUsd: 12.3449 } },
}));
const cost = screen.getByLabelText(/estimated cost \$12\.34/i);
fireEvent.focus(cost);
vi.advanceTimersByTime(150);
expect(screen.getByRole("tooltip")).toHaveTextContent("Estimated cost");
vi.useRealTimers();
});
test("shows pending, calculating, and unavailable states in both usage cells", () => {
const { rerender } = renderStats();
expect(screen.getByLabelText("Tokens pending")).toHaveTextContent("Pending");
expect(screen.getByLabelText("Estimated cost pending")).toHaveTextContent("Pending");
expect(screen.getAllByText("Pending").every((node) => node.classList.contains("taskchef-shimmer"))).toBe(true);
rerender();
expect(screen.getByLabelText("Tokens calculating")).toHaveTextContent("Calculating");
expect(screen.getByLabelText("Estimated cost calculating")).toHaveTextContent("Calculating");
rerender();
expect(screen.getByLabelText(/token usage unavailable: no matching/i)).toHaveTextContent("n/a tokens");
expect(screen.getByLabelText(/estimated cost unavailable: no matching/i)).toHaveTextContent("n/a");
});
test("retains known totals and marks both usage values as updating", () => {
const { container } = renderStats(fixtureTask({
status: "working",
turnRef: "turn-two",
usage: {
generationTurnRef: "turn-one",
status: "calculating",
task: { totalTokens: 1_324_567, estimatedCostUsd: 12.3449 },
},
}));
expect(screen.getByLabelText(/1,324,567 tokens; updating/i)).toHaveTextContent("1.32M tokens");
expect(screen.getByLabelText(/estimated cost \$12\.34; updating/i)).toHaveTextContent("12.34");
expect(container.querySelectorAll(".taskchef-shimmer")).toHaveLength(2);
});
test("retains terminal totals while a newer usage generation is calculating", () => {
const { container } = renderStats(fixtureTask({
status: "completed",
turnRef: "turn-two",
usage: {
generationTurnRef: "turn-one",
status: "calculating",
task: { totalTokens: 1_324_567, estimatedCostUsd: 12.3449 },
},
}));
expect(screen.getByLabelText(/1,324,567 tokens; updating/i)).toHaveTextContent("1.32M tokens");
expect(screen.getByLabelText(/estimated cost \$12\.34; updating/i)).toHaveTextContent("12.34");
expect(container.querySelectorAll(".taskchef-shimmer")).toHaveLength(2);
});
test("keeps unavailable cost static while retained tokens update", () => {
const { container } = renderStats(fixtureTask({
status: "working",
turnRef: "turn-two",
usage: {
generationTurnRef: "turn-one",
status: "calculating",
task: { totalTokens: 1_324_567, estimatedCostUsd: null },
},
}));
expect(screen.getByLabelText(/1,324,567 tokens; updating/i)).toHaveTextContent("1.32M tokens");
expect(screen.getByLabelText(/estimated cost unavailable/i)).toHaveTextContent("n/a");
expect(screen.getByLabelText(/estimated cost unavailable/i)).not.toHaveAccessibleName(/updating/i);
expect(container.querySelectorAll(".taskchef-shimmer")).toHaveLength(1);
});
test("places statistics and icon-only Open chat together in the card footer", () => {
const openChat = vi.fn();
const openDetail = vi.fn();
const { container } = render();
const metadata = container.querySelector(".taskchef-card-metadata") as HTMLElement;
const footer = container.querySelector(".taskchef-list-card-footer") as HTMLElement;
const stats = container.querySelector(".taskchef-card-stats") as HTMLElement;
expect(metadata.children[0]).toHaveClass("taskchef-status-badge");
expect(metadata.children).toHaveLength(1);
expect(footer.children[0]).toBe(stats);
const button = within(footer).getByRole("button", { name: /open chat/i });
expect(footer.children[1]).toBe(button);
expect(button).toHaveClass("taskchef-board-chat");
expect(button).not.toHaveTextContent("Open chat");
fireEvent.click(button);
fireEvent.click(screen.getByRole("button", { name: /^review checkout reconciliation$/i }));
expect(openChat).toHaveBeenCalledOnce();
expect(openDetail).toHaveBeenCalledOnce();
});
test("toggles the updated time while keeping the selected History icon", () => {
const { container } = renderStats(fixtureTask({ meaningfulUpdatedAt: "2026-08-30T08:00:00.000Z" }));
const time = screen.getByLabelText(/updated time for review checkout reconciliation/i);
expect(container.querySelector(".tabler-icon-history")).not.toBeNull();
fireEvent.click(time);
expect(time).toHaveAccessibleName(/show relative time/i);
});
});