import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Actions } from "../Actions"; import type { SavedTask, ServiceInstance } from "../../types"; const saveTaskMutate = vi.fn().mockResolvedValue({ id: "t1", name: "Restart svc", task_type: "shell", content: "", enabled: true, default_service_id: "", notes: "", }); const deleteTaskMutate = vi.fn(); const runTaskMutate = vi.fn().mockResolvedValue({}); let sshServices: ServiceInstance[] = []; let tasks: SavedTask[] = []; vi.mock("../../hooks/useSettings", () => ({ useTasks: () => ({ data: tasks }), useSaveTask: () => ({ mutateAsync: saveTaskMutate, isPending: false }), useDeleteTask: () => ({ mutate: deleteTaskMutate }), useRunTask: () => ({ mutateAsync: runTaskMutate, isPending: false }), useTaskRuns: () => ({ data: { items: [] } }), })); vi.mock("../../hooks/useServices", () => ({ useServiceInstances: () => ({ data: sshServices }), })); function sshService(overrides: Partial = {}): ServiceInstance { return { id: "s1", service_type: "ssh_tasks", name: "Box", config: { host: "box", username: "u" }, secrets_set: {}, enabled: true, created_at: 0, updated_at: 0, ...overrides, } as ServiceInstance; } function task(overrides: Partial = {}): SavedTask { return { id: "t1", name: "Restart svc", task_type: "shell", content: "systemctl restart foo", enabled: true, default_service_id: "", notes: "", created_at: 0, updated_at: 0, ...overrides, } as SavedTask; } beforeEach(() => { saveTaskMutate.mockClear(); deleteTaskMutate.mockClear(); runTaskMutate.mockClear(); sshServices = []; tasks = []; }); describe("Actions", () => { it("shows the empty state and creates a task via the editor dialog", async () => { render(); expect(screen.getByText("No action selected")).toBeInTheDocument(); expect( screen.getByRole("button", { name: "Add action" }), ).toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: "Add action" })); // Editor dialog opened (Name field is unique to the editor). expect(screen.getByLabelText("Name")).toBeInTheDocument(); // Controlled input parity: name + default shell type flow through. await userEvent.type(screen.getByLabelText("Name"), "Restart svc"); await userEvent.click(screen.getByRole("button", { name: "Save action" })); expect(saveTaskMutate).toHaveBeenCalledTimes(1); const saved = saveTaskMutate.mock.calls[0][0]; expect(saved.name).toBe("Restart svc"); expect(saved.task_type).toBe("shell"); expect(saved.default_service_id).toBe(""); }); it("disables the Run button until a run service is selected", async () => { sshServices = [sshService()]; tasks = [task()]; render(); // Selecting a saved task tab exposes the detail + Run control. await userEvent.click(screen.getByRole("tab", { name: "Restart svc" })); const runButton = screen.getByRole("button", { name: "Run action" }); expect(runButton).toBeDisabled(); }); it("runs a task on the selected SSH task service", async () => { sshServices = [sshService()]; tasks = [task()]; render(); await userEvent.click(screen.getByRole("tab", { name: "Restart svc" })); await userEvent.click( screen.getByRole("combobox", { name: "Run on SSH task service" }), ); await userEvent.click(screen.getByRole("option", { name: "Box" })); await userEvent.click(screen.getByRole("button", { name: "Run action" })); expect(runTaskMutate).toHaveBeenCalledTimes(1); expect(runTaskMutate).toHaveBeenCalledWith({ taskId: "t1", serviceId: "s1", }); }); });