Files
fusion/packages/dashboard/app/components/__tests__/TaskDetailModal.github-tracking-stale.test.tsx
gsxdsm 254e97da74 FN-7306: rename task-detail Chat tab to Activity
Renames the existing task-detail activity surface while preserving the stable chat tab id.

- Move the Activity tab to the first task-detail position and make it the default for every task state.
- Keep `chat` as the compatibility tab id for modal callers, plugin APIs, and deep links.
- Update task-detail tests and add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7306-task-detail-activity-tab.md     |  7 +++
 .../dashboard/app/components/TaskDetailModal.tsx   | 35 ++++++-------
 .../TaskDetailModal.attachments-and-tabs.test.tsx  | 57 +++++++++++-----------
 .../TaskDetailModal.definition-actions.test.tsx    | 24 ++++-----
 ...TaskDetailModal.github-tracking-header.test.tsx |  2 +-
 .../TaskDetailModal.github-tracking-stale.test.tsx |  2 +-
 ...lModal.inline-editing-and-integrations.test.tsx |  4 +-
 ...skDetailModal.models-progress-workflow.test.tsx |  2 +-
 .../__tests__/TaskDetailModal.rendering.test.tsx   |  4 +-
 ...etailModal.responsive-and-dependencies.test.tsx |  6 +--
 .../__tests__/TaskDetailModal.summary-tab.test.tsx | 33 ++++++++-----
 .../components/__tests__/TaskDetailModal.test.tsx  |  4 +-
 packages/dashboard/app/hooks/useModalManager.ts    |  4 ++
 packages/dashboard/app/plugins/types.ts            |  7 ++-
 14 files changed, 107 insertions(+), 84 deletions(-)

Fusion-Task-Id: FN-7306
Fusion-Task-Lineage: c905134c-de74-4c46-9c5a-d3fbc8200093
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-30 14:58:07 -07:00

209 lines
7.2 KiB
TypeScript

/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-7306 labels the stable internal `chat` tab as Activity and keeps it as the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Activity landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { makeTask, noopDelete, noopMerge, noopMove, noopOpenDetail, setupTaskDetailModalHooks } from "./TaskDetailModal.test-helpers";
import { TaskDetailModal } from "../TaskDetailModal";
setupTaskDetailModalHooks();
describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
it("ignores stale enable-tracking result after navigating to another task", async () => {
const user = userEvent.setup();
const onTaskUpdated = vi.fn();
const addToast = vi.fn();
let resolveUpdate!: (value: any) => void;
const deferred = new Promise((resolve) => {
resolveUpdate = resolve;
});
const { updateTask } = await import("../../api");
const mockUpdateTask = vi.mocked(updateTask);
mockUpdateTask.mockImplementationOnce(() => deferred as any);
const taskA = makeTask({ id: "FN-A", title: "A", column: "todo", githubTracking: { enabled: false } });
const taskB = makeTask({ id: "FN-B", title: "B", column: "todo", githubTracking: { enabled: false } });
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={addToast}
/>,
);
await user.click(screen.getByRole("button", { name: "Enable GitHub tracking" }));
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={addToast}
/>,
);
resolveUpdate({ ...taskA, githubTracking: { enabled: true } });
await waitFor(() => {
expect(onTaskUpdated).not.toHaveBeenCalledWith(expect.objectContaining({ id: "FN-A" }));
});
expect(addToast).not.toHaveBeenCalledWith(expect.stringContaining("FN-A"), "error");
expect(screen.getByLabelText("Enabling GitHub tracking")).toBeInTheDocument();
});
it("ignores stale repo-override save result after navigating to another task", async () => {
const user = userEvent.setup();
const onTaskUpdated = vi.fn();
let resolveUpdate!: (value: any) => void;
const deferred = new Promise((resolve) => {
resolveUpdate = resolve;
});
const { updateTask } = await import("../../api");
const mockUpdateTask = vi.mocked(updateTask);
mockUpdateTask.mockImplementationOnce(() => deferred as any);
const taskA = makeTask({ id: "FN-A", title: "A", column: "todo", githubTracking: { enabled: false } });
const taskB = makeTask({ id: "FN-B", title: "B", column: "todo", githubTracking: { enabled: false } });
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={() => {}}
/>,
);
await user.click(screen.getByRole("button", { name: "Expand GitHub tracking details" }));
const repoInput = screen.getByPlaceholderText("owner/repo");
await user.type(repoInput, "octo/demo");
await user.click(screen.getByRole("button", { name: "Save" }));
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={() => {}}
/>,
);
resolveUpdate({ ...taskA, githubTracking: { enabled: false, repoOverride: "octo/demo" } });
await waitFor(() => {
expect(onTaskUpdated).not.toHaveBeenCalledWith(expect.objectContaining({ id: "FN-A" }));
});
});
it("ignores stale create-tracking-issue retry result after navigating to another task", async () => {
const user = userEvent.setup();
const onTaskUpdated = vi.fn();
let resolveUpdate!: (value: any) => void;
const deferred = new Promise((resolve) => {
resolveUpdate = resolve;
});
const { updateTask } = await import("../../api");
const mockUpdateTask = vi.mocked(updateTask);
mockUpdateTask.mockImplementationOnce(() => deferred as any);
const taskA = makeTask({ id: "FN-A", title: "A", column: "todo", githubTracking: { enabled: true } });
const taskB = makeTask({ id: "FN-B", title: "B", column: "todo", githubTracking: { enabled: false } });
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={() => {}}
/>,
);
await user.click(screen.getByRole("button", { name: "Expand GitHub tracking details" }));
await user.click(screen.getByRole("button", { name: "Create tracking issue" }));
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={() => {}}
/>,
);
resolveUpdate({ ...taskA, githubTracking: { enabled: true } });
await waitFor(() => {
expect(onTaskUpdated).not.toHaveBeenCalledWith(expect.objectContaining({ id: "FN-A" }));
});
});
it("applies enable-tracking result when still viewing the same task", async () => {
const user = userEvent.setup();
const onTaskUpdated = vi.fn();
const taskA = makeTask({ id: "FN-A", title: "A", column: "todo", githubTracking: { enabled: false } });
const { updateTask } = await import("../../api");
const mockUpdateTask = vi.mocked(updateTask);
mockUpdateTask.mockResolvedValueOnce({ ...taskA, githubTracking: { enabled: true } } as any);
render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
onTaskUpdated={onTaskUpdated}
addToast={() => {}}
/>,
);
await user.click(screen.getByRole("button", { name: "Enable GitHub tracking" }));
await waitFor(() => {
expect(onTaskUpdated).toHaveBeenCalledTimes(1);
expect(onTaskUpdated).toHaveBeenCalledWith(expect.objectContaining({ id: "FN-A" }));
});
});
});