diff --git a/packages/views/autopilots/components/autopilot-dialog.schedule.test.tsx b/packages/views/autopilots/components/autopilot-dialog.schedule.test.tsx new file mode 100644 index 0000000000..8f3acfa37b --- /dev/null +++ b/packages/views/autopilots/components/autopilot-dialog.schedule.test.tsx @@ -0,0 +1,264 @@ +import { useImperativeHandle, useRef, useState } from "react"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { AutopilotTrigger } from "@multica/core/types"; +import { renderWithI18n } from "../../test/i18n"; + +// Regression cover for MUL-5649: editing a manual-only autopilot (no triggers) +// showed the schedule panel seeded with the editor's default — 09:00 every day +// — as if that were the autopilot's schedule. Saving compared that default +// against itself, found no change, and wrote nothing, while the toast said the +// autopilot was updated and the detail page still read "No triggers +// configured". The panel now states what is true (no schedule) and asks for the +// schedule to be added explicitly; adding one writes it, default or not. + +const mockUpdateAutopilot = vi.hoisted(() => vi.fn()); +const mockCreateTrigger = vi.hoisted(() => vi.fn()); +const mockUpdateTrigger = vi.hoisted(() => vi.fn()); + +vi.mock("@multica/core/hooks", () => ({ useWorkspaceId: () => "ws-test" })); +vi.mock("@multica/core/paths", () => ({ useCurrentWorkspace: () => ({ name: "Acme" }) })); + +vi.mock("@multica/core/workspace/queries", () => ({ + agentListOptions: (wsId: string) => ({ + queryKey: ["agents", wsId], + queryFn: async () => [ + { + id: "agent-1", + name: "Scout", + description: "Researches things", + archived_at: null, + runtime_id: "runtime-1", + }, + ], + }), + squadListOptions: (wsId: string) => ({ + queryKey: ["squads", wsId], + queryFn: async () => [], + }), +})); + +vi.mock("@multica/core/projects/queries", () => ({ + projectListOptions: (wsId: string) => ({ + queryKey: ["projects", wsId], + queryFn: async () => [], + }), +})); + +vi.mock("@multica/core/autopilots/queries", () => ({ + cronPreviewOptions: (wsId: string, expr: string, tz: string) => ({ + queryKey: ["cron-preview", wsId, expr, tz], + queryFn: async () => ({ next_runs: ["2126-07-14T01:00:00Z"] }), + retry: false, + }), +})); + +vi.mock("@multica/core/autopilots/mutations", () => ({ + useCreateAutopilot: () => ({ mutateAsync: vi.fn() }), + useCreateAutopilotTrigger: () => ({ mutateAsync: mockCreateTrigger }), + useUpdateAutopilot: () => ({ mutateAsync: mockUpdateAutopilot }), + useUpdateAutopilotTrigger: () => ({ mutateAsync: mockUpdateTrigger }), +})); + +vi.mock("sonner", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); + +vi.mock("../../editor", () => ({ + TitleEditor: ({ ref, defaultValue, placeholder, onChange, onSubmit }: any) => { + const [value, setValue] = useState(defaultValue ?? ""); + const inputRef = useRef(null); + useImperativeHandle(ref, () => ({ + getText: () => value, + focus: () => inputRef.current?.focus(), + focusAtCoords: () => inputRef.current?.focus(), + })); + return ( + { + setValue(e.target.value); + onChange?.(e.target.value); + }} + onKeyDown={(e) => { + if (e.key === "Enter") onSubmit?.(); + }} + /> + ); + }, + ContentEditor: ({ placeholder }: any) =>