Files
multica/packages/views/autopilots/components/trigger-config.test.ts
Naiyuan Qing cd6bb48283 feat(autopilots): unified create/edit dialog with issue-modal layout (#1501)
Replace separate CreateAutopilotDialog / EditAutopilotDialog with a single
shared <AutopilotDialog mode="create"|"edit"> that mirrors the issue create
modal — dynamic sizing, expand/collapse, richtext Prompt, pill toolbar.

- Tiptap ContentEditor replaces plain textarea for Prompt; detail page
  renders description via ReadonlyContent for visual parity.
- Pills: Agent, Priority, Execution Mode, Schedule (Popover hosting
  TriggerConfigSection). 0/1/N trigger strategy: add on 0, edit inline on
  1, disabled with tooltip on 2+ (power users edit in detail page).
- Exposes priority + execution_mode at creation time (backend always
  supported them; old UI only offered them in Edit).
- parseCronExpression reverse-parses stored cron back to TriggerConfig for
  Edit-time prefill (with round-trip tests).
- PillButton extracted to packages/views/common for reuse across modals.
- DialogContent uses showCloseButton={false} so the shared header renders
  the Maximize + Close buttons next to the Rocket-prefixed breadcrumb.
- Conditional mount at call sites (`{open && <AutopilotDialog/>}`) keeps
  state fresh on each open.
- Schedule dirty detection compares cron+timezone payloads vs mount
  snapshot, and edit-mode submits against a snapshotted trigger id so
  concurrent WS refetches can't mis-target the update.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:59:37 +08:00

69 lines
2.4 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
parseCronExpression,
toCronExpression,
getDefaultTriggerConfig,
} from "./trigger-config";
describe("parseCronExpression", () => {
it("round-trips hourly", () => {
const cfg = { ...getDefaultTriggerConfig(), frequency: "hourly" as const, time: "00:15" };
const cron = toCronExpression(cfg);
const parsed = parseCronExpression(cron, "UTC");
expect(parsed.frequency).toBe("hourly");
});
it("round-trips daily at 09:30", () => {
const cfg = { ...getDefaultTriggerConfig(), frequency: "daily" as const, time: "09:30" };
const cron = toCronExpression(cfg);
const parsed = parseCronExpression(cron, "UTC");
expect(parsed.frequency).toBe("daily");
expect(parsed.time).toBe("09:30");
});
it("recognises weekdays pattern", () => {
const parsed = parseCronExpression("0 9 * * 1-5", "UTC");
expect(parsed.frequency).toBe("weekdays");
expect(parsed.time).toBe("09:00");
});
it("recognises weekly with multiple days", () => {
const parsed = parseCronExpression("0 9 * * 1,3,5", "UTC");
expect(parsed.frequency).toBe("weekly");
expect(parsed.daysOfWeek).toEqual([1, 3, 5]);
expect(parsed.time).toBe("09:00");
});
it("falls back to custom for non-matching pattern", () => {
const parsed = parseCronExpression("*/15 * * * *", "UTC");
expect(parsed.frequency).toBe("custom");
expect(parsed.cronExpression).toBe("*/15 * * * *");
});
it("falls back to custom for malformed input", () => {
const parsed = parseCronExpression("not a cron", "UTC");
expect(parsed.frequency).toBe("custom");
});
it("preserves provided timezone", () => {
const parsed = parseCronExpression("0 9 * * *", "Asia/Shanghai");
expect(parsed.timezone).toBe("Asia/Shanghai");
});
it("rejects out-of-range minute", () => {
expect(parseCronExpression("60 * * * *", "UTC").frequency).toBe("custom");
});
it("rejects out-of-range hour", () => {
expect(parseCronExpression("0 24 * * *", "UTC").frequency).toBe("custom");
});
it("round-trips weekly preserving daysOfWeek", () => {
const cfg = { ...getDefaultTriggerConfig(), frequency: "weekly" as const, time: "14:45", daysOfWeek: [0, 2, 6] };
const parsed = parseCronExpression(toCronExpression(cfg), "UTC");
expect(parsed.frequency).toBe("weekly");
expect(parsed.time).toBe("14:45");
expect(parsed.daysOfWeek).toEqual([0, 2, 6]);
});
});