























@@ -77,6 +77,12 @@ function createProps(overrides: Partial<CronProps> = {}): CronProps {
7777};
7878}
797980+function getButtonByText(container: Element, text: string) {
81+return Array.from(container.querySelectorAll("button")).find(
82+(btn) => btn.textContent?.trim() === text,
83+);
84+}
85+8086describe("cron view", () => {
8187it("shows all-job history mode by default", () => {
8288const container = document.createElement("div");
@@ -250,41 +256,36 @@ describe("cron view", () => {
250256expect(container.textContent).not.toContain("Next 13");
251257});
252258253-it("calls onJobsFiltersChange when schedule filter changes", () => {
259+it("wires jobs filter changes and reset", () => {
254260const container = document.createElement("div");
255261const onJobsFiltersChange = vi.fn();
262+const onJobsFiltersReset = vi.fn();
256263render(renderCron(createProps({ onJobsFiltersChange })), container);
257264258-const select = container.querySelector('select[data-test-id="cron-jobs-schedule-filter"]');
259-expect(select).not.toBeNull();
260-if (!(select instanceof HTMLSelectElement)) {
265+const scheduleSelect = container.querySelector(
266+'select[data-test-id="cron-jobs-schedule-filter"]',
267+);
268+expect(scheduleSelect).not.toBeNull();
269+if (!(scheduleSelect instanceof HTMLSelectElement)) {
261270return;
262271}
263-select.value = "cron";
264-select.dispatchEvent(new Event("change", { bubbles: true }));
272+scheduleSelect.value = "cron";
273+scheduleSelect.dispatchEvent(new Event("change", { bubbles: true }));
265274266275expect(onJobsFiltersChange).toHaveBeenCalledWith({ cronJobsScheduleKindFilter: "cron" });
267-});
268-269-it("calls onJobsFiltersChange when last-run filter changes", () => {
270-const container = document.createElement("div");
271-const onJobsFiltersChange = vi.fn();
272-render(renderCron(createProps({ onJobsFiltersChange })), container);
273276274-const select = container.querySelector('select[data-test-id="cron-jobs-last-status-filter"]');
275-expect(select).not.toBeNull();
276-if (!(select instanceof HTMLSelectElement)) {
277+const lastRunSelect = container.querySelector(
278+'select[data-test-id="cron-jobs-last-status-filter"]',
279+);
280+expect(lastRunSelect).not.toBeNull();
281+if (!(lastRunSelect instanceof HTMLSelectElement)) {
277282return;
278283}
279-select.value = "error";
280-select.dispatchEvent(new Event("change", { bubbles: true }));
284+lastRunSelect.value = "error";
285+lastRunSelect.dispatchEvent(new Event("change", { bubbles: true }));
281286282287expect(onJobsFiltersChange).toHaveBeenCalledWith({ cronJobsLastStatusFilter: "error" });
283-});
284288285-it("calls onJobsFiltersReset when reset button is clicked", () => {
286-const container = document.createElement("div");
287-const onJobsFiltersReset = vi.fn();
288289render(
289290renderCron(
290291createProps({
@@ -302,7 +303,7 @@ describe("cron view", () => {
302303expect(onJobsFiltersReset).toHaveBeenCalledTimes(1);
303304});
304305305-it("shows webhook delivery option in the form", () => {
306+it("renders supported delivery options and normalizes stale announce selection", () => {
306307const container = document.createElement("div");
307308render(
308309renderCron(
@@ -317,10 +318,7 @@ describe("cron view", () => {
317318(opt.textContent ?? "").trim(),
318319);
319320expect(options).toContain("Webhook POST");
320-});
321321322-it("normalizes stale announce selection in the form when unsupported", () => {
323-const container = document.createElement("div");
324322render(
325323renderCron(
326324createProps({
@@ -335,12 +333,12 @@ describe("cron view", () => {
335333container,
336334);
337335338-const options = Array.from(container.querySelectorAll("option")).map((opt) =>
336+const normalizedOptions = Array.from(container.querySelectorAll("option")).map((opt) =>
339337(opt.textContent ?? "").trim(),
340338);
341-expect(options).not.toContain("Announce summary (default)");
342-expect(options).toContain("Webhook POST");
343-expect(options).toContain("None (internal)");
339+expect(normalizedOptions).not.toContain("Announce summary (default)");
340+expect(normalizedOptions).toContain("Webhook POST");
341+expect(normalizedOptions).toContain("None (internal)");
344342expect(container.querySelector('input[placeholder="https://example.com/cron"]')).toBeNull();
345343});
346344@@ -386,9 +384,7 @@ describe("cron view", () => {
386384container,
387385);
388386389-const editButton = Array.from(container.querySelectorAll("button")).find(
390-(btn) => btn.textContent?.trim() === "Edit",
391-);
387+const editButton = getButtonByText(container, "Edit");
392388expect(editButton).not.toBeUndefined();
393389editButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
394390expect(onEdit).toHaveBeenCalledWith(job);
@@ -397,15 +393,13 @@ describe("cron view", () => {
397393expect(container.textContent).toContain("Edit Job");
398394expect(container.textContent).toContain("Save changes");
399395400-const cancelButton = Array.from(container.querySelectorAll("button")).find(
401-(btn) => btn.textContent?.trim() === "Cancel",
402-);
396+const cancelButton = getButtonByText(container, "Cancel");
403397expect(cancelButton).not.toBeUndefined();
404398cancelButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
405399expect(onCancelEdit).toHaveBeenCalledTimes(1);
406400});
407401408-it("renders advanced controls for cron + agent payload + delivery", () => {
402+it("renders cron form sections and toggles advanced controls by schedule", () => {
409403const container = document.createElement("div");
410404render(
411405renderCron(
@@ -428,52 +422,29 @@ describe("cron view", () => {
428422expect(container.textContent).toContain("Model");
429423expect(container.textContent).toContain("Thinking");
430424expect(container.textContent).toContain("Best effort delivery");
431-});
432-433-it("groups stagger window and unit inside the same stagger row", () => {
434-const container = document.createElement("div");
435-render(
436-renderCron(
437-createProps({
438-form: {
439- ...DEFAULT_CRON_FORM,
440-scheduleKind: "cron",
441-payloadKind: "agentTurn",
442-},
443-}),
444-),
445-container,
446-);
447425448426const staggerGroup = container.querySelector(".cron-stagger-group");
449427expect(staggerGroup).not.toBeNull();
450428expect(staggerGroup?.textContent).toContain("Stagger window");
451429expect(staggerGroup?.textContent).toContain("Stagger unit");
452-});
453-454-it("explains timeout blank behavior and shows cron jitter hint", () => {
455-const container = document.createElement("div");
456-render(
457-renderCron(
458-createProps({
459-form: {
460- ...DEFAULT_CRON_FORM,
461-scheduleKind: "cron",
462-payloadKind: "agentTurn",
463-},
464-}),
465-),
466-container,
467-);
468-469430expect(container.textContent).toContain(
470431"Optional. Leave blank to use the gateway default timeout behavior for this run.",
471432);
472433expect(container.textContent).toContain("Need jitter? Use Advanced");
473-});
474434475-it("disables Agent ID when clear-agent is enabled", () => {
476-const container = document.createElement("div");
435+expect(container.textContent).toContain("Enabled");
436+expect(container.textContent).toContain("Jobs");
437+expect(container.textContent).toContain("Next wake");
438+expect(container.textContent).toContain("Basics");
439+expect(container.textContent).toContain("Schedule");
440+expect(container.textContent).toContain("Execution");
441+expect(container.textContent).toContain("Delivery");
442+443+const checkboxLabel = container.querySelector(".cron-checkbox");
444+expect(checkboxLabel).not.toBeNull();
445+const firstElement = checkboxLabel?.firstElementChild;
446+expect(firstElement?.tagName.toLowerCase()).toBe("input");
447+477448render(
478449renderCron(
479450createProps({
@@ -490,32 +461,7 @@ describe("cron view", () => {
490461expect(agentInput).not.toBeNull();
491462expect(agentInput instanceof HTMLInputElement).toBe(true);
492463expect(agentInput instanceof HTMLInputElement ? agentInput.disabled : false).toBe(true);
493-});
494464495-it("renders sectioned cron form layout", () => {
496-const container = document.createElement("div");
497-render(renderCron(createProps()), container);
498-expect(container.textContent).toContain("Enabled");
499-expect(container.textContent).toContain("Jobs");
500-expect(container.textContent).toContain("Next wake");
501-expect(container.textContent).toContain("Basics");
502-expect(container.textContent).toContain("Schedule");
503-expect(container.textContent).toContain("Execution");
504-expect(container.textContent).toContain("Delivery");
505-expect(container.textContent).toContain("Advanced");
506-});
507-508-it("renders checkbox fields with input first for alignment", () => {
509-const container = document.createElement("div");
510-render(renderCron(createProps()), container);
511-const checkboxLabel = container.querySelector(".cron-checkbox");
512-expect(checkboxLabel).not.toBeNull();
513-const firstElement = checkboxLabel?.firstElementChild;
514-expect(firstElement?.tagName.toLowerCase()).toBe("input");
515-});
516-517-it("hides cron-only advanced controls for non-cron schedules", () => {
518-const container = document.createElement("div");
519465render(
520466renderCron(
521467createProps({
@@ -535,7 +481,7 @@ describe("cron view", () => {
535481expect(container.textContent).not.toContain("Best effort delivery");
536482});
537483538-it("renders inline validation errors and disables submit when invalid", () => {
484+it("renders inline validation errors, disabled submit, and required aria bindings", () => {
539485const container = document.createElement("div");
540486render(
541487renderCron(
@@ -569,10 +515,7 @@ describe("cron view", () => {
569515);
570516expect(saveButton).not.toBeUndefined();
571517expect(saveButton?.disabled).toBe(true);
572-});
573518574-it("shows required legend and aria bindings for invalid required fields", () => {
575-const container = document.createElement("div");
576519render(
577520renderCron(
578521createProps({
@@ -609,7 +552,7 @@ describe("cron view", () => {
609552);
610553});
611554612-it("wires the Clone action from job rows", () => {
555+it("wires job row actions and selects the row before acting", () => {
613556const container = document.createElement("div");
614557const onClone = vi.fn();
615558const onLoadRuns = vi.fn();
@@ -625,85 +568,67 @@ describe("cron view", () => {
625568container,
626569);
627570628-const cloneButton = Array.from(container.querySelectorAll("button")).find(
629-(btn) => btn.textContent?.trim() === "Clone",
630-);
571+const cloneButton = getButtonByText(container, "Clone");
631572expect(cloneButton).not.toBeUndefined();
632573cloneButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
633574expect(onClone).toHaveBeenCalledWith(job);
634575expect(onLoadRuns).toHaveBeenCalledWith("job-clone");
635-});
636576637-it("selects row when clicking Enable/Disable, Run, and Remove actions", () => {
638-const container = document.createElement("div");
639577const onToggle = vi.fn();
640578const onRun = vi.fn();
641579const onRemove = vi.fn();
642-const onLoadRuns = vi.fn();
643-const job = createJob("job-actions");
580+const actionLoadRuns = vi.fn();
581+const actionJob = createJob("job-actions");
644582render(
645583renderCron(
646584createProps({
647-jobs: [job],
585+jobs: [actionJob],
648586 onToggle,
649587 onRun,
650588 onRemove,
651- onLoadRuns,
589+onLoadRuns: actionLoadRuns,
652590}),
653591),
654592container,
655593);
656594657-const enableButton = Array.from(container.querySelectorAll("button")).find(
658-(btn) => btn.textContent?.trim() === "Disable",
659-);
595+const enableButton = getButtonByText(container, "Disable");
660596expect(enableButton).not.toBeUndefined();
661597enableButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
662598663-const runButton = Array.from(container.querySelectorAll("button")).find(
664-(btn) => btn.textContent?.trim() === "Run",
665-);
599+const runButton = getButtonByText(container, "Run");
666600expect(runButton).not.toBeUndefined();
667601runButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
668602669-const removeButton = Array.from(container.querySelectorAll("button")).find(
670-(btn) => btn.textContent?.trim() === "Remove",
671-);
603+const removeButton = getButtonByText(container, "Remove");
672604expect(removeButton).not.toBeUndefined();
673605removeButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
674606675-expect(onToggle).toHaveBeenCalledWith(job, false);
676-expect(onRun).toHaveBeenCalledWith(job, "force");
677-expect(onRemove).toHaveBeenCalledWith(job);
678-expect(onLoadRuns).toHaveBeenCalledTimes(3);
679-expect(onLoadRuns).toHaveBeenNthCalledWith(1, "job-actions");
680-expect(onLoadRuns).toHaveBeenNthCalledWith(2, "job-actions");
681-expect(onLoadRuns).toHaveBeenNthCalledWith(3, "job-actions");
682-});
607+expect(onToggle).toHaveBeenCalledWith(actionJob, false);
608+expect(onRun).toHaveBeenCalledWith(actionJob, "force");
609+expect(onRemove).toHaveBeenCalledWith(actionJob);
610+expect(actionLoadRuns).toHaveBeenCalledTimes(3);
611+expect(actionLoadRuns).toHaveBeenNthCalledWith(1, "job-actions");
612+expect(actionLoadRuns).toHaveBeenNthCalledWith(2, "job-actions");
613+expect(actionLoadRuns).toHaveBeenNthCalledWith(3, "job-actions");
683614684-it("wires Run if due action with due mode", () => {
685-const container = document.createElement("div");
686-const onRun = vi.fn();
687-const onLoadRuns = vi.fn();
688-const job = createJob("job-due");
615+const onRunDue = vi.fn();
616+const dueJob = createJob("job-due");
689617render(
690618renderCron(
691619createProps({
692-jobs: [job],
693- onRun,
694- onLoadRuns,
620+jobs: [dueJob],
621+onRun: onRunDue,
695622}),
696623),
697624container,
698625);
699626700-const runDueButton = Array.from(container.querySelectorAll("button")).find(
701-(btn) => btn.textContent?.trim() === "Run if due",
702-);
627+const runDueButton = getButtonByText(container, "Run if due");
703628expect(runDueButton).not.toBeUndefined();
704629runDueButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
705630706-expect(onRun).toHaveBeenCalledWith(job, "due");
631+expect(onRunDue).toHaveBeenCalledWith(dueJob, "due");
707632});
708633709634it("renders suggestion datalists for agent/model/thinking/timezone", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。