121 lines
5.0 KiB
TypeScript
121 lines
5.0 KiB
TypeScript
// frontend/src/shell/NewSessionDialog.test.tsx
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { http, HttpResponse } from "msw";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { server } from "../test/msw";
|
|
import { canonicalWorkspaceFixture, workspaceRevisionFixture, workspaceSummaryFixture } from "../test/workspace-fixtures";
|
|
import { NewSessionDialog } from "./NewSessionDialog";
|
|
import { workspacePreferences } from "../workspaces/preferences";
|
|
|
|
function renderDialog() {
|
|
const onCreated = vi.fn();
|
|
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
render(
|
|
<QueryClientProvider client={client}>
|
|
<NewSessionDialog onCreated={onCreated} />
|
|
</QueryClientProvider>,
|
|
);
|
|
return { onCreated };
|
|
}
|
|
|
|
test("the form has only a question field (no workspace/model/provider/thinking)", async () => {
|
|
renderDialog();
|
|
await userEvent.click(screen.getByRole("button", { name: /new/i }));
|
|
expect(await screen.findByLabelText(/question/i)).toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/workspace/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/model/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/provider/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/thinking/i)).not.toBeInTheDocument();
|
|
});
|
|
|
|
test("submitting includes ephemeral migrated preferences and calls onCreated", async () => {
|
|
localStorage.clear();
|
|
let body: unknown = null;
|
|
server.use(
|
|
http.get("/api/settings", () => HttpResponse.json({
|
|
workspace: "default", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
})),
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("default", {
|
|
displayName: "Default",
|
|
revision: workspaceRevisionFixture("default"),
|
|
}),
|
|
}])),
|
|
http.get("/api/workspaces/default", () => HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("default", ["zai/glm-5.2"], "zai/glm-5.2"),
|
|
revision: workspaceRevisionFixture("default"),
|
|
})),
|
|
http.post("/api/sessions", async ({ request }) => {
|
|
body = await request.json();
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
const { onCreated } = renderDialog();
|
|
await userEvent.click(screen.getByRole("button", { name: /new/i }));
|
|
await userEvent.type(await screen.findByLabelText(/question/i), "Quante vendite nel 2025?");
|
|
await userEvent.click(screen.getByRole("button", { name: /^create$/i }));
|
|
|
|
await waitFor(() => expect(body).toEqual({
|
|
question: "Quante vendite nel 2025?",
|
|
workspaceId: "default", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
}));
|
|
await waitFor(() => expect(onCreated).toHaveBeenCalledWith("s1"));
|
|
});
|
|
|
|
test("first-run direct dialog creation waits for registry policy without a mounted footer", async () => {
|
|
let body: unknown;
|
|
let releasePolicy!: () => void;
|
|
let summaryRequestStarted = false;
|
|
let policyRequestStarted = false;
|
|
const policyMayFinish = new Promise<void>((resolve) => { releasePolicy = resolve; });
|
|
const revision = {
|
|
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
|
|
};
|
|
workspacePreferences.save({
|
|
workspaceId: "psd-clinical", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
|
|
});
|
|
server.use(
|
|
http.get("/api/workspaces", () => {
|
|
summaryRequestStarted = true;
|
|
return HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", { displayName: "PSD Clinical", revision: revision as any }),
|
|
}]);
|
|
}),
|
|
http.get("/api/workspaces/psd-clinical", async () => {
|
|
policyRequestStarted = true;
|
|
await policyMayFinish;
|
|
return HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("psd-clinical", ["zai/glm-5.2"], "zai/glm-5.2"), revision,
|
|
});
|
|
}),
|
|
http.post("/api/sessions", async ({ request }) => {
|
|
body = await request.json();
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
const { onCreated } = renderDialog();
|
|
await userEvent.click(screen.getByRole("button", { name: /new/i }));
|
|
await userEvent.type(await screen.findByLabelText(/question/i), "q");
|
|
await userEvent.click(screen.getByRole("button", { name: /^create$/i }));
|
|
|
|
await waitFor(() => expect(summaryRequestStarted).toBe(true));
|
|
await waitFor(() => expect(policyRequestStarted).toBe(true));
|
|
expect(body).toBeUndefined();
|
|
expect(screen.getByRole("button", { name: /creating/i })).toBeDisabled();
|
|
releasePolicy();
|
|
|
|
await waitFor(() => expect(body).toEqual({
|
|
question: "q", workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "medium",
|
|
}));
|
|
await waitFor(() => expect(onCreated).toHaveBeenCalledWith("s1"));
|
|
});
|
|
|
|
test("empty question shows a validation error and does not submit", async () => {
|
|
renderDialog();
|
|
await userEvent.click(screen.getByRole("button", { name: /new/i }));
|
|
await userEvent.click(screen.getByRole("button", { name: /^create$/i }));
|
|
expect(await screen.findByRole("alert")).toHaveTextContent(/empty/i);
|
|
});
|