feat: harden runtime readiness and session workflow
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { http, HttpResponse, delay } from "msw";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { server } from "../test/msw";
|
||||
import { FakeEventSource } from "../test/fakeEventSource";
|
||||
import { useSessionStore } from "../store/sessionStore";
|
||||
import { AppShell } from "./AppShell";
|
||||
|
||||
function renderShell() {
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
return render(<QueryClientProvider client={client}><AppShell /></QueryClientProvider>);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
FakeEventSource.instances = [];
|
||||
(globalThis as any).EventSource = FakeEventSource;
|
||||
useSessionStore.getState().resetSession();
|
||||
server.use(
|
||||
http.get("http://localhost:8787/sessions", () => HttpResponse.json([])),
|
||||
http.get("http://localhost:8787/settings", () =>
|
||||
HttpResponse.json({ workspace: "default", provider: "test", model: "test", thinking: "low" })),
|
||||
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([])),
|
||||
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [] })),
|
||||
);
|
||||
});
|
||||
|
||||
test("New session starts prewarm without delaying composer focus", async () => {
|
||||
let prewarmStarted = false;
|
||||
server.use(
|
||||
http.post("http://localhost:8787/runtime/prewarm", async () => {
|
||||
prewarmStarted = true;
|
||||
await delay(100);
|
||||
return new HttpResponse(null, { status: 202 });
|
||||
}),
|
||||
);
|
||||
renderShell();
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: /^new session$/i }));
|
||||
|
||||
const composer = screen.getByRole("textbox", { name: /new question/i });
|
||||
await waitFor(() => expect(prewarmStarted).toBe(true));
|
||||
await waitFor(() => expect(composer).toHaveFocus());
|
||||
});
|
||||
|
||||
test("shows the question and creation timer before POST /sessions resolves, then opens SSE", async () => {
|
||||
let releaseCreate!: () => void;
|
||||
const createMayFinish = new Promise<void>((resolve) => { releaseCreate = resolve; });
|
||||
server.use(
|
||||
http.post("http://localhost:8787/sessions", async () => {
|
||||
await createMayFinish;
|
||||
return HttpResponse.json({ id: "s-new" });
|
||||
}),
|
||||
);
|
||||
renderShell();
|
||||
|
||||
const composer = screen.getByRole("textbox", { name: /new question/i });
|
||||
await userEvent.type(composer, "How many patients?");
|
||||
await userEvent.click(screen.getByRole("button", { name: /send/i }));
|
||||
|
||||
expect(await screen.findByText("How many patients?", { selector: "p" })).toBeInTheDocument();
|
||||
expect(screen.getByText("Creating session…")).toBeInTheDocument();
|
||||
expect(screen.getByText(/\d+s/)).toBeInTheDocument();
|
||||
expect(FakeEventSource.instances).toHaveLength(0);
|
||||
|
||||
releaseCreate();
|
||||
await waitFor(() => expect(FakeEventSource.instances).toHaveLength(1));
|
||||
expect(screen.getByText("How many patients?", { selector: "p" })).toBeInTheDocument();
|
||||
expect(screen.getByText("Analyzing question…")).toBeInTheDocument();
|
||||
expect(useSessionStore.getState().currentPhase).toBe("F1");
|
||||
});
|
||||
|
||||
test("a failed create restores the landing view and preserves the question for retry", async () => {
|
||||
server.use(
|
||||
http.post("http://localhost:8787/sessions", () =>
|
||||
new HttpResponse("unavailable", { status: 503 })),
|
||||
);
|
||||
renderShell();
|
||||
|
||||
const composer = screen.getByRole("textbox", { name: /new question/i });
|
||||
await userEvent.type(composer, "Keep this question");
|
||||
await userEvent.click(screen.getByRole("button", { name: /send/i }));
|
||||
|
||||
expect(await screen.findByText(/failed to create session/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/type your question/i)).toBeInTheDocument();
|
||||
expect(composer).toHaveValue("Keep this question");
|
||||
expect(useSessionStore.getState().lastUserEntry).toBeNull();
|
||||
expect(FakeEventSource.instances).toHaveLength(0);
|
||||
});
|
||||
Reference in New Issue
Block a user