// @vitest-environment happy-dom import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { ApprovalView } from "./ApprovalView.js"; afterEach(cleanup); const ALL_OPTIONS = [ { optionId: "a1", name: "Allow once", kind: "allow_once" as const }, { optionId: "a2", name: "Allow always", kind: "allow_always" as const }, { optionId: "r1", name: "Reject", kind: "reject_once" as const }, ]; function buttons(): string[] { return [...document.querySelectorAll(".st-approval-acts button")].map((b) => b.textContent ?? ""); } describe("ApprovalView", () => { it("asks the operator with the three decisions the daemon exposes", () => { render( Promise.resolve()} />, ); expect(buttons()).toEqual(["Allow once", "Allow for session", "Deny"]); expect(screen.getByText("Write(a.ts)")).toBeTruthy(); expect(screen.getByText("/repo/a.ts")).toBeTruthy(); }); it("submits the decision the operator picked", async () => { const onDecide = vi.fn().mockResolvedValue(undefined); render(); fireEvent.click(screen.getByText("Deny")); await waitFor(() => expect(onDecide).toHaveBeenCalledWith("deny")); }); it("disables every button once one is pressed, so a decision is sent once", () => { render( new Promise(() => {})} />); fireEvent.click(screen.getByText("Allow once")); const all = [...document.querySelectorAll(".st-approval-acts button")]; expect(all.every((b) => b.disabled)).toBe(true); }); it("restores the controls when the decision failed and this approval is still pending", async () => { const onDecide = vi.fn().mockRejectedValue(new Error("nope")); render(); fireEvent.click(screen.getByText("Allow once")); await waitFor(() => { const all = [...document.querySelectorAll(".st-approval-acts button")]; expect(all.every((b) => !b.disabled)).toBe(true); }); }); it("offers no button for a decision the request cannot back", () => { render( Promise.resolve()} />, ); expect(buttons()).toEqual(["Allow once"]); }); it("says so when the request offers no decision at all", () => { render( Promise.resolve()} />); expect(screen.getByText("no decision offered")).toBeTruthy(); }); it("renders a resolved approval as a one-liner, with no controls", () => { render( Promise.resolve()} />, ); expect(screen.getByText("Write(a.ts) — approved")).toBeTruthy(); expect(document.querySelector(".st-approval-acts")).toBeNull(); }); });