import { render, screen, waitFor } from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { TestResultsResponse, TokenCostResponse } from "../api/client"; vi.mock("../api/client", async () => { const actual = await vi.importActual("../api/client"); return { ...actual, api: { ...actual.api, getWorkItemContent: vi.fn(), getTestResults: vi.fn(), getTokenCost: vi.fn(), }, }; }); vi.mock("../api/agents", () => ({ agentsApi: { listAgents: vi.fn(), getAgentConfig: vi.fn(), stopAgent: vi.fn(), startAgent: vi.fn(), }, subscribeAgentStream: vi.fn(() => () => {}), })); import { agentsApi, subscribeAgentStream } from "../api/agents"; import { api } from "../api/client"; const { WorkItemDetailPanel } = await import("./WorkItemDetailPanel"); const mockedGetWorkItemContent = vi.mocked(api.getWorkItemContent); const mockedGetTestResults = vi.mocked(api.getTestResults); const mockedGetTokenCost = vi.mocked(api.getTokenCost); const mockedListAgents = vi.mocked(agentsApi.listAgents); const mockedGetAgentConfig = vi.mocked(agentsApi.getAgentConfig); const mockedSubscribeAgentStream = vi.mocked(subscribeAgentStream); const DEFAULT_CONTENT = { content: "# Big Title\n\nSome content here.", stage: "current", name: "Big Title Story", agent: null, }; const sampleTestResults: TestResultsResponse = { unit: [ { name: "test_add", status: "pass", details: null }, { name: "test_subtract", status: "fail", details: "expected 3, got 4" }, ], integration: [{ name: "test_api_endpoint", status: "pass", details: null }], }; beforeEach(() => { vi.clearAllMocks(); mockedGetWorkItemContent.mockResolvedValue(DEFAULT_CONTENT); mockedGetTestResults.mockResolvedValue(null); mockedGetTokenCost.mockResolvedValue({ total_cost_usd: 0, agents: [] }); mockedListAgents.mockResolvedValue([]); mockedGetAgentConfig.mockResolvedValue([]); mockedSubscribeAgentStream.mockReturnValue(() => {}); }); afterEach(() => { vi.restoreAllMocks(); }); describe("WorkItemDetailPanel - Test Results", () => { it("shows empty test results message when no results exist", async () => { mockedGetTestResults.mockResolvedValue(null); render( {}} />, ); await waitFor(() => { expect(screen.getByTestId("test-results-empty")).toBeInTheDocument(); }); expect(screen.getByText("No test results recorded")).toBeInTheDocument(); }); it("shows unit and integration test results when available", async () => { mockedGetTestResults.mockResolvedValue(sampleTestResults); render( {}} />, ); await waitFor(() => { expect(screen.getByTestId("test-results-content")).toBeInTheDocument(); }); // Unit test section expect(screen.getByTestId("test-section-unit")).toBeInTheDocument(); expect( screen.getByText("Unit Tests (1 passed, 1 failed)"), ).toBeInTheDocument(); // Integration test section expect(screen.getByTestId("test-section-integration")).toBeInTheDocument(); expect( screen.getByText("Integration Tests (1 passed, 0 failed)"), ).toBeInTheDocument(); }); it("shows pass/fail status and details for each test", async () => { mockedGetTestResults.mockResolvedValue(sampleTestResults); render( {}} />, ); await waitFor(() => { expect(screen.getByTestId("test-case-test_add")).toBeInTheDocument(); }); // Passing test expect(screen.getByTestId("test-status-test_add")).toHaveTextContent( "PASS", ); expect(screen.getByText("test_add")).toBeInTheDocument(); // Failing test with details expect(screen.getByTestId("test-status-test_subtract")).toHaveTextContent( "FAIL", ); expect(screen.getByText("test_subtract")).toBeInTheDocument(); expect(screen.getByTestId("test-details-test_subtract")).toHaveTextContent( "expected 3, got 4", ); // Integration test expect( screen.getByTestId("test-status-test_api_endpoint"), ).toHaveTextContent("PASS"); }); it("re-fetches test results when pipelineVersion changes", async () => { mockedGetTestResults.mockResolvedValue(null); const { rerender } = render( {}} />, ); await waitFor(() => { expect(mockedGetTestResults).toHaveBeenCalledTimes(1); }); // Update with new results and bump pipelineVersion. mockedGetTestResults.mockResolvedValue(sampleTestResults); rerender( {}} />, ); await waitFor(() => { expect(mockedGetTestResults).toHaveBeenCalledTimes(2); }); await waitFor(() => { expect(screen.getByTestId("test-results-content")).toBeInTheDocument(); }); }); }); describe("WorkItemDetailPanel - Token Cost", () => { const sampleTokenCost: TokenCostResponse = { total_cost_usd: 0.012345, agents: [ { agent_name: "coder-1", model: "claude-sonnet-4-6", input_tokens: 1000, output_tokens: 500, cache_creation_input_tokens: 200, cache_read_input_tokens: 100, total_cost_usd: 0.009, }, { agent_name: "coder-2", model: null, input_tokens: 800, output_tokens: 300, cache_creation_input_tokens: 0, cache_read_input_tokens: 0, total_cost_usd: 0.003345, }, ], }; it("shows empty state when no token data exists", async () => { mockedGetTokenCost.mockResolvedValue({ total_cost_usd: 0, agents: [] }); render( {}} />, ); await waitFor(() => { expect(screen.getByTestId("token-cost-empty")).toBeInTheDocument(); }); expect(screen.getByText("No token data recorded")).toBeInTheDocument(); }); it("shows per-agent breakdown and total cost when data exists", async () => { mockedGetTokenCost.mockResolvedValue(sampleTokenCost); render( {}} />, ); await waitFor(() => { expect(screen.getByTestId("token-cost-content")).toBeInTheDocument(); }); expect(screen.getByTestId("token-cost-total")).toHaveTextContent( "$0.012345", ); expect(screen.getByTestId("token-cost-agent-coder-1")).toBeInTheDocument(); expect(screen.getByTestId("token-cost-agent-coder-2")).toBeInTheDocument(); }); it("shows agent name and model when model is present", async () => { mockedGetTokenCost.mockResolvedValue(sampleTokenCost); render( {}} />, ); await waitFor(() => { expect( screen.getByTestId("token-cost-agent-coder-1"), ).toBeInTheDocument(); }); const agentRow = screen.getByTestId("token-cost-agent-coder-1"); expect(agentRow).toHaveTextContent("coder-1"); expect(agentRow).toHaveTextContent("claude-sonnet-4-6"); }); it("shows agent name without model when model is null", async () => { mockedGetTokenCost.mockResolvedValue(sampleTokenCost); render( {}} />, ); await waitFor(() => { expect( screen.getByTestId("token-cost-agent-coder-2"), ).toBeInTheDocument(); }); const agentRow = screen.getByTestId("token-cost-agent-coder-2"); expect(agentRow).toHaveTextContent("coder-2"); expect(agentRow).not.toHaveTextContent("null"); }); it("re-fetches token cost when pipelineVersion changes", async () => { mockedGetTokenCost.mockResolvedValue({ total_cost_usd: 0, agents: [] }); const { rerender } = render( {}} />, ); await waitFor(() => { expect(mockedGetTokenCost).toHaveBeenCalledTimes(1); }); mockedGetTokenCost.mockResolvedValue(sampleTokenCost); rerender( {}} />, ); await waitFor(() => { expect(mockedGetTokenCost).toHaveBeenCalledTimes(2); }); await waitFor(() => { expect(screen.getByTestId("token-cost-content")).toBeInTheDocument(); }); }); });