Files
ThothII/frontend/src/shell/f1-loop.test.tsx
T

62 lines
2.5 KiB
TypeScript

// frontend/src/shell/f1-loop.test.tsx
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { http, HttpResponse } from "msw";
import { act } from "react";
import { server } from "../test/msw";
import { FakeEventSource } from "../test/fakeEventSource";
import { App } from "../App";
import { useSessionStore } from "../store/sessionStore";
beforeEach(() => {
localStorage.clear();
FakeEventSource.instances = [];
(globalThis as any).EventSource = FakeEventSource;
useSessionStore.getState().resetSession();
});
test("F1: create session -> widget via SSE -> respond -> POST /response", async () => {
let responded: any = null;
server.use(
http.post("/api/sessions", () => HttpResponse.json({ id: "s1" })),
http.get("/api/sessions", () => HttpResponse.json([])),
http.get("/api/settings", () => HttpResponse.json({
workspace: "default", provider: "zai", model: "glm-5.2", thinking: "low",
})),
http.get("/api/workspaces", () => HttpResponse.json([])),
http.get("/api/models", () => HttpResponse.json({ models: [] })),
http.post("/api/sessions/s1/response", async ({ request }) => {
responded = await request.json();
return new HttpResponse(null, { status: 204 });
}),
);
render(<App />);
// Type the first question directly in the sticky composer and send it
const composer = await screen.findByRole("textbox", { name: /new question/i });
await userEvent.type(composer, "Quante vendite?");
await userEvent.click(screen.getByRole("button", { name: /send/i }));
// Wait for the session to be created and SSE to connect
await waitFor(() => expect(FakeEventSource.instances).toHaveLength(1));
// Optimistic lifecycle: a brand-new question paints F1 immediately (before any gate).
await waitFor(() => expect(useSessionStore.getState().currentPhase).toBe("F1"));
// Simulate the backend emitting the F1 widget via a NAMED SSE event
// (`event: ui_request`) — the same addEventListener path production uses.
act(() =>
FakeEventSource.instances[0].emitNamed("ui_request", {
type: "ui_request",
ui_request: {
id: "u1",
widget: "select",
title: "Disambigua",
options: [{ id: "a", label: "interpretazione A" }],
},
})
);
await userEvent.click(await screen.findByRole("button", { name: /interpretazione A/ }));
expect(responded).toEqual({ ui_response: { id: "u1", kind: "select", choices: ["a"] } });
});