test(dashboard): align app tests with FN-7352/7261/7234 + add fetchWorkflowOptionalSteps mock
This commit is contained in:
@@ -590,7 +590,11 @@ describe("AppModals", () => {
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-detail-open-detail"));
|
||||
expect(pushStateSpy).toHaveBeenCalledTimes(1);
|
||||
expect(mockModalManager.openDetailTask).toHaveBeenCalledWith({ id: "FN-2", title: "Nested" }, undefined);
|
||||
/*
|
||||
FNXC:TaskDetailNav 2026-07-07-09:15:
|
||||
FN-7352 (route completed-task refine menus to detail) added a third `opts?: { origin?: DetailTaskOrigin }` argument to openDetailTask / openDetailTaskWithNav, so the modalManager call now carries three args (task, tab, opts). A task-to-task open with no explicit tab/origin passes (task, undefined, undefined).
|
||||
*/
|
||||
expect(mockModalManager.openDetailTask).toHaveBeenCalledWith({ id: "FN-2", title: "Nested" }, undefined, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { loadAllAppCss } from "../../test/cssFixture";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { ChangesDiffModal, type NormalizedFile } from "../ChangesDiffModal";
|
||||
import { ModalDismissPreferenceProvider } from "../../hooks/useOverlayDismiss";
|
||||
import type { MergeDetails } from "@fusion/core";
|
||||
|
||||
vi.mock("lucide-react", () => ({
|
||||
@@ -381,8 +382,14 @@ describe("ChangesDiffModal", () => {
|
||||
|
||||
it("calls onClose when clicking the modal overlay", () => {
|
||||
const onClose = vi.fn();
|
||||
/*
|
||||
FNXC:ChangesDiffModal 2026-07-07-09:20:
|
||||
FN-7261 (global modal dismissal setting) made backdrop dismissal default-off: useOverlayDismiss only closes when ModalDismissPreferenceProvider enables it. Wrap the render in the provider so the overlay-click dismiss path is exercised (matches AgentErrorDetailsModal.test.tsx).
|
||||
*/
|
||||
const { container } = render(
|
||||
<ChangesDiffModal {...defaultProps} onClose={onClose} />,
|
||||
<ModalDismissPreferenceProvider enabled>
|
||||
<ChangesDiffModal {...defaultProps} onClose={onClose} />
|
||||
</ModalDismissPreferenceProvider>,
|
||||
);
|
||||
|
||||
const overlay = container.querySelector(".modal-overlay");
|
||||
|
||||
@@ -92,7 +92,11 @@ describe("TaskDetailModal Summary tab", () => {
|
||||
expect(container.querySelector(".detail-tabs")?.firstElementChild?.textContent).toBe("Activity");
|
||||
const summaryButton = screen.getByRole("button", { name: "Summary" });
|
||||
expectButtonActive(summaryButton);
|
||||
expect(screen.queryByRole("button", { name: "Chat" })).toBeNull();
|
||||
/*
|
||||
FNXC:TaskDetailTabs 2026-07-07-09:25:
|
||||
The planner-chat ("Chat") tab now renders unconditionally in the task-detail tab strip (both taskDetailChatFirst branches), so done tasks expose Activity, Chat, Summary, ... (see TaskDetailModal.definition-actions.test.tsx). Done tasks still land on Summary by default; Chat is present but not active.
|
||||
*/
|
||||
expect(screen.getByRole("button", { name: "Chat" })).toBeInTheDocument();
|
||||
expect(screen.getByText("Completion summary")).toBeTruthy();
|
||||
expect(screen.getByText("summary")).toBeTruthy();
|
||||
expect(screen.getByText("What changed")).toBeTruthy();
|
||||
|
||||
@@ -10,6 +10,11 @@ import type { Task } from "@fusion/core";
|
||||
const apiMocks = vi.hoisted(() => ({
|
||||
fetchBoardWorkflows: vi.fn(),
|
||||
fetchWorkflowSteps: vi.fn(),
|
||||
/*
|
||||
FNXC:BoardNoLegacyFlash 2026-07-07-09:05:
|
||||
ListView renders QuickEntryBox, whose quick-create path calls fetchWorkflowOptionalSteps (added FN-6304). The partial api mock must expose it or vitest throws "No fetchWorkflowOptionalSteps export" during render and the skeleton/legacy assertions never settle.
|
||||
*/
|
||||
fetchWorkflowOptionalSteps: vi.fn(),
|
||||
fetchNodes: vi.fn(),
|
||||
fetchTaskDetail: vi.fn(),
|
||||
batchUpdateTaskModels: vi.fn(),
|
||||
@@ -23,7 +28,7 @@ const apiMocks = vi.hoisted(() => ({
|
||||
vi.mock("../../api", () => ({
|
||||
fetchBoardWorkflows: apiMocks.fetchBoardWorkflows,
|
||||
fetchWorkflowSteps: apiMocks.fetchWorkflowSteps,
|
||||
fetchNodes: apiMocks.fetchNodes,
|
||||
fetchWorkflowOptionalSteps: apiMocks.fetchWorkflowOptionalSteps,
|
||||
fetchTaskDetail: apiMocks.fetchTaskDetail,
|
||||
batchUpdateTaskModels: apiMocks.batchUpdateTaskModels,
|
||||
promoteTask: apiMocks.promoteTask,
|
||||
@@ -187,6 +192,7 @@ describe("no legacy-board flash before workflow lanes load (FN-6776)", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
apiMocks.fetchWorkflowSteps.mockResolvedValue([]);
|
||||
apiMocks.fetchWorkflowOptionalSteps.mockResolvedValue([]);
|
||||
apiMocks.fetchNodes.mockResolvedValue([]);
|
||||
apiMocks.fetchTaskDetail.mockResolvedValue(null);
|
||||
apiMocks.promoteTask.mockResolvedValue({});
|
||||
@@ -276,14 +282,19 @@ describe("no legacy-board flash before workflow lanes load (FN-6776)", () => {
|
||||
expectSkeleton(surface);
|
||||
});
|
||||
|
||||
it.each<Surface>(["Board", "ListView"])("%s fetch error exits the skeleton to a terminal legacy layout", async (surface) => {
|
||||
it.each<Surface>(["Board", "ListView"])("%s keeps the skeleton on a failed first fetch instead of flashing legacy (non-authoritative failure)", async (surface) => {
|
||||
mockViewport(1024);
|
||||
apiMocks.fetchBoardWorkflows.mockRejectedValue(new Error("network"));
|
||||
|
||||
renderSurface(surface);
|
||||
expectSkeleton(surface);
|
||||
|
||||
await waitFor(() => expectLegacyLayout(surface));
|
||||
/*
|
||||
FNXC:BoardNoLegacyFlash 2026-07-07-09:30:
|
||||
FN-7234 (preserve board workflow selections) made fetch failures non-authoritative: useBoardWorkflows keeps the current/cache-hydrated payload on a rejected fetch (empty .catch) rather than falling back to legacy. With no cached payload, boardWorkflows stays null so the skeleton persists — the board never flashes legacy on a failed first fetch. Recovery happens on the next visibility/focus/switcher-open re-fetch, not by dropping to legacy.
|
||||
*/
|
||||
await waitFor(() => expect(apiMocks.fetchBoardWorkflows).toHaveBeenCalled());
|
||||
expectSkeleton(surface);
|
||||
});
|
||||
|
||||
it.each<Surface>(["Board", "ListView"])("%s does not leak cached workflow layouts across project switches", async (surface) => {
|
||||
|
||||
Reference in New Issue
Block a user