feat(frontend): simplify Pi operator workflow

This commit is contained in:
2026-08-14 18:52:11 +02:00
parent dc83a55555
commit 1df881d98d
2 changed files with 192 additions and 39 deletions
+47 -16
View File
@@ -60,7 +60,7 @@ test("uses closed provider, model, and reasoning choices without a secret field
expect(screen.getByLabelText("Model")).toHaveTextContent("GLM 5.2");
expect(screen.queryByRole("textbox", { name: /provider|model|reasoning|credential/i })).not.toBeInTheDocument();
expect(document.querySelector('input[type="password"]')).toBeNull();
expect(screen.queryByText(/terminal|shell access/i)).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /terminal|shell access/i })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Update Pi" })).not.toBeInTheDocument();
});
@@ -167,26 +167,57 @@ test("explains forbidden management access without offering mutation controls",
expect(screen.queryByRole("button", { name: "Test saved defaults" })).not.toBeInTheDocument();
});
test("offers a copyable host-side Pi update instruction without an update action", async () => {
test("shows a seven-step host-terminal workflow in scrollable platform tabs", async () => {
const user = userEvent.setup();
const writeText = vi.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText } });
renderManagement();
expect(await screen.findByText("thothctl pi update")).toBeVisible();
await user.click(screen.getByRole("button", { name: "Copy update command" }));
expect(writeText).toHaveBeenCalledWith("thothctl pi update");
expect(screen.getByRole("status", { name: "Pi management feedback" })).toHaveTextContent("Update command copied");
expect(screen.queryByRole("button", { name: /update Pi/i })).not.toBeInTheDocument();
});
const tablist = await screen.findByRole("tablist", { name: "Pi host platform" });
expect(within(tablist).getAllByRole("tab").map((tab) => tab.textContent)).toEqual(["Linux", "macOS", "Windows"]);
expect(screen.getByRole("dialog", { name: "Pi management" })).toHaveClass("max-h-[calc(100vh-6rem)]");
expect(screen.getByTestId("pi-platform-instructions-scroll")).toHaveClass("overflow-y-scroll");
expect(screen.queryByRole("tabpanel")).not.toBeInTheDocument();
expect(within(tablist).getAllByRole("tab").every((tab) => tab.getAttribute("aria-selected") === "false")).toBe(true);
expect(screen.getByText("Using the host terminal:")).toBeVisible();
expect(screen.queryByText(/not this browser page/i)).not.toBeInTheDocument();
expect(screen.queryByRole("region", { name: "Host update" })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Copy update command" })).not.toBeInTheDocument();
expect(screen.queryByText(/:5173/)).not.toBeInTheDocument();
expect(await screen.findByText("Select the provider, model, and reasoning used for new Pi work. Credentials stay in protected host files.")).toBeVisible();
expect(screen.getByText("Shows at most 200 recent lines with declared secret values removed.")).toBeVisible();
test("reports when the browser cannot copy the host-side update command", async () => {
const user = userEvent.setup();
Object.defineProperty(navigator, "clipboard", { configurable: true, value: undefined });
renderManagement();
await user.click(within(tablist).getByRole("tab", { name: "Linux" }));
const linux = screen.getByRole("tabpanel", { name: "Linux" });
expect(within(linux).getAllByRole("listitem")).toHaveLength(7);
expect(linux).toHaveTextContent("The deploy directory is in the ThothII project root, beside compose.yaml");
expect(linux).toHaveTextContent("deploy/pi/models.json");
expect(linux).toHaveTextContent("deploy/pi/settings.json");
expect(linux).toHaveTextContent("baseUrl is the provider API endpoint");
expect(linux).toHaveTextContent("enabledModels uses provider/model identifiers");
expect(linux).toHaveTextContent("PI_AUTH_FILE is a setting in the installation environment file");
expect(linux).toHaveTextContent("~/bin/thothctl --installation ~/thothii-installation.yaml pi restart --yes --drain");
expect(linux).toHaveTextContent("pi update --version <VERSION> --source build --yes --drain");
expect(linux).toHaveTextContent("pi rollback --yes");
expect(linux).not.toHaveTextContent("~/.pi/agent/");
await user.click(await screen.findByRole("button", { name: "Copy update command" }));
expect(screen.getByRole("status", { name: "Pi management feedback" })).toHaveTextContent("Could not copy the update command");
await user.click(within(tablist).getByRole("tab", { name: "macOS" }));
const macos = screen.getByRole("tabpanel", { name: "macOS" });
expect(within(macos).getAllByRole("listitem")).toHaveLength(7);
expect(macos).toHaveTextContent("The deploy directory is in the ThothII project root, beside compose.yaml");
expect(macos).toHaveTextContent("deploy/pi/models.json");
expect(macos).toHaveTextContent("deploy/pi/settings.json");
expect(macos).toHaveTextContent("~/bin/thothctl --installation ~/thothii-installation.yaml pi restart --yes --drain");
expect(macos).toHaveTextContent("~/bin/thothctl --installation ~/thothii-installation.yaml pi update --version <VERSION> --source build --yes --drain");
expect(macos).toHaveTextContent("pi maintenance recover --yes");
await user.click(within(tablist).getByRole("tab", { name: "Windows" }));
const windows = screen.getByRole("tabpanel", { name: "Windows" });
expect(within(windows).getAllByRole("listitem")).toHaveLength(7);
expect(windows).toHaveTextContent("The deploy directory is in the ThothII project root, beside compose.yaml");
expect(windows).toHaveTextContent("deploy\\pi\\models.json");
expect(windows).toHaveTextContent("deploy\\pi\\settings.json");
expect(windows).toHaveTextContent('& (Resolve-Path "~\\bin\\thothctl-windows-amd64.exe") --installation (Resolve-Path "~\\thothii-installation.yaml") pi restart --yes --drain');
expect(windows).toHaveTextContent('& (Resolve-Path "~\\bin\\thothctl-windows-amd64.exe") --installation (Resolve-Path "~\\thothii-installation.yaml") pi update --version <VERSION> --source build --yes --drain');
expect(windows).toHaveTextContent("pi rollback --yes");
});
test("reloads installation defaults when the panel is reopened", async () => {