story-kit: merge 340_story_web_ui_rebuild_and_restart_button

This commit is contained in:
Dave
2026-03-20 09:08:13 +00:00
parent 0897b36cc1
commit 594114d671
4 changed files with 589 additions and 209 deletions
+101 -1
View File
@@ -1,7 +1,13 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { ChatHeader } from "./ChatHeader";
vi.mock("../api/client", () => ({
api: {
rebuildAndRestart: vi.fn(),
},
}));
interface ChatHeaderProps {
projectPath: string;
onCloseProject: () => void;
@@ -14,6 +20,7 @@ interface ChatHeaderProps {
onModelChange: (model: string) => void;
enableTools: boolean;
onToggleTools: (enabled: boolean) => void;
wsConnected: boolean;
}
function makeProps(overrides: Partial<ChatHeaderProps> = {}): ChatHeaderProps {
@@ -29,6 +36,7 @@ function makeProps(overrides: Partial<ChatHeaderProps> = {}): ChatHeaderProps {
onModelChange: vi.fn(),
enableTools: true,
onToggleTools: vi.fn(),
wsConnected: false,
...overrides,
};
}
@@ -211,4 +219,96 @@ describe("ChatHeader", () => {
expect(sessionBtn.style.backgroundColor).toBe("rgb(47, 47, 47)");
expect(sessionBtn.style.color).toBe("rgb(136, 136, 136)");
});
// ── Rebuild button ────────────────────────────────────────────────────────
it("renders rebuild button", () => {
render(<ChatHeader {...makeProps()} />);
expect(
screen.getByTitle("Rebuild and restart the server"),
).toBeInTheDocument();
});
it("shows confirmation dialog when rebuild button is clicked", () => {
render(<ChatHeader {...makeProps()} />);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
expect(screen.getByText("Rebuild and restart?")).toBeInTheDocument();
});
it("hides confirmation dialog when cancel is clicked", () => {
render(<ChatHeader {...makeProps()} />);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
fireEvent.click(screen.getByText("Cancel"));
expect(screen.queryByText("Rebuild and restart?")).not.toBeInTheDocument();
});
it("calls api.rebuildAndRestart and shows Building... when confirmed", async () => {
const { api } = await import("../api/client");
vi.mocked(api.rebuildAndRestart).mockReturnValue(new Promise(() => {}));
render(<ChatHeader {...makeProps()} />);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
fireEvent.click(screen.getByText("Rebuild"));
await waitFor(() => {
expect(screen.getByText("Building...")).toBeInTheDocument();
});
expect(api.rebuildAndRestart).toHaveBeenCalled();
});
it("shows Reconnecting... when rebuild triggers a network error", async () => {
const { api } = await import("../api/client");
vi.mocked(api.rebuildAndRestart).mockRejectedValue(
new TypeError("Failed to fetch"),
);
render(<ChatHeader {...makeProps()} />);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
fireEvent.click(screen.getByText("Rebuild"));
await waitFor(() => {
expect(screen.getByText("Reconnecting...")).toBeInTheDocument();
});
});
it("shows error when rebuild returns a failure message", async () => {
const { api } = await import("../api/client");
vi.mocked(api.rebuildAndRestart).mockResolvedValue(
"error[E0308]: mismatched types",
);
render(<ChatHeader {...makeProps()} />);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
fireEvent.click(screen.getByText("Rebuild"));
await waitFor(() => {
expect(screen.getByText("⚠ Rebuild failed")).toBeInTheDocument();
expect(
screen.getByText("error[E0308]: mismatched types"),
).toBeInTheDocument();
});
});
it("clears reconnecting state when wsConnected transitions to true", async () => {
const { api } = await import("../api/client");
vi.mocked(api.rebuildAndRestart).mockRejectedValue(
new TypeError("Failed to fetch"),
);
const { rerender } = render(
<ChatHeader {...makeProps({ wsConnected: false })} />,
);
fireEvent.click(screen.getByTitle("Rebuild and restart the server"));
fireEvent.click(screen.getByText("Rebuild"));
await waitFor(() => {
expect(screen.getByText("Reconnecting...")).toBeInTheDocument();
});
rerender(<ChatHeader {...makeProps({ wsConnected: true })} />);
await waitFor(() => {
expect(screen.getByText("↺ Rebuild")).toBeInTheDocument();
});
});
});