FN-6574: repair TaskDetailModal definition test lanes

Repair TaskDetailModal tests so Definition-surface assertions remain valid after Chat became the default tab.

- Add regression coverage for explicitly opening the Definition tab while the default tab remains Chat.
- Set definition-focused TaskDetailModal test renders to use initialTab="definition" across the split dashboard lanes.
- Document why Definition-only assertions opt into the Definition tab after the Chat-default-tab change.

Files changed:
 .../TaskDetailModal.attachments-and-tabs.test.tsx  | 33 +++++++++
 ...TaskDetailModal.github-tracking-header.test.tsx |  5 ++
 .../TaskDetailModal.github-tracking-stale.test.tsx | 11 +++
 ...lModal.inline-editing-and-integrations.test.tsx | 83 ++++++++++++++++++++++
 ...skDetailModal.models-progress-workflow.test.tsx | 33 +++++++++
 .../__tests__/TaskDetailModal.rendering.test.tsx   | 73 +++++++++++++++++++
 ...etailModal.responsive-and-dependencies.test.tsx | 37 ++++++++++
 .../components/__tests__/TaskDetailModal.test.tsx  | 17 +++++
 8 files changed, 292 insertions(+)

Fusion-Task-Id: FN-6574

Fusion-Task-Lineage: f8d5e038-058a-4d12-8384-2ef603988e3a
This commit is contained in:
gsxdsm
2026-06-17 11:00:02 -07:00
parent bbceb16d66
commit 9eff4d424e
8 changed files with 292 additions and 0 deletions

View File

