266 lines
6.5 KiB
TypeScript
266 lines
6.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import "@testing-library/jest-dom";
|
|
import { AgentDetailView } from "../AgentDetailView";
|
|
import type { AgentCapability, AgentDetail } from "../../api";
|
|
|
|
// Mock the API functions
|
|
vi.mock("../../api", () => ({
|
|
fetchAgent: vi.fn(),
|
|
updateAgentState: vi.fn(),
|
|
deleteAgent: vi.fn(),
|
|
fetchAgentLogs: vi.fn(),
|
|
}));
|
|
|
|
import { fetchAgent, updateAgentState } from "../../api";
|
|
|
|
const mockFetchAgent = vi.mocked(fetchAgent);
|
|
const mockUpdateAgentState = vi.mocked(updateAgentState);
|
|
|
|
describe("AgentDetailView", () => {
|
|
const createMockAgent = (overrides: Partial<{
|
|
id: string;
|
|
name: string;
|
|
role: AgentCapability;
|
|
state: "idle" | "active" | "paused" | "terminated";
|
|
taskId?: string;
|
|
}> = {}): AgentDetail => ({
|
|
id: "agent-001",
|
|
name: "Test Agent",
|
|
role: "executor" as AgentCapability,
|
|
state: "active",
|
|
taskId: "FN-001",
|
|
createdAt: "2024-01-01T00:00:00.000Z",
|
|
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
lastHeartbeatAt: "2024-01-01T00:05:00.000Z",
|
|
metadata: {},
|
|
heartbeatHistory: [],
|
|
activeRun: {
|
|
id: "run-001",
|
|
agentId: "agent-001",
|
|
startedAt: "2024-01-01T00:00:00.000Z",
|
|
endedAt: null,
|
|
status: "active",
|
|
},
|
|
completedRuns: [
|
|
{
|
|
id: "run-002",
|
|
agentId: "agent-001",
|
|
startedAt: "2023-12-31T00:00:00.000Z",
|
|
endedAt: "2023-12-31T00:05:00.000Z",
|
|
status: "completed",
|
|
},
|
|
],
|
|
...overrides,
|
|
} as AgentDetail);
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockFetchAgent.mockResolvedValue(createMockAgent());
|
|
mockUpdateAgentState.mockResolvedValue(createMockAgent({ state: "paused" }));
|
|
});
|
|
|
|
it("shows loading state initially", () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/Loading agent/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("displays agent name in header after loading", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
// Wait for the h2 element specifically (the header title)
|
|
await waitFor(() => {
|
|
const headings = screen.getAllByRole("heading", { level: 2 });
|
|
expect(headings.some(h => h.textContent === "Test Agent")).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("displays role badge", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("executor")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("displays state badge", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
// There should be at least one element with "active" (could be in badge or inline-badge)
|
|
expect(screen.getAllByText("active").length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it("shows all tabs", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Dashboard")).toBeInTheDocument();
|
|
expect(screen.getByText("Logs")).toBeInTheDocument();
|
|
expect(screen.getByText("Runs")).toBeInTheDocument();
|
|
expect(screen.getByText("Settings")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("shows Pause button for active agent", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Pause")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("shows Resume button for paused agent", async () => {
|
|
mockFetchAgent.mockResolvedValue(createMockAgent({ state: "paused" }));
|
|
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Resume")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("shows Delete button for terminated agent", async () => {
|
|
mockFetchAgent.mockResolvedValue(createMockAgent({ state: "terminated" }));
|
|
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Delete")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("shows statistics section on dashboard", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Total Runs")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("displays agent ID in footer", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("agent-001")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("calls API with correct agentId", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(mockFetchAgent).toHaveBeenCalledWith("agent-001");
|
|
});
|
|
});
|
|
|
|
it("displays health status indicator", async () => {
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
// Health status should be either Healthy, Unresponsive, or Idle
|
|
const healthTexts = ["Healthy", "Unresponsive", "Idle"];
|
|
const hasHealthStatus = healthTexts.some(text =>
|
|
document.body.textContent?.includes(text)
|
|
);
|
|
expect(hasHealthStatus).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("shows Live Run on runs tab when agent has active run", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<AgentDetailView
|
|
agentId="agent-001"
|
|
onClose={vi.fn()}
|
|
addToast={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Dashboard")).toBeInTheDocument();
|
|
});
|
|
|
|
await user.click(screen.getByText("Runs"));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Live Run")).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|