From 9eff4d424eac8fbd42e3e1d8dacdf139feae1843 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 17 Jun 2026 11:00:02 -0700 Subject: [PATCH] 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 --- ...kDetailModal.attachments-and-tabs.test.tsx | 33 ++++++++ ...etailModal.github-tracking-header.test.tsx | 5 ++ ...DetailModal.github-tracking-stale.test.tsx | 11 +++ ...l.inline-editing-and-integrations.test.tsx | 83 +++++++++++++++++++ ...ailModal.models-progress-workflow.test.tsx | 33 ++++++++ .../TaskDetailModal.rendering.test.tsx | 73 ++++++++++++++++ ...Modal.responsive-and-dependencies.test.tsx | 37 +++++++++ .../__tests__/TaskDetailModal.test.tsx | 17 ++++ 8 files changed, 292 insertions(+) diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx index f40c4505a9..1df79b5731 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx @@ -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( + , + ); + + 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( { it("keeps the summary, enable action, and disclosure toggle on one row across desktop and mobile CSS", () => { render( { const { rerender } = render( {}} 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( {}} onMoveTask={noopMove} @@ -77,6 +83,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => { const { rerender } = render( {}} onMoveTask={noopMove} @@ -95,6 +102,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => { rerender( {}} onMoveTask={noopMove} @@ -131,6 +139,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => { const { rerender } = render( {}} onMoveTask={noopMove} @@ -147,6 +156,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => { rerender( {}} onMoveTask={noopMove} @@ -176,6 +186,7 @@ describe("TaskDetailModal GitHub tracking stale await guards (FN-5148)", () => { render( {}} onMoveTask={noopMove} diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx index 34dbeb4db2..1d47889021 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx @@ -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( { it("does not render GitHub badge for non-github providers", () => { render( { it("hides source issue read section when sourceIssue metadata is missing", () => { render( { it("prefills source issue inputs in edit mode", async () => { const { container } = render( { it("renders source issue block below Model Configuration in edit mode", async () => { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { it("shows Edit button in header when task is in triage column", () => { const { container } = render( { it("shows Edit button in header when task is in todo column", () => { const { container } = render( { it("does not show Edit button when task is in in-progress column", () => { const { container } = render( { it("does not show Edit button when already in edit mode", () => { const { container } = render( { it("entering edit mode shows title input and description textarea", () => { const { container } = render( { it("clicking Cancel exits edit mode without saving", () => { const { container } = render( { const { container } = render( { it("Save button is enabled in edit mode", () => { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { it("Escape key exits edit mode", async () => { const { container } = render( { it("edit mode shows both title and description fields", () => { const { container } = render( { it("edit mode renders model configuration and workflow steps", () => { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { it("renders normalized priority in detail metadata", async () => { render( { render( { render( { render( { render( { render( { render( { render( { render( { it("renders no-commits-expected toggle after plan and before attachments", () => { render( { render( { it("pre-populates form with existing task values", () => { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { return ( { it("renders Save and Cancel in the modal footer, not inside the edit form body", () => { const { container } = render( { it("renders keyboard hint in the modal footer when editing", () => { const { container } = render( { it("shows normal modal actions (not edit actions) when not editing", () => { const { container } = render( { render( { render( { it("shows Assign Agent button when task has no assigned agent", () => { render( { render( { render( { render( { const { container } = render( { render( { render( { render( { const { container } = render( { const { container } = render( { render( { render( { const { container } = render( { render( { it("renders after the prompt/spec section in read mode", () => { render( { it("renders linked issue as link when url exists", async () => { render( { render( { it("shows section when tracking is disabled and task is in an eligible column", () => { render( { render( { render( { render( { it("hides the inline enable button when tracking is already enabled", () => { render( { it("hides the inline enable button when an issue is already linked", () => { render( { it("hides section when tracking is disabled and task is not in an eligible column", () => { render( { render( { render( { render( { return ( { render( { mockConfirm.mockResolvedValueOnce(false); render( { return render( { return render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { it("renders step progress section when steps exist", () => { const { container } = render( { it("shows '(no steps defined)' when steps array is empty", () => { const { container } = render( { it("renders correct number of segments matching step count", () => { const { container } = render( { it("segments have correct status modifier classes", () => { const { container } = render( { it("segments have correct inline background colors based on status", () => { const { container } = render( { it("displays singular completion label for one-step tasks", () => { render( { it("displays correct completion count", () => { render( { it("has data-tooltip attribute with step name and status on each segment", () => { const { container } = render( { it("step progress only renders in Definition tab, not in Agent Log subview", () => { const { container } = render( { it("step progress is hidden in Comments tab", () => { const { container } = render( { ])("never shows a separate Commits tab for done tasks (%s) — changes are in the Changes tab", (_label, taskOverrides) => { const { container } = render( { render( { render( { ])("Workflow tab is always rendered (%s)", (_label, taskOverrides) => { render( { render( { render( { render( { render( { render( { it("hides Definition content when Workflow tab is active", async () => { const { container } = render( { render( { ] as const)("renders provenance text for %s", (sourceType, sourceAgentId, expectedText) => { render( { it("renders parent task link for refinement provenance", async () => { render( { it("renders compact github issue link for github import provenance", () => { render( { it("falls back to 'Open issue' label for unparseable github import URL", () => { render( { it("renders github import provenance with no issue URL as plain label", () => { render( { it("renders finding label for research provenance", () => { render( { it("falls back to run id for research provenance context", () => { render( { it.each(["unknown", undefined] as const)("omits provenance for %s source", (sourceType) => { render( { it("FN-3755 renders provenance before created-updated timestamps", () => { const { container } = render( { it("keeps inline controls, provenance, and timestamps as direct detail-meta children", () => { const { container } = render( { it("keeps the optional PR link row in the same detail-meta row as provenance and timestamps", () => { const { container } = render( { it("renders compact relative timestamps for recent tasks", () => { render( { it("renders short calendar date for older timestamps", () => { render( { it("shows active file scope overlap blocker in Dependencies section", () => { render( { it("renders clear overlap blocker button only when overlapBlockedBy is present", () => { const { rerender } = render( { rerender( { render( { render( { render( { it("shows overlap blockedBy summary in Blocking section", () => { render( { it("renders modal wrapper structure and default close control", () => { const { container } = render( { it("renders mobile back control variant when requested", () => { render( { it("renders markdown-body without detail-prompt class when prompt exists", () => { const { container } = render( { it("strips the leading heading from prompt and renders remaining markdown", () => { const { container } = render( { it("renders (no prompt) with detail-prompt class when prompt is absent", () => { const { container } = render( { it("does not render a PROMPT.md heading", () => { render( { it("renders Review and Comments tabs", () => { render( { it("shows non-PR review shell message in Review tab", async () => { render( { render( { }); render( { it("keeps inline priority and execution controls aligned with shared sizing and gap", () => { render( { it("renders standard mode as an unpressed toggle", () => { render( { it("renders fast mode as a pressed toggle", () => { render( { render( { it("leaves attachment href/src URLs unchanged when no daemon token is present", () => { render( { it("renders Retry button when task status is 'failed' (in Actions dropdown)", () => { render( { it("does NOT render Retry button when task status is not 'failed'", () => { render( { it("does NOT render Retry button when onRetryTask is not provided", () => { render( { it("shows exactly one Retry button when task is in-review AND failed (in Actions dropdown)", () => { render( { it("shows exactly one Retry button when task is in-review AND stuck-killed (in Actions dropdown)", () => { render( { it("shows Retry for a stranded planning triage task", () => { render( { render( { render( { render( { it("shows in-review split button with primary action and secondary move option", () => { const { container } = render( { it("in-review failed task shows both Retry action and secondary move option", async () => { render( { it("split-button renders with chevron when multiple transitions exist", async () => { const { container } = render( { render( { it("chevron dropdown includes only secondary transitions", async () => { render( { it("shows description exactly once for a task without title", () => { const { container } = render( { it("shows the title in

when task.title is set", () => { const { container } = render( { const renderDetail = (taskOverrides: Parameters[0] = {}) => render( { const triageDescription = "H".repeat(250); const { container, rerender } = render( { rerender( { // With title const { container: withTitle } = render( { // Without title const { container: withoutTitle } = render( { const { container } = render( { render( { render( { render( { const { container } = render( { const { container } = render( { render( { render( { it("hides near-duplicate banner once dismissed", () => { render( { ])("hides near-duplicate decision banner when canonical is %s", (_label, canonical) => { render( { render( { render( { it("modal-actions contains Delete and Pause buttons for non-done tasks (via Actions dropdown)", () => { render( { render( { render( { render( { render( { render( { render( { render( { render( { render( { render( { render( { render( { it("in-review modal-actions contains Merge & Close and Back to In Progress buttons", () => { render( { render( { render( { render( { render( { render( { it("shows linked PR number in detail metadata for in-review tasks", () => { render( { it("shows linked PR number in merge details for done tasks", () => { render( { it("shows PR automation waiting label instead of Merge & Close when awaiting PR checks", () => { render( { it("shows Creating PR label while PR-first automation is creating a PR", () => { render( { function renderWithSearch(taskOverrides: Partial = {}) { return render( { const { container } = render( { const { container } = render( { it("renders dependency ID as label when no title or description available", () => { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { const { container } = render( { render( { const { container } = render( [0] = { const result = render( { const user = userEvent.setup(); render( { const user = userEvent.setup(); render( { const user = userEvent.setup(); render( { render( { render( { it("renders branch group card when task has group context", () => { render( { render( { const user = userEvent.setup(); render( { const user = userEvent.setup(); render( { const user = userEvent.setup(); render( { it("FN-4570: hides merge-blocker diagnostic while task is actively merging", () => { render( { ])("does not render diagnostic row for $label", ({ task }) => { render(