@@ -17,6 +17,10 @@ import {
} from "./TaskDetailModal.test-helpers";
import { TaskDetailModal, TaskDetailContent } from "../TaskDetailModal";
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Definition-tab regression coverage must prove both the no-`initialTab` Chat landing state and the explicit `initialTab="definition"` Definition surface for prompt, GitHub tracking, and dependency sections.
*/
setupTaskDetailModalHooks();
describe("TaskDetailModal", () => {
@@ -988,6 +992,35 @@ describe("TaskDetailModal", () => {
expect(container.querySelector(".detail-section--chat")).toBeNull();
});
it("FN-6574 renders Definition-only content when initialTab requests definition", () => {
const blocker = makeTask({ id: "FN-6574", title: "Definition task", prompt: "# Spec\n\nDefinition body unique text.", dependencies: ["FN-100"], githubTracking: { enabled: true } });
const dependency = makeTask({ id: "FN-100", title: "Dependency task" });
const dependent = makeTask({ id: "FN-200", title: "Dependent task", dependencies: ["FN-6574"] });
const { container } = render(
<TaskDetailModal
task={blocker}
tasks={[blocker, dependency, dependent]}
initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
expect(screen.getByRole("button", { name: "Definition" })).toHaveClass("detail-tab-active");
expect(screen.getByRole("button", { name: "Chat" })).not.toHaveClass("detail-tab-active");
expect(container.querySelector(".detail-section--chat")).toBeNull();
expect(screen.getByText("Definition body unique text.")).toBeInTheDocument();
expect(screen.getByText("GitHub tracking")).toBeInTheDocument();
expect(screen.getByText("Dependencies")).toBeInTheDocument();
expect(screen.getByText("Blocking")).toBeInTheDocument();
expect(container).toHaveTextContent("FN-100");
expect(container).toHaveTextContent("FN-200");
});
it("FN-6347 applies chat modifiers only while the Chat tab is active", () => {
const { container } = render(
<TaskDetailModal

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { loadAllAppCss } from "../../test/cssFixture";
@@ -10,6 +14,7 @@ describe("FN-4224 GitHub tracking header layout", () => {
it("keeps the summary, enable action, and disclosure toggle on one row across desktop and mobile CSS", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "todo",
githubTracking: { enabled: false },

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
@@ -25,6 +29,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
@@ -39,6 +44,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
await user.click(screen.getByRole("button", { name: "Enable GitHub tracking" }));
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
@@ -77,6 +83,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
@@ -95,6 +102,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
@@ -131,6 +139,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}
@@ -147,6 +156,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
rerender(
<TaskDetailModal
initialTab="definition"
task={taskB}
onClose={() => {}}
onMoveTask={noopMove}
@@ -176,6 +186,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => {
render(
<TaskDetailModal
initialTab="definition"
task={taskA}
onClose={() => {}}
onMoveTask={noopMove}

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { useState } from "react";
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
@@ -76,6 +80,7 @@ describe("TaskDetailModal", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceIssue: {
provider: "github",
@@ -138,6 +143,7 @@ describe("TaskDetailModal", () => {
it("does not render GitHub badge for non-github providers", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceIssue: {
provider: "gitlab",
@@ -163,6 +169,7 @@ describe("TaskDetailModal", () => {
it("hides source issue read section when sourceIssue metadata is missing", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceIssue: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -180,6 +187,7 @@ describe("TaskDetailModal", () => {
it("prefills source issue inputs in edit mode", async () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -214,6 +222,7 @@ describe("TaskDetailModal", () => {
it("renders source issue block below Model Configuration in edit mode", async () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -255,6 +264,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -304,6 +314,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -346,6 +357,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -385,6 +397,7 @@ describe("TaskDetailModal", () => {
it("shows Edit button in header when task is in triage column", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -402,6 +415,7 @@ describe("TaskDetailModal", () => {
it("shows Edit button in header when task is in todo column", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -419,6 +433,7 @@ describe("TaskDetailModal", () => {
it("does not show Edit button when task is in in-progress column", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "in-progress", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -436,6 +451,7 @@ describe("TaskDetailModal", () => {
it("does not show Edit button when already in edit mode", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -460,6 +476,7 @@ describe("TaskDetailModal", () => {
it("entering edit mode shows title input and description textarea", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task", description: "Test description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -486,6 +503,7 @@ describe("TaskDetailModal", () => {
it("clicking Cancel exits edit mode without saving", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Original title", description: "Original description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -518,6 +536,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Original title", description: "Original description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -551,6 +570,7 @@ describe("TaskDetailModal", () => {
it("Save button is enabled in edit mode", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test title", description: "Test description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -576,6 +596,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Original" })}
onClose={noop}
onMoveTask={noopMove}
@@ -608,6 +629,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Original" })}
onClose={noop}
onMoveTask={noopMove}
@@ -644,6 +666,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Original" })}
onClose={noop}
onMoveTask={noopMove}
@@ -674,6 +697,7 @@ describe("TaskDetailModal", () => {
it("Escape key exits edit mode", async () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test title" })}
onClose={noop}
onMoveTask={noopMove}
@@ -701,6 +725,7 @@ describe("TaskDetailModal", () => {
it("edit mode shows both title and description fields", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test title", description: "Test description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -722,6 +747,7 @@ describe("TaskDetailModal", () => {
it("edit mode renders model configuration and workflow steps", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -749,6 +775,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test", description: "Desc", dependencies: ["FN-002"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -782,6 +809,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test", description: "Desc", priority: "normal" })}
onClose={noop}
onMoveTask={noopMove}
@@ -817,6 +845,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test", description: "Desc", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -843,6 +872,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test", description: "Desc", executionMode: "fast" })}
onClose={noop}
onMoveTask={noopMove}
@@ -869,6 +899,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test", description: "Desc", executionMode: "fast" })}
onClose={noop}
onMoveTask={noopMove}
@@ -890,6 +921,7 @@ describe("TaskDetailModal", () => {
it("renders normalized priority in detail metadata", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", description: "Priority metadata", priority: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -913,6 +945,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", priority: "high", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -956,6 +989,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "triage",
@@ -995,6 +1029,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", description: "Priority metadata", priority: "high" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1022,6 +1057,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", description: "Priority metadata", priority: "low" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1054,6 +1090,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1085,6 +1122,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", executionMode: "fast" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1113,6 +1151,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1143,6 +1182,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1165,6 +1205,7 @@ describe("TaskDetailModal", () => {
it("renders no-commits-expected toggle after plan and before attachments", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -1197,6 +1238,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", noCommitsExpected: false })}
onClose={noop}
onMoveTask={noopMove}
@@ -1217,6 +1259,7 @@ describe("TaskDetailModal", () => {
it("pre-populates form with existing task values", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", title: "My Task", description: "My Description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1243,6 +1286,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", branch: "feature/fn-3422", baseBranch: "develop" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1275,6 +1319,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", branch: "feature/fn-3422", baseBranch: "develop" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1301,6 +1346,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", branch: "feature/fn-3422", baseBranch: "main" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1345,6 +1391,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={initialTask}
onClose={noop}
onMoveTask={noopMove}
@@ -1404,6 +1451,7 @@ describe("TaskDetailModal", () => {
return (
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1462,6 +1510,7 @@ describe("TaskDetailModal", () => {
it("renders Save and Cancel in the modal footer, not inside the edit form body", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1496,6 +1545,7 @@ describe("TaskDetailModal", () => {
it("renders keyboard hint in the modal footer when editing", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "triage", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1520,6 +1570,7 @@ describe("TaskDetailModal", () => {
it("shows normal modal actions (not edit actions) when not editing", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", title: "Test task" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1556,6 +1607,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -1589,6 +1641,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -1618,6 +1671,7 @@ describe("TaskDetailModal", () => {
it("shows Assign Agent button when task has no assigned agent", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ assignedAgentId: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -1647,6 +1701,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ assignedAgentId: "agent-002" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1680,6 +1735,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ assignedAgentId: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -1715,6 +1771,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ assignedAgentId: "agent-005" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1768,6 +1825,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1813,6 +1871,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1847,6 +1906,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={detail}
onClose={noop}
onMoveTask={noopMove}
@@ -1886,6 +1946,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1960,6 +2021,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -2034,6 +2096,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={strippedTask}
onClose={noop}
onMoveTask={noopMove}
@@ -2083,6 +2146,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -2122,6 +2186,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onOpenDetail={noop}
@@ -2153,6 +2218,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onOpenDetail={noop}
@@ -2181,6 +2247,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onOpenDetail={noop}
@@ -2208,6 +2275,7 @@ describe("TaskDetailModal", () => {
it("renders after the prompt/spec section in read mode", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -2236,6 +2304,7 @@ describe("TaskDetailModal", () => {
it("renders linked issue as link when url exists", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
githubTracking: {
enabled: true,
@@ -2295,6 +2364,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={optimisticTask}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2319,6 +2389,7 @@ describe("TaskDetailModal", () => {
it("shows section when tracking is disabled and task is in an eligible column", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "todo", githubTracking: { enabled: false } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2351,6 +2422,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={optimisticTask}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2390,6 +2462,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -2432,6 +2505,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -2476,6 +2550,7 @@ describe("TaskDetailModal", () => {
it("hides the inline enable button when tracking is already enabled", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "todo", githubTracking: { enabled: true } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2493,6 +2568,7 @@ describe("TaskDetailModal", () => {
it("hides the inline enable button when an issue is already linked", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "todo",
githubTracking: {
@@ -2546,6 +2622,7 @@ describe("TaskDetailModal", () => {
it("hides section when tracking is disabled and task is not in an eligible column", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "done", githubTracking: { enabled: false } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2582,6 +2659,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "done", githubTracking: { enabled: true } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2611,6 +2689,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",
@@ -2654,6 +2733,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "in-progress",
@@ -2713,6 +2793,7 @@ describe("TaskDetailModal", () => {
return (
<TaskDetailModal
initialTab="definition"
task={taskState}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2759,6 +2840,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-001", column: "todo", githubTracking: { enabled: true, repoOverride: "runfusion/fusion" } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
@@ -2794,6 +2876,7 @@ describe("TaskDetailModal", () => {
mockConfirm.mockResolvedValueOnce(false);
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-001",
column: "todo",

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
@@ -47,6 +51,7 @@ describe("TaskDetailModal", () => {
return render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -81,6 +86,7 @@ describe("TaskDetailModal", () => {
return render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent", ...taskOverrides })}
onClose={noop}
onMoveTask={noopMove}
@@ -220,6 +226,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -288,6 +295,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -333,6 +341,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -408,6 +417,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
prompt: "# Hello\n\nContent",
planningModelProvider: "google",
@@ -459,6 +469,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -505,6 +516,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent" })}
onClose={noop}
onMoveTask={noopMove}
@@ -561,6 +573,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nContent", assignedAgentId: "agent-1", status: "executing", column: "in-progress" })}
onClose={noop}
onMoveTask={noopMove}
@@ -589,6 +602,7 @@ describe("TaskDetailModal", () => {
it("renders step progress section when steps exist", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Step 1", status: "done" },
@@ -611,6 +625,7 @@ describe("TaskDetailModal", () => {
it("shows '(no steps defined)' when steps array is empty", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ steps: [] })}
onClose={noop}
onMoveTask={noopMove}
@@ -628,6 +643,7 @@ describe("TaskDetailModal", () => {
it("renders correct number of segments matching step count", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Step 1", status: "done" },
@@ -651,6 +667,7 @@ describe("TaskDetailModal", () => {
it("segments have correct status modifier classes", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Step 1", status: "done" },
@@ -678,6 +695,7 @@ describe("TaskDetailModal", () => {
it("segments have correct inline background colors based on status", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Step 1", status: "done" },
@@ -707,6 +725,7 @@ describe("TaskDetailModal", () => {
it("displays singular completion label for one-step tasks", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [{ name: "Step 1", status: "done" }],
})}
@@ -726,6 +745,7 @@ describe("TaskDetailModal", () => {
it("displays correct completion count", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Step 1", status: "done" },
@@ -750,6 +770,7 @@ describe("TaskDetailModal", () => {
it("has data-tooltip attribute with step name and status on each segment", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
steps: [
{ name: "Initialize project", status: "done" },
@@ -773,6 +794,7 @@ describe("TaskDetailModal", () => {
it("step progress only renders in Definition tab, not in Agent Log subview", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
prompt: "# Test",
steps: [
@@ -802,6 +824,7 @@ describe("TaskDetailModal", () => {
it("step progress is hidden in Comments tab", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
prompt: "# Test",
steps: [
@@ -834,6 +857,7 @@ describe("TaskDetailModal", () => {
])("never shows a separate Commits tab for done tasks (%s) — changes are in the Changes tab", (_label, taskOverrides) => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask(taskOverrides)}
onClose={noop}
onMoveTask={noopMove}
@@ -860,6 +884,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -893,6 +918,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -925,6 +951,7 @@ describe("TaskDetailModal", () => {
])("Workflow tab is always rendered (%s)", (_label, taskOverrides) => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask(taskOverrides)}
onClose={noop}
onMoveTask={noopMove}
@@ -954,6 +981,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ enabledWorkflowSteps: ["WS-001"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -982,6 +1010,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ enabledWorkflowSteps: ["WS-001"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -1007,6 +1036,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ enabledWorkflowSteps: ["WS-001"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -1034,6 +1064,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ enabledWorkflowSteps: ["WS-001"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -1077,6 +1108,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ enabledWorkflowSteps: ["WS-001", "WS-002"] })}
onClose={noop}
onMoveTask={noopMove}
@@ -1100,6 +1132,7 @@ describe("TaskDetailModal", () => {
it("hides Definition content when Workflow tab is active", async () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
enabledWorkflowSteps: ["WS-001"],
prompt: "# Test prompt",

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
@@ -27,6 +31,7 @@ describe("TaskDetailModal", () => {
render(
<FileBrowserProvider openFile={openFile}>
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "done",
summary: "See `packages/dashboard/app/App.tsx:12` for context.",
@@ -59,6 +64,7 @@ describe("TaskDetailModal", () => {
] as const)("renders provenance text for %s", (sourceType, sourceAgentId, expectedText) => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceType, sourceAgentId })}
onClose={noop}
onMoveTask={noopMove}
@@ -78,6 +84,7 @@ describe("TaskDetailModal", () => {
it("renders parent task link for refinement provenance", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceType: "task_refine", sourceParentTaskId: "FN-001" })}
onClose={noop}
onMoveTask={noopMove}
@@ -100,6 +107,7 @@ describe("TaskDetailModal", () => {
it("renders compact github issue link for github import provenance", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "github_import",
sourceMetadata: { issueUrl: "https://github.com/owner/repo/issues/42" },
@@ -128,6 +136,7 @@ describe("TaskDetailModal", () => {
it("falls back to 'Open issue' label for unparseable github import URL", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "github_import",
sourceMetadata: { issueUrl: "https://example.com/something" },
@@ -151,6 +160,7 @@ describe("TaskDetailModal", () => {
it("renders github import provenance with no issue URL as plain label", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "github_import",
sourceMetadata: {},
@@ -171,6 +181,7 @@ describe("TaskDetailModal", () => {
it("renders finding label for research provenance", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "research",
sourceMetadata: {
@@ -195,6 +206,7 @@ describe("TaskDetailModal", () => {
it("falls back to run id for research provenance context", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "research",
sourceMetadata: { runId: "RR-456" },
@@ -216,6 +228,7 @@ describe("TaskDetailModal", () => {
it.each(["unknown", undefined] as const)("omits provenance for %s source", (sourceType) => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceType })}
onClose={noop}
onMoveTask={noopMove}
@@ -232,6 +245,7 @@ describe("TaskDetailModal", () => {
it("FN-3755 renders provenance before created-updated timestamps", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceType: "dashboard_ui" })}
onClose={noop}
onMoveTask={noopMove}
@@ -253,6 +267,7 @@ describe("TaskDetailModal", () => {
it("keeps inline controls, provenance, and timestamps as direct detail-meta children", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceType: "task_refine", sourceParentTaskId: "FN-001" })}
onClose={noop}
onMoveTask={noopMove}
@@ -277,6 +292,7 @@ describe("TaskDetailModal", () => {
it("keeps the optional PR link row in the same detail-meta row as provenance and timestamps", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "dashboard_ui",
prInfo: { number: 42, url: "https://github.com/owner/repo/pull/42" },
@@ -316,6 +332,7 @@ describe("TaskDetailModal", () => {
it("renders compact relative timestamps for recent tasks", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "dashboard_ui",
createdAt: "2026-05-09T12:00:00.000Z",
@@ -344,6 +361,7 @@ describe("TaskDetailModal", () => {
it("renders short calendar date for older timestamps", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
sourceType: "dashboard_ui",
createdAt: "2026-05-01T12:00:00.000Z",
@@ -368,6 +386,7 @@ describe("TaskDetailModal", () => {
it("shows active file scope overlap blocker in Dependencies section", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER" })}
tasks={[
makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER" }),
@@ -389,6 +408,7 @@ describe("TaskDetailModal", () => {
it("renders clear overlap blocker button only when overlapBlockedBy is present", () => {
const { rerender } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER" })}
onClose={noop}
onMoveTask={noopMove}
@@ -403,6 +423,7 @@ describe("TaskDetailModal", () => {
rerender(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -423,6 +444,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER", status: "queued" })}
onClose={noop}
onMoveTask={noopMove}
@@ -451,6 +473,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER", status: "planning" })}
onClose={noop}
onMoveTask={noopMove}
@@ -478,6 +501,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-T", column: "todo", overlapBlockedBy: "FN-OVER", status: "queued" })}
onClose={noop}
onMoveTask={noopMove}
@@ -499,6 +523,7 @@ describe("TaskDetailModal", () => {
it("shows overlap blockedBy summary in Blocking section", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ id: "FN-B", column: "in-progress" })}
tasks={[
makeTask({ id: "FN-B", column: "in-progress" }),
@@ -520,6 +545,7 @@ describe("TaskDetailModal", () => {
it("renders modal wrapper structure and default close control", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -539,6 +565,7 @@ describe("TaskDetailModal", () => {
it("renders mobile back control variant when requested", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -597,6 +624,7 @@ describe("TaskDetailModal", () => {
it("renders markdown-body without detail-prompt class when prompt exists", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nSome **bold** text" })}
onClose={noop}
onMoveTask={noopMove}
@@ -615,6 +643,7 @@ describe("TaskDetailModal", () => {
it("strips the leading heading from prompt and renders remaining markdown", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Hello\n\nSome **bold** text" })}
onClose={noop}
onMoveTask={noopMove}
@@ -633,6 +662,7 @@ describe("TaskDetailModal", () => {
it("renders (no prompt) with detail-prompt class when prompt is absent", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: undefined })}
onClose={noop}
onMoveTask={noopMove}
@@ -652,6 +682,7 @@ describe("TaskDetailModal", () => {
it("does not render a PROMPT.md heading", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Some prompt content" })}
onClose={noop}
onMoveTask={noopMove}
@@ -668,6 +699,7 @@ describe("TaskDetailModal", () => {
it("renders Review and Comments tabs", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -685,6 +717,7 @@ describe("TaskDetailModal", () => {
it("shows non-PR review shell message in Review tab", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ reviewState: { source: "reviewer-agent", items: [], addressing: [] } })}
onClose={noop}
onMoveTask={noopMove}
@@ -733,6 +766,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ reviewState: { source: "pull-request", summary: { reviewDecision: "REVIEW_REQUIRED", reviewers: [], blockingReasons: [], checks: [] }, items: [], addressing: [] } })}
onClose={noop}
onMoveTask={noopMove}
@@ -771,6 +805,7 @@ describe("TaskDetailModal", () => {
});
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ reviewState: { source: "pull-request", summary: { reviewDecision: "CHANGES_REQUESTED", reviewers: [{ login: "octocat", state: "CHANGES_REQUESTED" }], blockingReasons: ["changes requested review is active"], checks: [] }, items: [], addressing: [] } })}
onClose={noop}
onMoveTask={noopMove}
@@ -790,6 +825,7 @@ describe("TaskDetailModal", () => {
it("keeps inline priority and execution controls aligned with shared sizing and gap", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "todo", priority: "high", executionMode: "fast" })}
onClose={noop}
onMoveTask={noopMove}
@@ -818,6 +854,7 @@ describe("TaskDetailModal", () => {
it("renders standard mode as an unpressed toggle", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "triage", executionMode: "standard" })}
onClose={noop}
onMoveTask={noopMove}
@@ -837,6 +874,7 @@ describe("TaskDetailModal", () => {
it("renders fast mode as a pressed toggle", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "todo", executionMode: "fast" })}
onClose={noop}
onMoveTask={noopMove}
@@ -870,6 +908,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
attachments: [
{
@@ -904,6 +943,7 @@ describe("TaskDetailModal", () => {
it("leaves attachment href/src URLs unchanged when no daemon token is present", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
attachments: [
{
@@ -934,6 +974,7 @@ describe("TaskDetailModal", () => {
it("renders Retry button when task status is 'failed' (in Actions dropdown)", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ status: "failed" })}
onClose={noop}
onMoveTask={noopMove}
@@ -955,6 +996,7 @@ describe("TaskDetailModal", () => {
it("does NOT render Retry button when task status is not 'failed'", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ status: "executing" })}
onClose={noop}
onMoveTask={noopMove}
@@ -975,6 +1017,7 @@ describe("TaskDetailModal", () => {
it("does NOT render Retry button when onRetryTask is not provided", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ status: "failed" })}
onClose={noop}
onMoveTask={noopMove}
@@ -992,6 +1035,7 @@ describe("TaskDetailModal", () => {
it("shows exactly one Retry button when task is in-review AND failed (in Actions dropdown)", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "failed" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1014,6 +1058,7 @@ describe("TaskDetailModal", () => {
it("shows exactly one Retry button when task is in-review AND stuck-killed (in Actions dropdown)", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "stuck-killed" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1036,6 +1081,7 @@ describe("TaskDetailModal", () => {
it("shows Retry for a stranded planning triage task", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "triage", status: "planning", stuckKillCount: 6 })}
onClose={noop}
onMoveTask={noopMove}
@@ -1060,6 +1106,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "failed" })}
onClose={onClose}
onMoveTask={noopMove}
@@ -1095,6 +1142,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "failed" })}
onClose={onClose}
onMoveTask={noopMove}
@@ -1134,6 +1182,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "failed" })}
onClose={onClose}
onMoveTask={noopMove}
@@ -1167,6 +1216,7 @@ describe("TaskDetailModal", () => {
it("shows in-review split button with primary action and secondary move option", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1194,6 +1244,7 @@ describe("TaskDetailModal", () => {
it("in-review failed task shows both Retry action and secondary move option", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review", status: "failed" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1223,6 +1274,7 @@ describe("TaskDetailModal", () => {
it("split-button renders with chevron when multiple transitions exist", async () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-progress" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1258,6 +1310,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-progress" })}
onClose={noop}
onMoveTask={onMoveTask}
@@ -1279,6 +1332,7 @@ describe("TaskDetailModal", () => {
it("chevron dropdown includes only secondary transitions", async () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-progress" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1306,6 +1360,7 @@ describe("TaskDetailModal", () => {
it("shows description exactly once for a task without title", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
title: undefined,
description: "Fix the login bug",
@@ -1335,6 +1390,7 @@ describe("TaskDetailModal", () => {
it("shows the title in <h2> when task.title is set", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
title: "Implement dark mode",
description: "Add dark mode toggle to the settings page",
@@ -1365,6 +1421,7 @@ describe("TaskDetailModal", () => {
const renderDetail = (taskOverrides: Parameters<typeof makeTask>[0] = {}) => render(
<TaskDetailModal
initialTab="definition"
task={makeTask(taskOverrides)}
onClose={noop}
onMoveTask={noopMove}
@@ -1519,6 +1576,7 @@ describe("TaskDetailModal", () => {
const triageDescription = "H".repeat(250);
const { container, rerender } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-TODO",
column: "todo",
@@ -1539,6 +1597,7 @@ describe("TaskDetailModal", () => {
rerender(
<TaskDetailModal
initialTab="definition"
task={makeTask({
id: "FN-TRIAGE",
column: "triage",
@@ -1627,6 +1686,7 @@ describe("TaskDetailModal", () => {
// With title
const { container: withTitle } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ title: "Some title" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1641,6 +1701,7 @@ describe("TaskDetailModal", () => {
// Without title
const { container: withoutTitle } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ title: undefined, description: "A description" })}
onClose={noop}
onMoveTask={noopMove}
@@ -1688,6 +1749,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1733,6 +1795,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1767,6 +1830,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={detail}
onClose={noop}
onMoveTask={noopMove}
@@ -1806,6 +1870,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1881,6 +1946,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -1955,6 +2021,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={strippedTask}
onClose={noop}
onMoveTask={noopMove}
@@ -2004,6 +2071,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -2036,6 +2104,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceMetadata: { nearDuplicateOf: "FN-1234" } })}
tasks={[makeTask({ id: "FN-1234" })]}
onClose={noop}
@@ -2058,6 +2127,7 @@ describe("TaskDetailModal", () => {
it("hides near-duplicate banner once dismissed", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceMetadata: { nearDuplicateOf: "FN-1234", nearDuplicateDismissed: true } })}
tasks={[makeTask({ id: "FN-1234" })]}
onClose={noop}
@@ -2079,6 +2149,7 @@ describe("TaskDetailModal", () => {
])("hides near-duplicate decision banner when canonical is %s", (_label, canonical) => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceMetadata: { nearDuplicateOf: "FN-1234" } })}
tasks={canonical ? [canonical] : []}
onClose={noop}
@@ -2101,6 +2172,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ sourceMetadata: { nearDuplicateOf: "FN-1234" } })}
tasks={[makeTask({ id: "FN-1234" })]}
onClose={noop}
@@ -2155,6 +2227,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
@@ -147,6 +151,7 @@ describe("TaskDetailModal", () => {
it("modal-actions contains Delete and Pause buttons for non-done tasks (via Actions dropdown)", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-progress" as Column })}
onClose={noop}
onMoveTask={noopMove}
@@ -175,6 +180,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
githubTracking: {
enabled: true,
@@ -212,6 +218,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ githubTracking: { enabled: true, issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00.000Z" } } })}
onClose={noop}
onMoveTask={noopMove}
@@ -238,6 +245,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ githubTracking: { enabled: true, issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00.000Z" } } })}
onClose={noop}
onMoveTask={noopMove}
@@ -262,6 +270,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -300,6 +309,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ githubTracking: { enabled: true, issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00.000Z" } } })}
onClose={noop}
onMoveTask={noopMove}
@@ -361,6 +371,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -397,6 +408,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ githubTracking: { enabled: true, issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00.000Z" } } })}
onClose={noop}
onMoveTask={noopMove}
@@ -433,6 +445,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
@@ -475,6 +488,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ githubTracking: { enabled: true, issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00.000Z" } } })}
onClose={noop}
onMoveTask={noopMove}
@@ -504,6 +518,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "todo" as any })}
onClose={noop}
onMoveTask={noopMove}
@@ -540,6 +555,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "done" as any })}
onClose={noop}
onMoveTask={noopMove}
@@ -573,6 +589,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "done" as any })}
onClose={noop}
onMoveTask={noopMove}
@@ -596,6 +613,7 @@ describe("TaskDetailModal", () => {
it("in-review modal-actions contains Merge & Close and Back to In Progress buttons", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column })}
onClose={noop}
onMoveTask={noopMove}
@@ -625,6 +643,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column })}
onClose={noop}
onMoveTask={noopMove}
@@ -654,6 +673,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column })}
onClose={noop}
onMoveTask={noopMove}
@@ -700,6 +720,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review" as Column,
prInfo: {
@@ -749,6 +770,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review" as Column,
prInfo: {
@@ -795,6 +817,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review" as Column,
prInfo: {
@@ -823,6 +846,7 @@ describe("TaskDetailModal", () => {
it("shows linked PR number in detail metadata for in-review tasks", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column, prInfo: {
url: "https://github.com/owner/repo/pull/42",
number: 42,
@@ -847,6 +871,7 @@ describe("TaskDetailModal", () => {
it("shows linked PR number in merge details for done tasks", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "done" as Column,
prInfo: {
@@ -877,6 +902,7 @@ describe("TaskDetailModal", () => {
it("shows PR automation waiting label instead of Merge & Close when awaiting PR checks", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column, status: "awaiting-pr-checks", prInfo: {
url: "https://github.com/owner/repo/pull/42",
number: 42,
@@ -903,6 +929,7 @@ describe("TaskDetailModal", () => {
it("shows Creating PR label while PR-first automation is creating a PR", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "in-review" as Column, status: "creating-pr" })}
onClose={noop}
onMoveTask={noopMove}
@@ -930,6 +957,7 @@ describe("TaskDetailModal", () => {
function renderWithSearch(taskOverrides: Partial<TaskDetail> = {}) {
return render(
<TaskDetailModal
initialTab="definition"
task={makeTask(taskOverrides)}
tasks={searchTasks}
onClose={noop}
@@ -1024,6 +1052,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001", "FN-002"] })}
tasks={allTasks}
onClose={noop}
@@ -1058,6 +1087,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
tasks={allTasks}
onClose={noop}
@@ -1077,6 +1107,7 @@ describe("TaskDetailModal", () => {
it("renders dependency ID as label when no title or description available", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
// No tasks prop - dependency not found
onClose={noop}
@@ -1103,6 +1134,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
tasks={allTasks}
onClose={noop}
@@ -1129,6 +1161,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
tasks={allTasks}
onClose={noop}
@@ -1158,6 +1191,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
onOpenDetail={onOpenDetail}
onClose={noop}
@@ -1186,6 +1220,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
onOpenDetail={onOpenDetail}
onClose={noop}
@@ -1214,6 +1249,7 @@ describe("TaskDetailModal", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ dependencies: ["FN-001"] })}
onOpenDetail={onOpenDetail}
onClose={noop}
@@ -1246,6 +1282,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
<TaskDetailModal
initialTab="definition"
task={tasks[0]}
tasks={tasks}
onOpenDetail={noopOpenDetail}

View File

@@ -1,3 +1,7 @@
/*
FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-6532 made Chat the default TaskDetailModal tab. Tests that assert Definition-only sections must opt into `initialTab="definition"` so they verify the intended surface instead of the Chat landing state.
*/
import { describe, it, expect, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import type { ComponentProps } from "react";
@@ -34,6 +38,7 @@ function renderSummarizeTitleModal(overrides: Parameters<typeof makeTask>[0] = {
const result = render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}
@@ -169,6 +174,7 @@ describe("TaskDetailModal GitHub tracking CTA", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
githubTracking: { enabled: true },
title: "",
@@ -194,6 +200,7 @@ describe("TaskDetailModal GitHub tracking CTA", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
githubTracking: { enabled: true },
title: "Real title",
@@ -217,6 +224,7 @@ describe("TaskDetailModal GitHub tracking CTA", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
githubTracking: { enabled: true },
title: "",
@@ -281,6 +289,7 @@ describe("TaskDetailModal Logs activity loading", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeSlimTask() as any}
onClose={noop}
onMoveTask={noopMove}
@@ -418,6 +427,7 @@ describe("TaskDetailModal Logs agent loading", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ prompt: "# Loaded" })}
onClose={noop}
onMoveTask={noopMove}
@@ -442,6 +452,7 @@ describe("TaskDetailModal branch group surfacing", () => {
it("renders branch group card when task has group context", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ branchContext: { groupId: "BG-1", source: "planning", assignmentMode: "shared" } })}
onClose={noop}
onMoveTask={noopMove}
@@ -466,6 +477,7 @@ describe("TaskDetailModal delete affordance", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({ column: "done" })}
onClose={onClose}
onMoveTask={noopMove}
@@ -494,6 +506,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review",
inReviewStall: {
@@ -531,6 +544,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review",
mergeRetries: 3,
@@ -557,6 +571,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
const user = userEvent.setup();
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review",
inReviewStall: {
@@ -583,6 +598,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
it("FN-4570: hides merge-blocker diagnostic while task is actively merging", () => {
render(
<TaskDetailModal
initialTab="definition"
task={makeTask({
column: "in-review",
status: "merging-fix",
@@ -631,6 +647,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
])("does not render diagnostic row for $label", ({ task }) => {
render(
<TaskDetailModal
initialTab="definition"
task={task}
onClose={noop}
onMoveTask={noopMove}