/** * @file WebhookSettings.test.tsx * @description The webhook form must not save "only selected rules" with no * rule ticked (the server reads an empty list as ALL rules), and a failed * toggle must surface as an error toast instead of only a console line. * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; const toastError = vi.fn(); vi.mock("../../lib/toast", () => ({ toast: { error: (m: unknown) => toastError(m), success: vi.fn(), info: vi.fn() }, })); const update = vi.fn(); vi.mock("../../lib/api", () => ({ api: { webhooks: { list: vi.fn(async () => ({ targets: [ { id: "w1", name: "Team", type: "generic", url_preview: "https://x/…", enabled: true, rule_ids: [], config: {}, }, ], })), providers: vi.fn(async () => ({ providers: [ { type: "generic", label: "Generic", url_required: true, has_default_url: false, supports_secret: false, supports_headers: false, fields: [], }, ], })), update: (...a: unknown[]) => update(...a), create: vi.fn(), remove: vi.fn(), test: vi.fn(), }, alerts: { rules: { list: vi.fn(async () => ({ rules: [{ id: "r1", name: "Rule one" }] })) } }, }, })); import { WebhookSettings } from "../WebhookSettings"; beforeEach(() => { toastError.mockReset(); update.mockReset(); }); describe("WebhookSettings", () => { it("blocks 'only selected rules' with none ticked", async () => { render(); await userEvent.click(await screen.findByRole("button", { name: /add/i })); const inputs = screen.getAllByRole("textbox"); await userEvent.type(inputs[0]!, "Hook"); await userEvent.type(inputs[1]!, "https://example.com/h"); const create = screen.getByRole("button", { name: /^create/i }); expect(create).toBeEnabled(); await userEvent.click(screen.getByRole("checkbox", { name: /all/i })); expect(create).toBeDisabled(); await userEvent.click(screen.getByRole("checkbox", { name: "Rule one" })); expect(create).toBeEnabled(); }); it("toasts a failed toggle", async () => { update.mockRejectedValue(new Error("boom")); render(); await userEvent.click(await screen.findByRole("switch")); await waitFor(() => expect(toastError).toHaveBeenCalled()); }); });