4fa4f13558
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
219 lines
7.0 KiB
TypeScript
219 lines
7.0 KiB
TypeScript
import { createElement } from "react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
|
|
import type { AgentState } from "@/features/agents/state/store";
|
|
import { AgentBrainPanel } from "@/features/agents/components/AgentInspectPanels";
|
|
import type { GatewayClient } from "@/lib/gateway/GatewayClient";
|
|
|
|
const createAgent = (agentId: string, name: string, sessionKey: string): AgentState => ({
|
|
agentId,
|
|
name,
|
|
sessionKey,
|
|
status: "idle",
|
|
sessionCreated: true,
|
|
awaitingUserInput: false,
|
|
hasUnseenActivity: false,
|
|
outputLines: [],
|
|
lastResult: null,
|
|
lastDiff: null,
|
|
runId: null,
|
|
runStartedAt: null,
|
|
streamText: null,
|
|
thinkingTrace: null,
|
|
latestOverride: null,
|
|
latestOverrideKind: null,
|
|
lastAssistantMessageAt: null,
|
|
lastActivityAt: null,
|
|
latestPreview: null,
|
|
lastUserMessage: null,
|
|
draft: "",
|
|
sessionSettingsSynced: true,
|
|
historyLoadedAt: null,
|
|
historyFetchLimit: null,
|
|
historyFetchedCount: null,
|
|
historyMaybeTruncated: false,
|
|
toolCallingEnabled: true,
|
|
showThinkingTraces: true,
|
|
model: null,
|
|
thinkingLevel: null,
|
|
avatarSeed: `seed-${agentId}`,
|
|
avatarUrl: null,
|
|
});
|
|
|
|
const createMockClient = () => {
|
|
const filesByAgent: Record<string, Record<string, string>> = {
|
|
"agent-1": {
|
|
"AGENTS.md": "alpha agents",
|
|
"SOUL.md": "# SOUL.md - Who You Are\n\n## Core Truths\n\nBe useful.",
|
|
"IDENTITY.md": "# IDENTITY.md - Who Am I?\n\n- Name: Alpha\n- Creature: droid\n- Vibe: calm\n- Emoji: 🤖\n",
|
|
"USER.md": "# USER.md - About Your Human\n\n- Name: George\n- What to call them: GP\n\n## Context\n\nBuilding Claw3D.",
|
|
"TOOLS.md": "tool notes",
|
|
"HEARTBEAT.md": "heartbeat notes",
|
|
"MEMORY.md": "durable memory",
|
|
},
|
|
"agent-2": {
|
|
"AGENTS.md": "beta agents",
|
|
},
|
|
};
|
|
|
|
const calls: Array<{ method: string; params: unknown }> = [];
|
|
|
|
const client = {
|
|
call: vi.fn(async (method: string, params: unknown) => {
|
|
calls.push({ method, params });
|
|
if (method === "agents.files.get") {
|
|
const record = params && typeof params === "object" ? (params as Record<string, unknown>) : {};
|
|
const agentId = typeof record.agentId === "string" ? record.agentId : "";
|
|
const name = typeof record.name === "string" ? record.name : "";
|
|
const content = filesByAgent[agentId]?.[name];
|
|
if (typeof content !== "string") {
|
|
return { file: { name, missing: true } };
|
|
}
|
|
return { file: { name, missing: false, content } };
|
|
}
|
|
if (method === "agents.files.set") {
|
|
const record = params && typeof params === "object" ? (params as Record<string, unknown>) : {};
|
|
const agentId = typeof record.agentId === "string" ? record.agentId : "";
|
|
const name = typeof record.name === "string" ? record.name : "";
|
|
const content = typeof record.content === "string" ? record.content : "";
|
|
if (!filesByAgent[agentId]) {
|
|
filesByAgent[agentId] = {};
|
|
}
|
|
filesByAgent[agentId][name] = content;
|
|
return { ok: true };
|
|
}
|
|
return {};
|
|
}),
|
|
} as unknown as GatewayClient;
|
|
|
|
return { client, calls, filesByAgent };
|
|
};
|
|
|
|
describe("AgentBrainPanel", () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
it("renders_behavior_sections_and_loads_agent_files", async () => {
|
|
const { client } = createMockClient();
|
|
const agents = [
|
|
createAgent("agent-1", "Alpha", "session-1"),
|
|
createAgent("agent-2", "Beta", "session-2"),
|
|
];
|
|
|
|
render(
|
|
createElement(AgentBrainPanel, {
|
|
client,
|
|
agents,
|
|
selectedAgentId: "agent-1",
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument();
|
|
});
|
|
|
|
expect(screen.getByRole("heading", { name: "Directives" })).toBeInTheDocument();
|
|
expect(screen.getByRole("heading", { name: "Context" })).toBeInTheDocument();
|
|
expect(screen.getByRole("heading", { name: "Identity" })).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Directives")).toHaveValue("alpha agents");
|
|
expect(screen.getByLabelText("Persona")).toHaveValue(
|
|
"# SOUL.md - Who You Are\n\n## Core Truths\n\nBe useful."
|
|
);
|
|
expect(screen.getByLabelText("Name")).toHaveValue("Alpha");
|
|
});
|
|
|
|
it("shows_actionable_message_when_session_key_missing", async () => {
|
|
const { client } = createMockClient();
|
|
const agents = [createAgent("", "Alpha", "session-1")];
|
|
|
|
render(
|
|
createElement(AgentBrainPanel, {
|
|
client,
|
|
agents,
|
|
selectedAgentId: "",
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Agent ID is missing for this agent.")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("saves_updated_behavior_files", async () => {
|
|
const { client, calls, filesByAgent } = createMockClient();
|
|
const agents = [createAgent("agent-1", "Alpha", "session-1")];
|
|
|
|
render(
|
|
createElement(AgentBrainPanel, {
|
|
client,
|
|
agents,
|
|
selectedAgentId: "agent-1",
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByLabelText("Directives")).toBeInTheDocument();
|
|
});
|
|
|
|
fireEvent.change(screen.getByLabelText("Directives"), {
|
|
target: { value: "alpha directives updated" },
|
|
});
|
|
|
|
const saveButton = screen.getByRole("button", { name: "Save" });
|
|
expect(saveButton).not.toBeDisabled();
|
|
fireEvent.click(saveButton);
|
|
|
|
await waitFor(() => {
|
|
expect(calls.some((entry) => entry.method === "agents.files.set")).toBe(true);
|
|
});
|
|
expect(filesByAgent["agent-1"]["AGENTS.md"]).toBe("alpha directives updated");
|
|
});
|
|
|
|
it("discards_unsaved_changes_without_writing_files", async () => {
|
|
const { client, calls } = createMockClient();
|
|
const agents = [createAgent("agent-1", "Alpha", "session-1")];
|
|
|
|
render(
|
|
createElement(AgentBrainPanel, {
|
|
client,
|
|
agents,
|
|
selectedAgentId: "agent-1",
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByLabelText("Name")).toBeInTheDocument();
|
|
});
|
|
|
|
fireEvent.change(screen.getByLabelText("Name"), {
|
|
target: { value: "Alpha Prime" },
|
|
});
|
|
expect(screen.getByLabelText("Name")).toHaveValue("Alpha Prime");
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Discard" }));
|
|
expect(screen.getByLabelText("Name")).toHaveValue("Alpha");
|
|
expect(calls.some((entry) => entry.method === "agents.files.set")).toBe(false);
|
|
});
|
|
|
|
it("does_not_render_name_editor_in_personality_panel", async () => {
|
|
const { client } = createMockClient();
|
|
const agents = [createAgent("agent-1", "Alpha", "session-1")];
|
|
|
|
render(
|
|
createElement(AgentBrainPanel, {
|
|
client,
|
|
agents,
|
|
selectedAgentId: "agent-1",
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument();
|
|
});
|
|
expect(screen.queryByLabelText("Agent name")).not.toBeInTheDocument();
|
|
expect(screen.queryByRole("button", { name: "Update Name" })).not.toBeInTheDocument();
|
|
});
|
|
});
|