[Test] Add unit tests for 5 untested policy components

Adds Vitest + RTL test files for policy_table, policy_templates,
guardrail_selection_modal, impact_popover, and add_attachment_form.
53 tests total covering rendering, user interactions, API calls,
and conditional UI behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yuneng-jiang 2026-03-06 17:22:39 -08:00
parent 3a2cba43dc
commit a0fb994ef0
5 changed files with 715 additions and 0 deletions

View File

@ -0,0 +1,110 @@
import React from "react";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../tests/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as networking from "../networking";
import AddAttachmentForm from "./add_attachment_form";
import { Policy } from "./types";
vi.mock("../networking");
vi.mock("./impact_preview_alert", () => ({
default: ({ impactResult }: { impactResult: any }) =>
React.createElement("div", { "data-testid": "impact-preview" }, `${impactResult.affected_keys_count} keys`),
}));
const makePolicy = (overrides: Partial<Policy> = {}): Policy => ({
policy_id: "policy-id-1",
policy_name: "test-policy",
inherit: null,
description: null,
guardrails_add: [],
guardrails_remove: [],
condition: null,
...overrides,
});
const defaultProps = {
visible: true,
onClose: vi.fn(),
onSuccess: vi.fn(),
accessToken: "test-token",
policies: [makePolicy({ policy_name: "policy-alpha" }), makePolicy({ policy_name: "policy-beta", policy_id: "id-2" })],
createAttachment: vi.fn(),
};
describe("AddAttachmentForm", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(networking.teamListCall).mockResolvedValue([]);
vi.mocked(networking.keyListCall).mockResolvedValue({ keys: [] });
vi.mocked(networking.modelAvailableCall).mockResolvedValue({ data: [] });
});
it("should render the modal title when visible", async () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
expect(await screen.findByText("Create Policy Attachment")).toBeInTheDocument();
});
it("should not render modal content when visible is false", () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} visible={false} />);
expect(screen.queryByText("Create Policy Attachment")).not.toBeInTheDocument();
});
it("should fetch teams, keys, and models on mount when visible and accessToken are provided", async () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
await waitFor(() => {
expect(networking.teamListCall).toHaveBeenCalled();
expect(networking.keyListCall).toHaveBeenCalled();
expect(networking.modelAvailableCall).toHaveBeenCalled();
});
});
it("should not fetch teams, keys, or models when accessToken is null", () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} accessToken={null} />);
expect(networking.teamListCall).not.toHaveBeenCalled();
expect(networking.keyListCall).not.toHaveBeenCalled();
expect(networking.modelAvailableCall).not.toHaveBeenCalled();
});
it("should call onClose when the Cancel button is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
await user.click(await screen.findByRole("button", { name: /cancel/i }));
expect(defaultProps.onClose).toHaveBeenCalled();
});
it("should not show scope-specific fields when scope is global (default)", async () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
await screen.findByText("Create Policy Attachment");
expect(screen.queryByText("Teams")).not.toBeInTheDocument();
expect(screen.queryByText("Keys")).not.toBeInTheDocument();
expect(screen.queryByText("Models")).not.toBeInTheDocument();
});
it("should show Teams, Keys, Models, and Tags fields when scope is switched to specific", async () => {
const user = userEvent.setup();
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
await screen.findByText("Create Policy Attachment");
await user.click(screen.getByRole("radio", { name: /specific/i }));
expect(screen.getByText("Teams")).toBeInTheDocument();
expect(screen.getByText("Keys")).toBeInTheDocument();
expect(screen.getByText("Models")).toBeInTheDocument();
expect(screen.getByText("Tags")).toBeInTheDocument();
});
it("should show the 'Estimate Impact' button only when scope is specific", async () => {
const user = userEvent.setup();
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
await screen.findByText("Create Policy Attachment");
expect(screen.queryByRole("button", { name: /estimate impact/i })).not.toBeInTheDocument();
await user.click(screen.getByRole("radio", { name: /specific/i }));
expect(screen.getByRole("button", { name: /estimate impact/i })).toBeInTheDocument();
});
it("should render a 'Create Attachment' submit button", async () => {
renderWithProviders(<AddAttachmentForm {...defaultProps} />);
expect(await screen.findByRole("button", { name: /create attachment/i })).toBeInTheDocument();
});
});

