import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { ExperimentalAgentOnboardingModal } from "../ExperimentalAgentOnboardingModal";
let streamHandlers: any;
const { mockCancel } = vi.hoisted(() => ({
mockCancel: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("../../api", () => ({
startAgentOnboardingStreaming: vi.fn().mockResolvedValue({ sessionId: "onb-1" }),
connectAgentOnboardingStream: vi.fn().mockImplementation((_sessionId, _projectId, handlers) => {
streamHandlers = handlers;
setTimeout(() => handlers.onQuestion?.({ id: "q1", type: "text", question: "What should this agent primarily help with?" }), 0);
return { close: vi.fn(), isConnected: vi.fn(() => true) };
}),
respondToAgentOnboarding: vi.fn().mockImplementation(() => {
setTimeout(
() =>
streamHandlers?.onSummary?.({
name: "Docs Reviewer",
role: "reviewer",
instructionsText: "Review docs for accuracy and clarity.",
thinkingLevel: "medium",
maxTurns: 20,
soul: "Thorough and empathetic reviewer.",
memory: "- Follow docs style guide\n- Call out unclear steps",
skills: ["docs", "review"],
templateId: "reviewer-template",
rationale: "Matched your request to the reviewer preset",
}),
0,
);
return Promise.resolve({ type: "question", data: {} });
}),
cancelAgentOnboarding: mockCancel,
}));
describe("ExperimentalAgentOnboardingModal", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("walks onboarding flow and hands draft to create form", async () => {
const onUseDraft = vi.fn();
render(
,
);
fireEvent.change(screen.getByLabelText("What should this new agent own?"), { target: { value: "Review docs" } });
fireEvent.click(screen.getByText("Start onboarding"));
await screen.findByText("What should this agent primarily help with?");
fireEvent.change(screen.getByLabelText("What should this agent primarily help with?"), { target: { value: "Docs" } });
fireEvent.click(screen.getByText("Continue"));
await screen.findByText("Draft ready for review");
expect(screen.getByText("Template:")).toBeTruthy();
expect(screen.getByText("reviewer-template")).toBeTruthy();
expect(screen.getByText(/Matched your request/)).toBeTruthy();
expect(screen.getByText("Soul / personality")).toBeTruthy();
expect(screen.getByText("Thorough and empathetic reviewer.")).toBeTruthy();
expect(screen.getByText("Core instructions")).toBeTruthy();
expect(screen.getByText("Review docs for accuracy and clarity.")).toBeTruthy();
expect(screen.getByText("Runtime hints")).toBeTruthy();
expect(screen.getByText("Thinking level:")).toBeTruthy();
expect(screen.getByText("Max turns:")).toBeTruthy();
expect(screen.getByText("Starter memory / playbook")).toBeTruthy();
expect(screen.getByText(/Follow docs style guide/)).toBeTruthy();
expect(screen.getByText("Skills")).toBeTruthy();
expect(screen.getByText("docs, review")).toBeTruthy();
fireEvent.click(screen.getByText("Continue to agent form"));
await waitFor(() => {
expect(onUseDraft).toHaveBeenCalledWith(expect.objectContaining({ name: "Docs Reviewer" }));
});
});
it("renders stream errors and still closes cleanly", async () => {
render(
,
);
fireEvent.change(screen.getByLabelText("What should this new agent own?"), { target: { value: "Review docs" } });
fireEvent.click(screen.getByText("Start onboarding"));
await waitFor(() => {
streamHandlers?.onError?.("Service unavailable");
expect(screen.getByText("Service unavailable")).toBeTruthy();
});
});
it("cancels server session on close", async () => {
const onClose = vi.fn();
render(
,
);
fireEvent.change(screen.getByLabelText("What should this new agent own?"), { target: { value: "Review docs" } });
fireEvent.click(screen.getByText("Start onboarding"));
await screen.findByText("What should this agent primarily help with?");
fireEvent.click(screen.getByLabelText("Close"));
await waitFor(() => {
expect(mockCancel).toHaveBeenCalledWith("onb-1", undefined);
expect(onClose).toHaveBeenCalled();
});
});
it("resets onboarding state when closed and reopened", async () => {
const { rerender } = render(
,
);
fireEvent.change(screen.getByLabelText("What should this new agent own?"), { target: { value: "Review docs" } });
fireEvent.click(screen.getByText("Start onboarding"));
await screen.findByText("What should this agent primarily help with?");
rerender(
,
);
rerender(
,
);
expect(screen.getByLabelText("What should this new agent own?")).toBeTruthy();
expect(screen.queryByText("What should this agent primarily help with?")).toBeNull();
});
it("always closes even when session cancel request fails", async () => {
mockCancel.mockRejectedValueOnce(new Error("cancel failed"));
const onClose = vi.fn();
render(
,
);
fireEvent.change(screen.getByLabelText("What should this new agent own?"), { target: { value: "Review docs" } });
fireEvent.click(screen.getByText("Start onboarding"));
await screen.findByText("What should this agent primarily help with?");
fireEvent.click(screen.getByLabelText("Close"));
await waitFor(() => {
expect(mockCancel).toHaveBeenCalledWith("onb-1", undefined);
expect(onClose).toHaveBeenCalled();
});
});
});