feat(frontend): SettingsDialog with workspace/provider/model/thinking selects

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:26:32 +02:00
co-authored by Claude Opus 4.8
parent 32d963b43d
commit 29b7e85510
2 changed files with 321 additions and 0 deletions
@@ -0,0 +1,93 @@
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 { SettingsDialog } from "./SettingsDialog";
function renderDialog() {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
render(
<QueryClientProvider client={client}>
<SettingsDialog />
</QueryClientProvider>,
);
}
beforeEach(() => {
server.use(
http.get("http://localhost:8787/workspaces", () =>
HttpResponse.json([
{ name: "psd", file: "psd.yaml" },
{ name: "tht-test", file: "tht-test.yaml" },
]),
),
http.get("http://localhost:8787/models", () =>
HttpResponse.json({
models: [
{ provider: "zai", id: "glm-5.2", name: "GLM 5.2", reasoning: true },
{ provider: "anthropic", id: "claude-opus-4-8", name: "Claude Opus 4.8", reasoning: true },
],
}),
),
http.get("http://localhost:8787/settings", () =>
HttpResponse.json({ workspace: "psd", provider: "zai", model: "glm-5.2", thinking: "medium" }),
),
);
});
test("opens and pre-selects current settings", async () => {
renderDialog();
await userEvent.click(screen.getByRole("button", { name: /settings/i }));
const workspace = (await screen.findByLabelText(/workspace/i)) as HTMLSelectElement;
expect(workspace.value).toBe("psd");
const thinking = screen.getByLabelText(/thinking/i) as HTMLSelectElement;
expect(thinking.value).toBe("medium");
// thinking options are exactly low/medium/high
expect(Array.from(thinking.options).map((o) => o.value)).toEqual(["low", "medium", "high"]);
});
test("provider change filters the model list", async () => {
renderDialog();
await userEvent.click(screen.getByRole("button", { name: /settings/i }));
const provider = (await screen.findByLabelText(/provider/i)) as HTMLSelectElement;
await userEvent.selectOptions(provider, "anthropic");
const model = screen.getByLabelText(/modello/i) as HTMLSelectElement;
const optionValues = Array.from(model.options).map((o) => o.value);
expect(optionValues).toContain("claude-opus-4-8");
expect(optionValues).not.toContain("glm-5.2");
});
test("Salva PUTs the selected settings", async () => {
let body: unknown = null;
server.use(
http.put("http://localhost:8787/settings", async ({ request }) => {
body = await request.json();
return HttpResponse.json(await request.json());
}),
);
renderDialog();
await userEvent.click(screen.getByRole("button", { name: /settings/i }));
await screen.findByLabelText(/workspace/i);
await userEvent.selectOptions(screen.getByLabelText(/thinking/i), "high");
await userEvent.click(screen.getByRole("button", { name: /salva/i }));
await waitFor(() =>
expect(body).toEqual({ workspace: "psd", provider: "zai", model: "glm-5.2", thinking: "high" }),
);
});
test("degrades to free-text provider/model when models list is empty", async () => {
server.use(
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [] })),
http.get("http://localhost:8787/settings", () =>
HttpResponse.json({ workspace: "psd", provider: "", model: "", thinking: "low" }),
),
);
renderDialog();
await userEvent.click(screen.getByRole("button", { name: /settings/i }));
expect(await screen.findByTestId("provider-freetext")).toHaveProperty("tagName", "INPUT");
expect(screen.getByTestId("model-freetext")).toHaveProperty("tagName", "INPUT");
});