feat(KB-662): complete Step 2 — add visibility refresh tests for useProjects
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,13 +3,13 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { TaskComments } from "../TaskComments";
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
addComment: vi.fn(),
|
||||
addSteeringComment: vi.fn(),
|
||||
addTaskComment: vi.fn(),
|
||||
updateTaskComment: vi.fn(),
|
||||
deleteTaskComment: vi.fn(),
|
||||
}));
|
||||
|
||||
import { addComment, addTaskComment, updateTaskComment, deleteTaskComment } from "../../api";
|
||||
import { addSteeringComment, addTaskComment, updateTaskComment, deleteTaskComment } from "../../api";
|
||||
|
||||
const makeTask = (overrides: any = {}) => ({
|
||||
id: "FN-001",
|
||||
@@ -155,8 +155,8 @@ describe("TaskComments", () => {
|
||||
expect(screen.getByText(/AI Guidance comments are injected into the task execution context/)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("uses addComment API when AI Guidance type is selected", async () => {
|
||||
vi.mocked(addComment).mockResolvedValue(makeTask({
|
||||
it("uses addSteeringComment API when AI Guidance type is selected", async () => {
|
||||
vi.mocked(addSteeringComment).mockResolvedValue(makeTask({
|
||||
comments: [{ id: "c1", text: "Guidance text", author: "user", createdAt: "2026-01-01T00:00:00.000Z" }],
|
||||
}));
|
||||
|
||||
@@ -170,7 +170,7 @@ describe("TaskComments", () => {
|
||||
fireEvent.click(screen.getByText("Add Guidance"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(addComment).toHaveBeenCalledWith("FN-001", "Guidance text");
|
||||
expect(addSteeringComment).toHaveBeenCalledWith("FN-001", "Guidance text");
|
||||
expect(addTaskComment).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -188,7 +188,7 @@ describe("TaskComments", () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(addTaskComment).toHaveBeenCalledWith("FN-001", "User text", "user");
|
||||
expect(addComment).not.toHaveBeenCalled();
|
||||
expect(addSteeringComment).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user