This merge delivers v0.12.0, which includes a new experimental agent onboarding modal (`ExperimentalAgentOnboardingModal`) that streamlines the handoff from onboarding to the create-agent form, along with backend lifecycle tests and documentation for the experimental flag. The release bumps all work Fusion-Task-Id: FN-3024
174 lines
5.6 KiB
TypeScript
174 lines
5.6 KiB
TypeScript
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",
|
|
thinkingLevel: "medium",
|
|
maxTurns: 20,
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={vi.fn()}
|
|
onUseDraft={onUseDraft}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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();
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={vi.fn()}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={onClose}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={vi.fn()}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={false}
|
|
onClose={vi.fn()}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
rerender(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={vi.fn()}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<ExperimentalAgentOnboardingModal
|
|
isOpen={true}
|
|
onClose={onClose}
|
|
onUseDraft={vi.fn()}
|
|
existingAgents={[]}
|
|
/>,
|
|
);
|
|
|
|
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();
|
|
});
|
|
});
|
|
});
|