View File

@ -0,0 +1,127 @@
import React from "react";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../tests/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import GuardrailSelectionModal from "./guardrail_selection_modal";
const makeGuardrailDef = (name: string, description = "A guardrail description") => ({
guardrail_name: name,
guardrail_info: { description },
litellm_params: { guardrail: "presidio", mode: "pre_call" },
});
const makeTemplate = (guardrailDefs: any[] = [], overrides: any = {}) => ({
title: "Test Template",
guardrailDefinitions: guardrailDefs,
...overrides,
});
const defaultProps = {
visible: true,
template: makeTemplate([makeGuardrailDef("guardrail-new-1"), makeGuardrailDef("guardrail-new-2")]),
existingGuardrails: new Set<string>(),
onConfirm: vi.fn(),
onCancel: vi.fn(),
};
describe("GuardrailSelectionModal", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render guardrail names from the template", async () => {
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
expect(await screen.findByText("guardrail-new-1")).toBeInTheDocument();
expect(screen.getByText("guardrail-new-2")).toBeInTheDocument();
});
it("should pre-select only new guardrails when the modal opens", async () => {
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
await screen.findByText("guardrail-new-1");
const checkboxes = screen.getAllByRole("checkbox");
checkboxes.forEach((cb) => expect(cb).toBeChecked());
});
it("should not show a checkbox for guardrails that already exist", async () => {
const props = {
...defaultProps,
template: makeTemplate([makeGuardrailDef("existing-g"), makeGuardrailDef("new-g")]),
existingGuardrails: new Set(["existing-g"]),
};
renderWithProviders(<GuardrailSelectionModal {...props} />);
await screen.findByText("existing-g");
expect(screen.getAllByRole("checkbox")).toHaveLength(1);
});
it("should show an 'Already exists' tag for guardrails that exist in the system", async () => {
const props = {
...defaultProps,
template: makeTemplate([makeGuardrailDef("existing-g")]),
existingGuardrails: new Set(["existing-g"]),
};
renderWithProviders(<GuardrailSelectionModal {...props} />);
expect(await screen.findByText("Already exists")).toBeInTheDocument();
});
it("should show 'Create N Guardrails & Use Template' on the confirm button when N guardrails are selected", async () => {
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
expect(await screen.findByRole("button", { name: /create 2 guardrails & use template/i })).toBeInTheDocument();
});
it("should show 'Use Template' on the confirm button when no new guardrails are selected", async () => {
const props = {
...defaultProps,
template: makeTemplate([makeGuardrailDef("existing-g")]),
existingGuardrails: new Set(["existing-g"]),
};
renderWithProviders(<GuardrailSelectionModal {...props} />);
expect(await screen.findByRole("button", { name: /^use template$/i })).toBeInTheDocument();
});
it("should call onConfirm with the definitions of selected guardrails when confirmed", async () => {
const user = userEvent.setup();
const def = makeGuardrailDef("my-guardrail");
const props = { ...defaultProps, template: makeTemplate([def]) };
renderWithProviders(<GuardrailSelectionModal {...props} />);
await user.click(await screen.findByRole("button", { name: /create 1 guardrail/i }));
expect(defaultProps.onConfirm).toHaveBeenCalledWith([def]);
});
it("should deselect all guardrails when 'Deselect All' is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
await screen.findByText("guardrail-new-1");
await user.click(screen.getByRole("button", { name: /deselect all/i }));
screen.getAllByRole("checkbox").forEach((cb) => expect(cb).not.toBeChecked());
});
it("should re-select all new guardrails when 'Select All New' is clicked after deselecting", async () => {
const user = userEvent.setup();
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
await screen.findByText("guardrail-new-1");
await user.click(screen.getByRole("button", { name: /deselect all/i }));
await user.click(screen.getByRole("button", { name: /select all new/i }));
screen.getAllByRole("checkbox").forEach((cb) => expect(cb).toBeChecked());
});
it("should show 'No guardrails defined' when the template has no guardrail definitions", async () => {
const props = { ...defaultProps, template: makeTemplate([]) };
renderWithProviders(<GuardrailSelectionModal {...props} />);
expect(await screen.findByText(/no guardrails defined for this template/i)).toBeInTheDocument();
});
it("should show a progress badge when progressInfo is provided", async () => {
const props = { ...defaultProps, progressInfo: { current: 2, total: 5 } };
renderWithProviders(<GuardrailSelectionModal {...props} />);
expect(await screen.findByText(/template 2 of 5/i)).toBeInTheDocument();
});
it("should call onCancel when the Cancel button is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<GuardrailSelectionModal {...defaultProps} />);
await screen.findByText("guardrail-new-1");
await user.click(screen.getByRole("button", { name: /^cancel$/i }));
expect(defaultProps.onCancel).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,184 @@
import React from "react";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../tests/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as networking from "../networking";
import ImpactPopover from "./impact_popover";
import { PolicyAttachment } from "./types";
vi.mock("../networking");
vi.mock("@heroicons/react/outline", () => ({
EyeIcon: function EyeIcon() { return null; },
}));
vi.mock("@tremor/react", async (importOriginal) => {
const actual = await importOriginal<typeof import("@tremor/react")>();
return {
...actual,
Icon: ({ icon: IconComp, onClick, className }: any) =>
React.createElement("button", { type: "button", onClick, className }, IconComp?.displayName ?? IconComp?.name ?? "icon"),
};
});
// Expose the Popover's onOpenChange so tests can trigger it programmatically.
vi.mock("antd", async (importOriginal) => {
const actual = await importOriginal<any>();
return {
...actual,
Popover: ({ children, onOpenChange, content }: any) =>
React.createElement(
"div",
null,
React.createElement("div", { "data-testid": "popover-content" }, content),
React.createElement(
"div",
{
role: "button",
"aria-label": "open-popover",
onClick: () => onOpenChange?.(true),
},
children
)
),
Tooltip: ({ children }: any) => React.createElement(React.Fragment, null, children),
Spin: () => React.createElement("span", null, "Loading..."),
Tag: ({ children }: any) => React.createElement("span", null, children),
};
});
const makeAttachment = (overrides: Partial<PolicyAttachment> = {}): PolicyAttachment => ({
attachment_id: "att-001",
policy_name: "my-policy",
scope: null,
teams: [],
keys: [],
models: [],
tags: [],
...overrides,
});
describe("ImpactPopover", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render", () => {
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
expect(screen.getByRole("button", { name: /open-popover/i })).toBeInTheDocument();
});
it("should show 'Click to load' as the initial popover content", () => {
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
expect(screen.getByText(/click to load/i)).toBeInTheDocument();
});
it("should call estimateAttachmentImpactCall when the popover is opened", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: 0,
affected_teams_count: 0,
sample_keys: [],
sample_teams: [],
});
const attachment = makeAttachment({ policy_name: "rate-limit", teams: ["team-a"] });
renderWithProviders(<ImpactPopover attachment={attachment} accessToken="my-token" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
await waitFor(() => {
expect(networking.estimateAttachmentImpactCall).toHaveBeenCalledWith("my-token", {
policy_name: "rate-limit",
scope: null,
teams: ["team-a"],
keys: [],
models: [],
tags: [],
});
});
});
it("should not call the API when accessToken is null", async () => {
const user = userEvent.setup();
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken={null} />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(networking.estimateAttachmentImpactCall).not.toHaveBeenCalled();
});
it("should show a loading indicator while the impact is being fetched", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockReturnValue(new Promise(() => {}));
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
// Multiple "Loading..." nodes exist (Spin + adjacent text) — assert at least one is present
expect(screen.queryAllByText(/loading/i).length).toBeGreaterThan(0);
});
it("should show a global scope warning when affected_keys_count is -1", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: -1,
affected_teams_count: -1,
sample_keys: [],
sample_teams: [],
});
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(await screen.findByText(/global scope.*affects all keys and teams/i)).toBeInTheDocument();
});
it("should show key and team counts when impact data is loaded for a specific scope", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: 5,
affected_teams_count: 2,
sample_keys: ["sk-abc"],
sample_teams: ["team-x"],
});
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(await screen.findByText(/5/)).toBeInTheDocument();
expect(screen.getByText(/2/)).toBeInTheDocument();
});
it("should render sample key tags when returned from the API", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: 2,
affected_teams_count: 0,
sample_keys: ["sk-key-one", "sk-key-two"],
sample_teams: [],
});
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(await screen.findByText("sk-key-one")).toBeInTheDocument();
expect(screen.getByText("sk-key-two")).toBeInTheDocument();
});
it("should show 'No keys or teams currently affected' when both counts are 0", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: 0,
affected_teams_count: 0,
sample_keys: [],
sample_teams: [],
});
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(await screen.findByText(/no keys or teams currently affected/i)).toBeInTheDocument();
});
it("should not call the API a second time when the popover is already loaded", async () => {
const user = userEvent.setup();
vi.mocked(networking.estimateAttachmentImpactCall).mockResolvedValue({
affected_keys_count: 1,
affected_teams_count: 0,
sample_keys: ["sk-abc"],
sample_teams: [],
});
renderWithProviders(<ImpactPopover attachment={makeAttachment()} accessToken="tok" />);
await user.click(screen.getByRole("button", { name: /open-popover/i }));
await screen.findByText("sk-abc");
await user.click(screen.getByRole("button", { name: /open-popover/i }));
expect(networking.estimateAttachmentImpactCall).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,150 @@
import React from "react";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../tests/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import PolicyTable from "./policy_table";
import { Policy } from "./types";
vi.mock("@heroicons/react/outline", () => ({
TrashIcon: function TrashIcon() { return null; },
PencilIcon: function PencilIcon() { return null; },
SwitchVerticalIcon: function SwitchVerticalIcon() { return null; },
ChevronUpIcon: function ChevronUpIcon() { return null; },
ChevronDownIcon: function ChevronDownIcon() { return null; },
}));
vi.mock("@tremor/react", async (importOriginal) => {
const actual = await importOriginal<typeof import("@tremor/react")>();
return {
...actual,
Button: React.forwardRef<HTMLButtonElement, any>(({ children, ...props }, ref) =>
React.createElement("button", { ...props, ref }, children)
),
Icon: ({ icon: IconComp, onClick, className }: any) =>
React.createElement("button", { type: "button", onClick, className }, IconComp?.displayName ?? IconComp?.name ?? "icon"),
Tooltip: ({ children }: { children?: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
Badge: ({ children }: { children?: React.ReactNode }) =>
React.createElement("span", null, children),
};
});
const makePolicy = (overrides: Partial<Policy> = {}): Policy => ({
policy_id: "policy-id-1",
policy_name: "test-policy",
inherit: null,
description: null,
guardrails_add: [],
guardrails_remove: [],
condition: null,
...overrides,
});
const defaultProps = {
policies: [],
isLoading: false,
onDeleteClick: vi.fn(),
onEditClick: vi.fn(),
onViewClick: vi.fn(),
isAdmin: true,
};
describe("PolicyTable", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render column headers", () => {
renderWithProviders(<PolicyTable {...defaultProps} />);
expect(screen.getByText("Name")).toBeInTheDocument();
expect(screen.getByText("Description")).toBeInTheDocument();
expect(screen.getByText("Actions")).toBeInTheDocument();
});
it("should show a loading message when isLoading is true", () => {
renderWithProviders(<PolicyTable {...defaultProps} isLoading />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
it("should show 'No policies found' when there are no policies", () => {
renderWithProviders(<PolicyTable {...defaultProps} />);
expect(screen.getByText(/no policies found/i)).toBeInTheDocument();
});
it("should render a button with the policy name for each grouped policy", () => {
const policies = [
makePolicy({ policy_name: "alpha-policy", policy_id: "id-1" }),
makePolicy({ policy_name: "beta-policy", policy_id: "id-2" }),
];
renderWithProviders(<PolicyTable {...defaultProps} policies={policies} />);
expect(screen.getByRole("button", { name: "alpha-policy" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "beta-policy" })).toBeInTheDocument();
});
it("should call onViewClick with the policy_id when the policy name button is clicked", async () => {
const user = userEvent.setup();
const policy = makePolicy({ policy_name: "my-policy", policy_id: "view-id-1" });
renderWithProviders(<PolicyTable {...defaultProps} policies={[policy]} />);
await user.click(screen.getByRole("button", { name: "my-policy" }));
expect(defaultProps.onViewClick).toHaveBeenCalledWith("view-id-1");
});
it("should call onDeleteClick with policy_id and policy_name when the delete icon is clicked", async () => {
const user = userEvent.setup();
const policy = makePolicy({ policy_name: "del-policy", policy_id: "del-id-1" });
renderWithProviders(<PolicyTable {...defaultProps} policies={[policy]} />);
await user.click(screen.getByRole("button", { name: /TrashIcon/i }));
expect(defaultProps.onDeleteClick).toHaveBeenCalledWith("del-id-1", "del-policy");
});
it("should call onEditClick with the policy when the edit icon is clicked", async () => {
const user = userEvent.setup();
const policy = makePolicy({ policy_name: "edit-policy", policy_id: "edit-id-1" });
renderWithProviders(<PolicyTable {...defaultProps} policies={[policy]} />);
await user.click(screen.getByRole("button", { name: /PencilIcon/i }));
expect(defaultProps.onEditClick).toHaveBeenCalledWith(policy);
});
it("should not show admin action icons for non-admins", () => {
const policy = makePolicy();
renderWithProviders(<PolicyTable {...defaultProps} policies={[policy]} isAdmin={false} />);
expect(screen.queryByRole("button", { name: /TrashIcon/i })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /PencilIcon/i })).not.toBeInTheDocument();
});
it("should show a version badge when multiple versions of the same policy name exist", () => {
const policies = [
makePolicy({ policy_name: "versioned", policy_id: "v1", version_status: "published", version_number: 1 }),
makePolicy({ policy_name: "versioned", policy_id: "v2", version_status: "production", version_number: 2 }),
];
renderWithProviders(<PolicyTable {...defaultProps} policies={policies} />);
expect(screen.getByText(/2 version/i)).toBeInTheDocument();
});
it("should group policies with the same name into a single row", () => {
const policies = [
makePolicy({ policy_name: "shared", policy_id: "s1", version_status: "published" }),
makePolicy({ policy_name: "shared", policy_id: "s2", version_status: "production" }),
];
renderWithProviders(<PolicyTable {...defaultProps} policies={policies} />);
expect(screen.getAllByRole("button", { name: "shared" })).toHaveLength(1);
});
it("should show an overflow tag when more than 2 guardrails_add exist", () => {
const policy = makePolicy({ guardrails_add: ["g1", "g2", "g3", "g4"] });
renderWithProviders(<PolicyTable {...defaultProps} policies={[policy]} />);
expect(screen.getByText("+2")).toBeInTheDocument();
});
it("should prefer the production version as the primary policy when grouping", async () => {
const user = userEvent.setup();
const policies = [
makePolicy({ policy_name: "grouped", policy_id: "published-id", version_status: "published" }),
makePolicy({ policy_name: "grouped", policy_id: "prod-id", version_status: "production" }),
];
renderWithProviders(<PolicyTable {...defaultProps} policies={policies} />);
await user.click(screen.getByRole("button", { name: "grouped" }));
expect(defaultProps.onViewClick).toHaveBeenCalledWith("prod-id");
});
});

View File

@ -0,0 +1,144 @@
import React from "react";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../tests/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as networking from "../networking";
import PolicyTemplates from "./policy_templates";
vi.mock("../networking");
vi.mock("@heroicons/react/outline", () => ({
ShieldCheckIcon: function ShieldCheckIcon() { return null; },
ShieldExclamationIcon: function ShieldExclamationIcon() { return null; },
BeakerIcon: function BeakerIcon() { return null; },
CurrencyDollarIcon: function CurrencyDollarIcon() { return null; },
CheckCircleIcon: function CheckCircleIcon() { return null; },
}));
const makeTemplate = (overrides: any = {}) => ({
id: "tpl-1",
title: "Test Template",
description: "A test template",
icon: "ShieldCheckIcon",
iconColor: "text-green-500",
iconBg: "bg-green-50",
guardrails: ["guardrail-a"],
tags: [],
complexity: "Low" as const,
...overrides,
});
const defaultProps = {
onUseTemplate: vi.fn(),
onOpenAiSuggestion: vi.fn(),
accessToken: "test-token",
};
describe("PolicyTemplates", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render the section header after loading", async () => {
vi.mocked(networking.getPolicyTemplates).mockResolvedValue([]);
renderWithProviders(<PolicyTemplates {...defaultProps} />);
expect(await screen.findByText("Policy Templates")).toBeInTheDocument();
});
it("should not show the template grid while fetching", () => {
vi.mocked(networking.getPolicyTemplates).mockReturnValue(new Promise(() => {}));
renderWithProviders(<PolicyTemplates {...defaultProps} />);
expect(screen.queryByText("Policy Templates")).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /use ai to find templates/i })).not.toBeInTheDocument();
});
it("should render a card for each fetched template", async () => {
const templates = [
makeTemplate({ title: "Template Alpha" }),
makeTemplate({ id: "tpl-2", title: "Template Beta" }),
];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
renderWithProviders(<PolicyTemplates {...defaultProps} />);
expect(await screen.findByText("Template Alpha")).toBeInTheDocument();
expect(screen.getByText("Template Beta")).toBeInTheDocument();
});
it("should call onTemplatesLoaded with the fetched templates after loading", async () => {
const templates = [makeTemplate()];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
const onTemplatesLoaded = vi.fn();
renderWithProviders(<PolicyTemplates {...defaultProps} onTemplatesLoaded={onTemplatesLoaded} />);
await waitFor(() => {
expect(onTemplatesLoaded).toHaveBeenCalledWith(templates);
});
});
it("should call onOpenAiSuggestion when the AI suggestion button is clicked", async () => {
vi.mocked(networking.getPolicyTemplates).mockResolvedValue([]);
const user = userEvent.setup();
renderWithProviders(<PolicyTemplates {...defaultProps} />);
await screen.findByText("Policy Templates");
await user.click(screen.getByRole("button", { name: /use ai to find templates/i }));
expect(defaultProps.onOpenAiSuggestion).toHaveBeenCalled();
});
it("should render tag filter checkboxes for unique tags across all templates", async () => {
const templates = [
makeTemplate({ tags: ["compliance"] }),
makeTemplate({ id: "tpl-2", tags: ["compliance", "security"] }),
];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
renderWithProviders(<PolicyTemplates {...defaultProps} />);
expect(await screen.findByRole("checkbox", { name: /compliance/i })).toBeInTheDocument();
expect(screen.getByRole("checkbox", { name: /security/i })).toBeInTheDocument();
});
it("should filter to only matching templates when a tag is selected", async () => {
const templates = [
makeTemplate({ id: "tpl-1", title: "Compliance Template", tags: ["compliance"] }),
makeTemplate({ id: "tpl-2", title: "Security Template", tags: ["security"] }),
];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
const user = userEvent.setup();
renderWithProviders(<PolicyTemplates {...defaultProps} />);
await screen.findByText("Compliance Template");
await user.click(screen.getByRole("checkbox", { name: /compliance/i }));
expect(screen.getByText("Compliance Template")).toBeInTheDocument();
expect(screen.queryByText("Security Template")).not.toBeInTheDocument();
});
it("should show 'No templates match' when selected tags exclude all templates", async () => {
const templates = [
makeTemplate({ id: "tpl-1", title: "Alpha Template", tags: ["alpha"] }),
makeTemplate({ id: "tpl-2", title: "Beta Template", tags: ["beta"] }),
];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
const user = userEvent.setup();
renderWithProviders(<PolicyTemplates {...defaultProps} />);
await screen.findByText("Alpha Template");
await user.click(screen.getByRole("checkbox", { name: /alpha/i }));
await user.click(screen.getByRole("checkbox", { name: /beta/i }));
expect(screen.getByText(/no templates match the selected filters/i)).toBeInTheDocument();
});
it("should restore all templates when 'Clear all' is clicked", async () => {
const templates = [
makeTemplate({ id: "tpl-1", title: "Alpha Template", tags: ["alpha"] }),
makeTemplate({ id: "tpl-2", title: "Beta Template", tags: ["beta"] }),
];
vi.mocked(networking.getPolicyTemplates).mockResolvedValue(templates);
const user = userEvent.setup();
renderWithProviders(<PolicyTemplates {...defaultProps} />);
await screen.findByText("Alpha Template");
await user.click(screen.getByRole("checkbox", { name: /alpha/i }));
expect(screen.queryByText("Beta Template")).not.toBeInTheDocument();
await user.click(screen.getByRole("button", { name: /clear all/i }));
expect(screen.getByText("Beta Template")).toBeInTheDocument();
});
it("should not fetch templates when accessToken is null", () => {
renderWithProviders(<PolicyTemplates {...defaultProps} accessToken={null} />);
expect(networking.getPolicyTemplates).not.toHaveBeenCalled();
});
});