Files
fusion/packages/dashboard/app/components/__tests__/AgentPermissionPolicyEditor.test.tsx
Fusion f990b93a86 test(FN-4612): complete Step 3 — add bullet list indentation regression coverage
Fusion-Task-Id: FN-4612
Fusion-Task-Lineage: 72e6259a-3642-427e-9f3f-23eb206815a4
2026-05-15 09:04:26 -07:00

185 lines
5.8 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import "../../styles.css";
import { AgentPermissionPolicyEditor } from "../AgentPermissionPolicyEditor";
import {
AGENT_PERMISSION_POLICY_ACTION_CATEGORIES,
type AgentPermissionPolicy,
} from "@fusion/core";
describe("AgentPermissionPolicyEditor", () => {
it("preset dropdown switches all rules", () => {
const onChange = vi.fn();
render(
<AgentPermissionPolicyEditor
mode="project-default"
value={{ presetId: "unrestricted", rules: {
git_write: "allow",
file_write_delete: "allow",
command_execution: "allow",
network_api: "allow",
task_agent_mutation: "allow",
} }}
onChange={onChange}
/>,
);
fireEvent.change(screen.getByLabelText("Preset"), { target: { value: "locked-down" } });
const payload = onChange.mock.calls.at(-1)?.[0] as AgentPermissionPolicy;
expect(payload.presetId).toBe("locked-down");
expect(payload.rules.git_write).toBe("block");
});
it("changing one category flips to custom", () => {
const onChange = vi.fn();
render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={onChange}
/>,
);
const selects = screen.getAllByRole("combobox");
fireEvent.change(selects[1], { target: { value: "require-approval" } });
const payload = onChange.mock.calls.at(-1)?.[0] as AgentPermissionPolicy;
expect(payload.presetId).toBe("custom");
});
it("agent override inherit preset emits undefined", () => {
const onChange = vi.fn();
render(
<AgentPermissionPolicyEditor
mode="agent-override"
value={{ presetId: "custom", rules: {
git_write: "allow",
file_write_delete: "allow",
command_execution: "allow",
network_api: "allow",
task_agent_mutation: "allow",
} }}
onChange={onChange}
/>,
);
fireEvent.change(screen.getByLabelText("Preset"), { target: { value: "inherit" } });
expect(onChange).toHaveBeenLastCalledWith(undefined);
});
it("shows inherit annotation from project default", () => {
render(
<AgentPermissionPolicyEditor
mode="agent-override"
value={undefined}
projectDefault={{ git_write: "require-approval" }}
onChange={() => {}}
/>,
);
expect(screen.getByText("from project default: Require approval")).toBeInTheDocument();
});
it("shows network and task mutation examples", () => {
render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
expect(screen.getByText("fn_research_run (web/research)")).toBeInTheDocument();
expect(screen.getByText("fn_task_create")).toBeInTheDocument();
});
it("renders exempt tools guidance", () => {
render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
expect(screen.getByText("Tools exempt from approval policy")).toBeInTheDocument();
expect(screen.getByText(/bypass approval policy/i)).toBeInTheDocument();
expect(screen.getByText("fn_send_message")).toBeInTheDocument();
});
it("does not duplicate fn tool examples across category rows", () => {
const { container } = render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
const rowTools = Array.from(container.querySelectorAll(".agent-policy-examples code"))
.map((node) => node.textContent ?? "")
.filter((name) => name.startsWith("fn_"));
expect(new Set(rowTools).size).toBe(rowTools.length);
});
it("renders exactly the action-gate categories as configurable rows", () => {
const { container } = render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
const rowCategories = Array.from(container.querySelectorAll(".agent-policy-row"))
.map((row) => row.getAttribute("data-category"))
.filter((value): value is string => typeof value === "string");
expect(new Set(rowCategories)).toEqual(new Set(AGENT_PERMISSION_POLICY_ACTION_CATEGORIES));
expect(rowCategories).toHaveLength(AGENT_PERMISSION_POLICY_ACTION_CATEGORIES.length);
});
it("keeps exempt tools panel read-only", () => {
const { container } = render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
const details = container.querySelector(".agent-policy-exempt");
expect(details).toBeTruthy();
expect(details?.querySelector("input, select, button")).toBeNull();
});
it("applies space-xl indentation to policy bullet lists", () => {
const { container } = render(
<AgentPermissionPolicyEditor
mode="project-default"
value={undefined}
onChange={() => {}}
/>,
);
const examplesList = container.querySelector(".agent-policy-examples");
const exemptList = container.querySelector(".agent-policy-exempt-list");
expect(examplesList).toBeTruthy();
expect(exemptList).toBeTruthy();
const spaceXl = getComputedStyle(document.documentElement).getPropertyValue("--space-xl").trim();
expect(spaceXl).not.toBe("");
if (!examplesList || !exemptList) {
return;
}
const examplesPaddingLeft = getComputedStyle(examplesList).paddingLeft;
const exemptPaddingLeft = getComputedStyle(exemptList).paddingLeft;
const expectedPaddingLeft = examplesPaddingLeft === "var(--space-xl)" ? "var(--space-xl)" : spaceXl;
expect(examplesPaddingLeft).toBe(expectedPaddingLeft);
expect(exemptPaddingLeft).toBe(expectedPaddingLeft);
});
});