import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; import type { WorkflowFieldDefinition } from "../../api"; import { makeTask, noop, noopDelete, noopMerge, noopMove, noopOpenDetail, setupTaskDetailModalHooks, } from "./TaskDetailModal.test-helpers"; import { TaskDetailModal } from "../TaskDetailModal"; import * as dashboardApi from "../../api"; import { FileBrowserProvider } from "../../context/FileBrowserContext"; setupTaskDetailModalHooks(); function renderModal(task = makeTask({ column: "done" })) { return render( , ); } describe("TaskDetailModal custom fields (U13/KTD-14)", () => { beforeEach(() => vi.clearAllMocks()); it("renders no fields section when the workflow declares no fields (today's UI)", async () => { vi.spyOn(dashboardApi, "fetchBoardWorkflows").mockResolvedValue({ flagEnabled: true, defaultWorkflowId: "builtin:coding", workflows: [{ id: "builtin:coding", name: "Coding", columns: [] }], taskWorkflowIds: {}, }); renderModal(); // Allow the field-defs fetch to settle. await waitFor(() => expect(dashboardApi.fetchBoardWorkflows).toHaveBeenCalled()); expect(screen.queryByTestId("task-fields-section")).toBeNull(); }); it("renders the schema-driven fields section when the workflow declares fields", async () => { vi.spyOn(dashboardApi, "fetchBoardWorkflows").mockResolvedValue({ flagEnabled: true, defaultWorkflowId: "builtin:coding", workflows: [ { id: "builtin:coding", name: "Coding", columns: [], fields: [ { id: "owner", name: "Owner", type: "string", render: { placement: "detail" } }, ], }, ], taskWorkflowIds: { "FN-001": "builtin:coding" }, }); renderModal(makeTask({ id: "FN-001", column: "done", customFields: { owner: "alice" } })); await waitFor(() => expect(screen.getByTestId("task-fields-section")).toBeTruthy()); expect((screen.getByLabelText("Owner") as HTMLInputElement).value).toBe("alice"); }); it("uses workflowFieldDefs prop directly and skips the board-workflows fetch", async () => { const fetchSpy = vi.spyOn(dashboardApi, "fetchBoardWorkflows"); const defs: WorkflowFieldDefinition[] = [ { id: "owner", name: "Owner", type: "string", render: { placement: "detail" } }, ]; render( , ); await waitFor(() => expect(screen.getByTestId("task-fields-section")).toBeTruthy()); expect((screen.getByLabelText("Owner") as HTMLInputElement).value).toBe("bob"); // The fetch must NOT have been triggered since the prop was provided. expect(fetchSpy).not.toHaveBeenCalled(); }); it("renders no fields section when workflowFieldDefs prop is an empty array", async () => { const fetchSpy = vi.spyOn(dashboardApi, "fetchBoardWorkflows"); render( , ); // Give React a tick to settle; no section should appear. await new Promise((r) => setTimeout(r, 50)); expect(screen.queryByTestId("task-fields-section")).toBeNull(); expect(fetchSpy).not.toHaveBeenCalled(); }); });