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>
This commit is contained in:
7
.changeset/fn-7306-task-detail-activity-tab.md
Normal file
7
.changeset/fn-7306-task-detail-activity-tab.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Rename the task-detail Chat tab to Activity and make it first.
|
||||||
|
category: feature
|
||||||
|
dev: Keeps the internal `chat` tab id compatible for existing deep links and plugin callers.
|
||||||
@@ -194,20 +194,20 @@ function formatDurationCompact(ageMs: number): string {
|
|||||||
type TabId = "summary" | "definition" | "chat" | "logs" | "changes" | "review" | "pr" | "comments" | "model" | "workflow" | "documents" | "stats" | "routing" | "retries" | "terminal" | `plugin-${string}`;
|
type TabId = "summary" | "definition" | "chat" | "logs" | "changes" | "review" | "pr" | "comments" | "model" | "workflow" | "documents" | "stats" | "routing" | "retries" | "terminal" | `plugin-${string}`;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FNXC:TaskDetailSummaryTab 2026-06-27-00:00:
|
FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
Done tasks land on Summary instead of Chat so completed work opens on the completion overview. Chat stays the implicit default for every other column, and explicit tab requests continue to win for done tasks.
|
The existing task activity/steering surface keeps the stable internal `chat` tab id for deep-link/plugin compatibility, but its top-level user-facing label is Activity. Activity is the implicit default for every task column, including done tasks; Summary remains explicitly available for completed work until a later subtask adds the separate planner-model Chat surface.
|
||||||
|
|
||||||
FNXC:TaskDetailSummaryTab 2026-06-27-00:00:
|
FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
Only an omitted initial tab is the implicit default. Preserve explicit `initialTab="chat"` requests from plugins and task-detail entrypoints so done tasks can still deep-link directly to Chat.
|
Only an omitted initial tab is the implicit default. Preserve explicit `initialTab="chat"` requests from plugins and task-detail entrypoints so existing links continue to open the renamed Activity surface.
|
||||||
*/
|
*/
|
||||||
function resolveDefaultTab(initialTab: TabId | undefined, column: ColumnId): TabId {
|
function resolveDefaultTab(initialTab: TabId | undefined, _column: ColumnId): TabId {
|
||||||
if (initialTab === "retries") {
|
if (initialTab === "retries") {
|
||||||
return "definition";
|
return "definition";
|
||||||
}
|
}
|
||||||
if (initialTab) {
|
if (initialTab) {
|
||||||
return initialTab;
|
return initialTab;
|
||||||
}
|
}
|
||||||
return column === "done" ? "summary" : "chat";
|
return "chat";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lazy-load the terminal so xterm + addons stay out of the main bundle (U11).
|
// Lazy-load the terminal so xterm + addons stay out of the main bundle (U11).
|
||||||
@@ -501,8 +501,8 @@ export function TaskDetailContent({
|
|||||||
autoMergeEnabled: autoMergeEnabledProp,
|
autoMergeEnabled: autoMergeEnabledProp,
|
||||||
onOpenWorkflowEditor,
|
onOpenWorkflowEditor,
|
||||||
/**
|
/**
|
||||||
* FNXC:TaskDetailTabs 2026-06-17-00:00:
|
* FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
* FN-6532 makes Chat the default task-detail view when no caller supplies an explicit initial tab.
|
* The Activity tab is still addressed as `chat` internally so existing callers and deep links do not break while the label/order changes ahead of the future planner Chat tab.
|
||||||
*/
|
*/
|
||||||
initialTab,
|
initialTab,
|
||||||
mobileHeaderMode = "close",
|
mobileHeaderMode = "close",
|
||||||
@@ -3136,12 +3136,15 @@ export function TaskDetailContent({
|
|||||||
<>
|
<>
|
||||||
<div className="detail-tabs">
|
<div className="detail-tabs">
|
||||||
{/*
|
{/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-00:00:
|
FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
FN-6532 requires Chat to be the first task-detail tab while preserving every explicit tab entrypoint.
|
The existing task activity/steering surface is now labelled Activity and always renders first. Keep the `chat` tab id because a later subtask will add the separate planner-model Chat surface; this rename must not break existing `initialTab="chat"` callers.
|
||||||
|
|
||||||
FNXC:TaskDetailSummaryTab 2026-06-27-00:00:
|
|
||||||
Done tasks expose Summary as the first tab because the implicit Chat default resolves there for completed work; non-done tasks keep Chat first and never render an empty Summary shell.
|
|
||||||
*/}
|
*/}
|
||||||
|
<button
|
||||||
|
className={`detail-tab${activeTab === "chat" ? " detail-tab-active" : ""}`}
|
||||||
|
onClick={() => setActiveTab("chat")}
|
||||||
|
>
|
||||||
|
{t("taskDetail.tabs.activity", "Activity")}
|
||||||
|
</button>
|
||||||
{task.column === "done" && (
|
{task.column === "done" && (
|
||||||
<button
|
<button
|
||||||
className={`detail-tab${activeTab === "summary" ? " detail-tab-active" : ""}`}
|
className={`detail-tab${activeTab === "summary" ? " detail-tab-active" : ""}`}
|
||||||
@@ -3150,12 +3153,6 @@ export function TaskDetailContent({
|
|||||||
{t("taskDetail.tabs.summary", "Summary")}
|
{t("taskDetail.tabs.summary", "Summary")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
|
||||||
className={`detail-tab${activeTab === "chat" ? " detail-tab-active" : ""}`}
|
|
||||||
onClick={() => setActiveTab("chat")}
|
|
||||||
>
|
|
||||||
{t("taskDetail.tabs.chat", "Chat")}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
className={`detail-tab${activeTab === "definition" ? " detail-tab-active" : ""}`}
|
className={`detail-tab${activeTab === "definition" ? " detail-tab-active" : ""}`}
|
||||||
onClick={() => setActiveTab("definition")}
|
onClick={() => setActiveTab("definition")}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { TaskDetailModal, TaskDetailContent } from "../TaskDetailModal";
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
FN-7306 labels the stable internal `chat` tab as Activity and keeps it as the default TaskDetailModal tab. Definition-tab regression coverage must prove both the no-`initialTab` Activity landing state and the explicit `initialTab="definition"` Definition surface for prompt, GitHub tracking, and dependency sections.
|
||||||
*/
|
*/
|
||||||
setupTaskDetailModalHooks();
|
setupTaskDetailModalHooks();
|
||||||
|
|
||||||
@@ -439,7 +439,7 @@ describe("TaskDetailModal", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("tab toggle", () => {
|
describe("tab toggle", () => {
|
||||||
it("defaults to the Chat tab", () => {
|
it("defaults to the Activity tab", () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
||||||
@@ -452,11 +452,10 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText("Definition")).toBeTruthy();
|
expect(screen.getByText("Plan")).toBeTruthy();
|
||||||
expect(screen.getByText("Logs")).toBeTruthy();
|
expect(screen.getByText("Logs")).toBeTruthy();
|
||||||
// Activity and Agent Log are subviews inside the Logs tab, not top-level tabs.
|
// Logs subview controls should NOT be visible on the default Activity tab.
|
||||||
// They should NOT be visible on the default Chat tab.
|
expect(container.querySelector(".log-subview-toggle")).toBeNull();
|
||||||
expect(screen.queryByText("Activity")).toBeNull();
|
|
||||||
expect(screen.queryByText("Agent Log")).toBeNull();
|
expect(screen.queryByText("Agent Log")).toBeNull();
|
||||||
// Chat content should be visible by default.
|
// Chat content should be visible by default.
|
||||||
expect(container.querySelector(".detail-section--chat")).toBeTruthy();
|
expect(container.querySelector(".detail-section--chat")).toBeTruthy();
|
||||||
@@ -638,8 +637,8 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeTruthy();
|
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeTruthy();
|
||||||
expect(container.querySelector(".detail-activity")).toBeNull();
|
expect(container.querySelector(".detail-activity")).toBeNull();
|
||||||
|
|
||||||
// Switch back to Activity subview within Logs tab
|
// Switch back to Activity subview within Logs tab.
|
||||||
fireEvent.click(screen.getByText("Activity"));
|
fireEvent.click(container.querySelector<HTMLButtonElement>(".log-subview-toggle .log-subview-btn")!);
|
||||||
expect(container.querySelector(".detail-activity")).toBeTruthy();
|
expect(container.querySelector(".detail-activity")).toBeTruthy();
|
||||||
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeNull();
|
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeNull();
|
||||||
|
|
||||||
@@ -649,7 +648,7 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container.querySelector(".detail-activity")).toBeNull();
|
expect(container.querySelector(".detail-activity")).toBeNull();
|
||||||
|
|
||||||
// Switch back to Definition tab
|
// Switch back to Definition tab
|
||||||
fireEvent.click(screen.getByText("Definition"));
|
fireEvent.click(screen.getByText("Plan"));
|
||||||
expect(container.querySelector(".markdown-body")).toBeTruthy();
|
expect(container.querySelector(".markdown-body")).toBeTruthy();
|
||||||
expect(container.querySelector(".detail-activity")).toBeNull();
|
expect(container.querySelector(".detail-activity")).toBeNull();
|
||||||
|
|
||||||
@@ -681,7 +680,7 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container.querySelector(".markdown-body")).toBeNull();
|
expect(container.querySelector(".markdown-body")).toBeNull();
|
||||||
|
|
||||||
// Click Definition tab to go back
|
// Click Definition tab to go back
|
||||||
fireEvent.click(screen.getByText("Definition"));
|
fireEvent.click(screen.getByText("Plan"));
|
||||||
|
|
||||||
// Definition content should reappear
|
// Definition content should reappear
|
||||||
expect(container.querySelector(".markdown-body")).toBeTruthy();
|
expect(container.querySelector(".markdown-body")).toBeTruthy();
|
||||||
@@ -705,7 +704,7 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Default: Chat tab active → enabled should be false
|
// Default: Activity tab active → enabled should be false
|
||||||
const initialCall = mockUseAgentLogs.mock.calls[mockUseAgentLogs.mock.calls.length - 1];
|
const initialCall = mockUseAgentLogs.mock.calls[mockUseAgentLogs.mock.calls.length - 1];
|
||||||
expect(initialCall[1]).toBe(false);
|
expect(initialCall[1]).toBe(false);
|
||||||
|
|
||||||
@@ -758,18 +757,17 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// For an in-progress task (no workflow steps, no merge commit), the
|
// For an in-progress task (no workflow steps, no merge commit), the
|
||||||
// top-level tabs are: Chat, Definition, Logs, Changes, Review, Comments,
|
// top-level tabs are: Activity, Plan, Logs, Changes, Review, Comments,
|
||||||
// Artifacts, Model, Workflow, Stats, Routing.
|
// Artifacts, Model, Workflow, Stats, Routing.
|
||||||
const tabTexts = ["Chat", "Definition", "Logs", "Changes", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing"];
|
const tabTexts = ["Activity", "Plan", "Logs", "Changes", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing"];
|
||||||
const tabs = screen.getAllByRole("button").filter((b) =>
|
const tabs = screen.getAllByRole("button").filter((b) =>
|
||||||
tabTexts.includes(b.textContent || "")
|
tabTexts.includes(b.textContent || "")
|
||||||
);
|
);
|
||||||
expect(tabs.map((tab) => tab.textContent)).toEqual(tabTexts);
|
expect(tabs.map((tab) => tab.textContent)).toEqual(tabTexts);
|
||||||
expect(tabs[0].textContent).toBe("Chat");
|
expect(tabs[0].textContent).toBe("Activity");
|
||||||
expect(tabs[1].textContent).toBe("Definition");
|
expect(tabs[1].textContent).toBe("Plan");
|
||||||
expect(tabs[2].textContent).toBe("Logs");
|
expect(tabs[2].textContent).toBe("Logs");
|
||||||
|
|
||||||
// Activity and Agent Log are NOT top-level tabs (they are subviews inside Logs)
|
|
||||||
expect(container.querySelectorAll(".detail-tab").length).toBe(11);
|
expect(container.querySelectorAll(".detail-tab").length).toBe(11);
|
||||||
// Workflow tab should always appear even when no workflow steps are configured
|
// Workflow tab should always appear even when no workflow steps are configured
|
||||||
expect(screen.getByText("Workflow")).toBeInTheDocument();
|
expect(screen.getByText("Workflow")).toBeInTheDocument();
|
||||||
@@ -841,7 +839,7 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Chat" }));
|
fireEvent.click(screen.getByRole("button", { name: "Activity" }));
|
||||||
const content = container.querySelector(".task-detail-content");
|
const content = container.querySelector(".task-detail-content");
|
||||||
const titleRow = container.querySelector(".detail-title-row");
|
const titleRow = container.querySelector(".detail-title-row");
|
||||||
expect(content).not.toHaveClass("task-detail-content--chat-expanded");
|
expect(content).not.toHaveClass("task-detail-content--chat-expanded");
|
||||||
@@ -942,7 +940,7 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Chat" }));
|
fireEvent.click(screen.getByRole("button", { name: "Activity" }));
|
||||||
fireEvent.click(screen.getByTestId("task-chat-expand-toggle"));
|
fireEvent.click(screen.getByTestId("task-chat-expand-toggle"));
|
||||||
expect(container.querySelector(".task-detail-content")).toHaveClass("task-detail-content--chat-expanded");
|
expect(container.querySelector(".task-detail-content")).toHaveClass("task-detail-content--chat-expanded");
|
||||||
|
|
||||||
@@ -951,7 +949,7 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(screen.queryByTestId("task-chat-expand-toggle")).toBeNull();
|
expect(screen.queryByTestId("task-chat-expand-toggle")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FN-6532 defaults to Chat first while preserving explicit tab requests", () => {
|
it("FN-6532 defaults to Activity first while preserving explicit tab requests", () => {
|
||||||
const { container, rerender } = render(
|
const { container, rerender } = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
||||||
@@ -965,10 +963,10 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tabs = Array.from(container.querySelectorAll<HTMLButtonElement>(".detail-tab"));
|
const tabs = Array.from(container.querySelectorAll<HTMLButtonElement>(".detail-tab"));
|
||||||
expect(tabs.map((tab) => tab.textContent)).toEqual(expect.arrayContaining(["Chat", "Definition"]));
|
expect(tabs.map((tab) => tab.textContent)).toEqual(expect.arrayContaining(["Activity", "Plan"]));
|
||||||
expect(tabs[0]).toHaveTextContent("Chat");
|
expect(tabs[0]).toHaveTextContent("Activity");
|
||||||
const chatTab = screen.getByRole("button", { name: "Chat" });
|
const chatTab = screen.getByRole("button", { name: "Activity" });
|
||||||
const definitionTab = screen.getByRole("button", { name: "Definition" });
|
const definitionTab = screen.getByRole("button", { name: "Plan" });
|
||||||
expect(chatTab.compareDocumentPosition(definitionTab) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
expect(chatTab.compareDocumentPosition(definitionTab) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||||
expect(chatTab).toHaveClass("detail-tab-active");
|
expect(chatTab).toHaveClass("detail-tab-active");
|
||||||
expect(definitionTab).not.toHaveClass("detail-tab-active");
|
expect(definitionTab).not.toHaveClass("detail-tab-active");
|
||||||
@@ -988,7 +986,8 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByRole("button", { name: "Logs" })).toHaveClass("detail-tab-active");
|
expect(screen.getByRole("button", { name: "Logs" })).toHaveClass("detail-tab-active");
|
||||||
expect(screen.getByRole("button", { name: "Chat" })).not.toHaveClass("detail-tab-active");
|
expect(container.querySelector(".detail-tabs .detail-tab:first-child")).toHaveTextContent("Activity");
|
||||||
|
expect(container.querySelector(".detail-tabs .detail-tab:first-child")).not.toHaveClass("detail-tab-active");
|
||||||
expect(container.querySelector(".detail-section--chat")).toBeNull();
|
expect(container.querySelector(".detail-section--chat")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1010,8 +1009,8 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByRole("button", { name: "Definition" })).toHaveClass("detail-tab-active");
|
expect(screen.getByRole("button", { name: "Plan" })).toHaveClass("detail-tab-active");
|
||||||
expect(screen.getByRole("button", { name: "Chat" })).not.toHaveClass("detail-tab-active");
|
expect(screen.getByRole("button", { name: "Activity" })).not.toHaveClass("detail-tab-active");
|
||||||
expect(container.querySelector(".detail-section--chat")).toBeNull();
|
expect(container.querySelector(".detail-section--chat")).toBeNull();
|
||||||
expect(screen.getByText("Definition body unique text.")).toBeInTheDocument();
|
expect(screen.getByText("Definition body unique text.")).toBeInTheDocument();
|
||||||
expect(screen.getByText("GitHub tracking")).toBeInTheDocument();
|
expect(screen.getByText("GitHub tracking")).toBeInTheDocument();
|
||||||
@@ -1021,7 +1020,7 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container).toHaveTextContent("FN-200");
|
expect(container).toHaveTextContent("FN-200");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FN-6347 applies chat modifiers only while the Chat tab is active", () => {
|
it("FN-6347 applies chat modifiers only while the Activity tab is active", () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
task={makeTask({ prompt: "# Hello\n\nContent" })}
|
||||||
@@ -1061,7 +1060,7 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Chat" }));
|
fireEvent.click(screen.getByRole("button", { name: "Activity" }));
|
||||||
expect(container.querySelector(".detail-body--chat")).toBeTruthy();
|
expect(container.querySelector(".detail-body--chat")).toBeTruthy();
|
||||||
|
|
||||||
fireEvent.click(screen.getByLabelText("Edit task"));
|
fireEvent.click(screen.getByLabelText("Edit task"));
|
||||||
@@ -1097,7 +1096,7 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container.querySelector(".detail-body--agent-log")).toBeTruthy();
|
expect(container.querySelector(".detail-body--agent-log")).toBeTruthy();
|
||||||
|
|
||||||
// Switch back to Definition tab
|
// Switch back to Definition tab
|
||||||
fireEvent.click(screen.getByText("Definition"));
|
fireEvent.click(screen.getByText("Plan"));
|
||||||
|
|
||||||
// modifier class should be removed
|
// modifier class should be removed
|
||||||
expect(container.querySelector(".detail-body--agent-log")).toBeNull();
|
expect(container.querySelector(".detail-body--agent-log")).toBeNull();
|
||||||
|
|||||||
@@ -222,10 +222,10 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// In-progress tasks show exactly 11 tabs:
|
// In-progress tasks show exactly 11 tabs:
|
||||||
// Chat, Plan, Logs, Changes, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
// Activity, Plan, Logs, Changes, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
||||||
const tabs = container.querySelectorAll(".detail-tab");
|
const tabs = container.querySelectorAll(".detail-tab");
|
||||||
expect(tabs.length).toBe(11);
|
expect(tabs.length).toBe(11);
|
||||||
expect(tabs[0].textContent).toBe("Chat");
|
expect(tabs[0].textContent).toBe("Activity");
|
||||||
expect(tabs[1].textContent).toBe("Plan");
|
expect(tabs[1].textContent).toBe("Plan");
|
||||||
expect(tabs[2].textContent).toBe("Logs");
|
expect(tabs[2].textContent).toBe("Logs");
|
||||||
expect(tabs[3].textContent).toBe("Changes");
|
expect(tabs[3].textContent).toBe("Changes");
|
||||||
@@ -257,7 +257,7 @@ describe("TaskDetailModal", () => {
|
|||||||
// In-progress task with workflow steps: 11 tabs (Review after Changes, Workflow after Model)
|
// In-progress task with workflow steps: 11 tabs (Review after Changes, Workflow after Model)
|
||||||
const tabs = container.querySelectorAll(".detail-tab");
|
const tabs = container.querySelectorAll(".detail-tab");
|
||||||
expect(tabs.length).toBe(11);
|
expect(tabs.length).toBe(11);
|
||||||
expect(tabs[0].textContent).toBe("Chat");
|
expect(tabs[0].textContent).toBe("Activity");
|
||||||
expect(tabs[1].textContent).toBe("Plan");
|
expect(tabs[1].textContent).toBe("Plan");
|
||||||
expect(tabs[2].textContent).toBe("Logs");
|
expect(tabs[2].textContent).toBe("Logs");
|
||||||
expect(tabs[3].textContent).toBe("Changes");
|
expect(tabs[3].textContent).toBe("Changes");
|
||||||
@@ -287,11 +287,11 @@ describe("TaskDetailModal", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Done task with commit SHA: Summary, Chat, Plan, Logs, Changes, Review, Comments, Artifacts, Model, Workflow, Stats, Routing (12 tabs, no Commits)
|
// Done task with commit SHA: Activity, Summary, Plan, Logs, Changes, Review, Comments, Artifacts, Model, Workflow, Stats, Routing (12 tabs, no Commits)
|
||||||
const tabs = container.querySelectorAll(".detail-tab");
|
const tabs = container.querySelectorAll(".detail-tab");
|
||||||
expect(tabs.length).toBe(12);
|
expect(tabs.length).toBe(12);
|
||||||
expect(tabs[0].textContent).toBe("Summary");
|
expect(tabs[0].textContent).toBe("Activity");
|
||||||
expect(tabs[1].textContent).toBe("Chat");
|
expect(tabs[1].textContent).toBe("Summary");
|
||||||
expect(tabs[2].textContent).toBe("Plan");
|
expect(tabs[2].textContent).toBe("Plan");
|
||||||
expect(tabs[3].textContent).toBe("Logs");
|
expect(tabs[3].textContent).toBe("Logs");
|
||||||
expect(tabs[4].textContent).toBe("Changes");
|
expect(tabs[4].textContent).toBe("Changes");
|
||||||
@@ -327,8 +327,8 @@ describe("TaskDetailModal", () => {
|
|||||||
// Done task with workflow steps and commit SHA: 12 tabs including Summary and Review (no Commits)
|
// Done task with workflow steps and commit SHA: 12 tabs including Summary and Review (no Commits)
|
||||||
const tabs = container.querySelectorAll(".detail-tab");
|
const tabs = container.querySelectorAll(".detail-tab");
|
||||||
expect(tabs.length).toBe(12);
|
expect(tabs.length).toBe(12);
|
||||||
expect(tabs[0].textContent).toBe("Summary");
|
expect(tabs[0].textContent).toBe("Activity");
|
||||||
expect(tabs[1].textContent).toBe("Chat");
|
expect(tabs[1].textContent).toBe("Summary");
|
||||||
expect(tabs[2].textContent).toBe("Plan");
|
expect(tabs[2].textContent).toBe("Plan");
|
||||||
expect(tabs[3].textContent).toBe("Logs");
|
expect(tabs[3].textContent).toBe("Logs");
|
||||||
expect(tabs[4].textContent).toBe("Changes");
|
expect(tabs[4].textContent).toBe("Changes");
|
||||||
@@ -358,9 +358,9 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const triageTabs = triageContainer.querySelectorAll(".detail-tab");
|
const triageTabs = triageContainer.querySelectorAll(".detail-tab");
|
||||||
expect(triageTabs.length).toBe(10); // Chat, Plan, Logs, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
expect(triageTabs.length).toBe(10); // Activity, Plan, Logs, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
||||||
expect(Array.from(triageTabs).map(t => t.textContent)).toEqual([
|
expect(Array.from(triageTabs).map(t => t.textContent)).toEqual([
|
||||||
"Chat", "Plan", "Logs", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing",
|
"Activity", "Plan", "Logs", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { container: todoContainer } = render(
|
const { container: todoContainer } = render(
|
||||||
@@ -377,9 +377,9 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const todoTabs = todoContainer.querySelectorAll(".detail-tab");
|
const todoTabs = todoContainer.querySelectorAll(".detail-tab");
|
||||||
expect(todoTabs.length).toBe(10); // Chat, Plan, Logs, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
expect(todoTabs.length).toBe(10); // Activity, Plan, Logs, Review, Comments, Artifacts, Model, Workflow, Stats, Routing
|
||||||
expect(Array.from(todoTabs).map(t => t.textContent)).toEqual([
|
expect(Array.from(todoTabs).map(t => t.textContent)).toEqual([
|
||||||
"Chat", "Plan", "Logs", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing",
|
"Activity", "Plan", "Logs", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing",
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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 { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, waitFor } from "@testing-library/react";
|
import { render, screen, waitFor } from "@testing-library/react";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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 { describe, it, expect, vi } from "vitest";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -2259,7 +2259,7 @@ describe("TaskDetailModal", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Only standard tabs should be visible (Definition, Logs, etc.)
|
// Only standard tabs should be visible (Definition, Logs, etc.)
|
||||||
expect(screen.getByText("Definition")).toBeDefined();
|
expect(screen.getByText("Plan")).toBeDefined();
|
||||||
expect(screen.getByText("Logs")).toBeDefined();
|
expect(screen.getByText("Logs")).toBeDefined();
|
||||||
// Plugin tabs should not exist
|
// Plugin tabs should not exist
|
||||||
expect(screen.queryByText("Plugin A Tab")).toBeNull();
|
expect(screen.queryByText("Plugin A Tab")).toBeNull();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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 { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||||
@@ -886,7 +886,7 @@ describe("TaskDetailModal", () => {
|
|||||||
|
|
||||||
expect(container.querySelector(".task-detail-content--embedded")).toBeTruthy();
|
expect(container.querySelector(".task-detail-content--embedded")).toBeTruthy();
|
||||||
expect(screen.queryByRole("button", { name: "Close" })).toBeNull();
|
expect(screen.queryByRole("button", { name: "Close" })).toBeNull();
|
||||||
expect(screen.getByRole("button", { name: "Definition" })).toBeInTheDocument();
|
expect(screen.getByRole("button", { name: "Plan" })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders header close control for embedded floating task details", () => {
|
it("renders header close control for embedded floating task details", () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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 { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||||
@@ -225,8 +225,8 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(container.querySelectorAll(".detail-timestamp-item").length).toBe(2);
|
expect(container.querySelectorAll(".detail-timestamp-item").length).toBe(2);
|
||||||
const tabs = container.querySelectorAll(".detail-tab");
|
const tabs = container.querySelectorAll(".detail-tab");
|
||||||
expect(Array.from(tabs).map((tab) => tab.textContent?.trim())).toEqual([
|
expect(Array.from(tabs).map((tab) => tab.textContent?.trim())).toEqual([
|
||||||
"Chat",
|
"Activity",
|
||||||
"Definition",
|
"Plan",
|
||||||
"Logs",
|
"Logs",
|
||||||
"Changes",
|
"Changes",
|
||||||
"Review",
|
"Review",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { render, screen, within } from "@testing-library/react";
|
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||||
import type { Column, ModelPricingOverrides, TaskTokenUsage } from "@fusion/core";
|
import type { Column, ModelPricingOverrides, TaskTokenUsage } from "@fusion/core";
|
||||||
import {
|
import {
|
||||||
makeTask,
|
makeTask,
|
||||||
@@ -76,7 +76,7 @@ function doneTask(overrides = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("TaskDetailModal Summary tab", () => {
|
describe("TaskDetailModal Summary tab", () => {
|
||||||
it("lands done tasks on Summary and renders completion, changed-files, and agent-work sections", () => {
|
it("lands done tasks on Activity first and keeps Summary accessible", () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
task={doneTask()}
|
task={doneTask()}
|
||||||
@@ -89,7 +89,15 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(container.querySelector(".detail-tabs")?.firstElementChild?.textContent).toBe("Activity");
|
||||||
|
const activityButton = screen.getByRole("button", { name: "Activity" });
|
||||||
|
expectButtonActive(activityButton);
|
||||||
|
expect(screen.queryByRole("button", { name: "Chat" })).toBeNull();
|
||||||
|
expect(screen.queryByText("Completion summary")).toBeNull();
|
||||||
|
expect(container.querySelector(".detail-section--chat [data-testid='task-chat-tab']")).toBeTruthy();
|
||||||
|
|
||||||
const summaryButton = screen.getByRole("button", { name: "Summary" });
|
const summaryButton = screen.getByRole("button", { name: "Summary" });
|
||||||
|
fireEvent.click(summaryButton);
|
||||||
expectButtonActive(summaryButton);
|
expectButtonActive(summaryButton);
|
||||||
expect(screen.getByText("Completion summary")).toBeTruthy();
|
expect(screen.getByText("Completion summary")).toBeTruthy();
|
||||||
expect(screen.getByText("summary")).toBeTruthy();
|
expect(screen.getByText("summary")).toBeTruthy();
|
||||||
@@ -99,10 +107,9 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
expect(screen.getByText("Preflight")).toBeTruthy();
|
expect(screen.getByText("Preflight")).toBeTruthy();
|
||||||
expect(screen.getByText("Code Review")).toBeTruthy();
|
expect(screen.getByText("Code Review")).toBeTruthy();
|
||||||
expect(screen.getByText("Agents retried this task 1 time.")).toBeTruthy();
|
expect(screen.getByText("Agents retried this task 1 time.")).toBeTruthy();
|
||||||
expect(container.querySelector(".detail-tabs")?.firstElementChild?.textContent).toBe("Summary");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("honors explicit Chat for done tasks", () => {
|
it("honors explicit initialTab=\"chat\" for done tasks", () => {
|
||||||
render(
|
render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
task={doneTask()}
|
task={doneTask()}
|
||||||
@@ -116,7 +123,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expectButtonActive(screen.getByRole("button", { name: "Chat" }));
|
expectButtonActive(screen.getByRole("button", { name: "Activity" }));
|
||||||
expect(screen.queryByText("Completion summary")).toBeNull();
|
expect(screen.queryByText("Completion summary")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -153,7 +160,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
expect(screen.queryByText("Completion summary")).toBeNull();
|
expect(screen.queryByText("Completion summary")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render Summary for non-done columns and still defaults to Chat", () => {
|
it("does not render Summary for non-done columns and still defaults to Activity", () => {
|
||||||
for (const column of ["in-progress", "in-review", "todo"] as Column[]) {
|
for (const column of ["in-progress", "in-review", "todo"] as Column[]) {
|
||||||
const rendered = render(
|
const rendered = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
@@ -167,7 +174,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(screen.queryByRole("button", { name: "Summary" })).toBeNull();
|
expect(screen.queryByRole("button", { name: "Summary" })).toBeNull();
|
||||||
expectButtonActive(screen.getByRole("button", { name: "Chat" }));
|
expectButtonActive(screen.getByRole("button", { name: "Activity" }));
|
||||||
rendered.unmount();
|
rendered.unmount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -359,6 +366,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
const full = render(
|
const full = render(
|
||||||
<TaskDetailContent
|
<TaskDetailContent
|
||||||
task={task}
|
task={task}
|
||||||
|
initialTab="summary"
|
||||||
onMoveTask={noopMove}
|
onMoveTask={noopMove}
|
||||||
onDeleteTask={noopDelete}
|
onDeleteTask={noopDelete}
|
||||||
onMergeTask={noopMerge}
|
onMergeTask={noopMerge}
|
||||||
@@ -374,6 +382,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
<TaskDetailContent
|
<TaskDetailContent
|
||||||
task={task}
|
task={task}
|
||||||
embedded
|
embedded
|
||||||
|
initialTab="summary"
|
||||||
onMoveTask={noopMove}
|
onMoveTask={noopMove}
|
||||||
onDeleteTask={noopDelete}
|
onDeleteTask={noopDelete}
|
||||||
onMergeTask={noopMerge}
|
onMergeTask={noopMerge}
|
||||||
@@ -417,6 +426,7 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
retrySummary: { total: 0 },
|
retrySummary: { total: 0 },
|
||||||
})}
|
})}
|
||||||
onClose={noop}
|
onClose={noop}
|
||||||
|
initialTab="summary"
|
||||||
onMoveTask={noopMove}
|
onMoveTask={noopMove}
|
||||||
onDeleteTask={noopDelete}
|
onDeleteTask={noopDelete}
|
||||||
onMergeTask={noopMerge}
|
onMergeTask={noopMerge}
|
||||||
@@ -451,8 +461,8 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
expect(summaryButton.classList.contains("detail-tab")).toBe(true);
|
expect(summaryButton.classList.contains("detail-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resolves the done-task Summary default in embedded TaskDetailContent", () => {
|
it("resolves the done-task Activity default in embedded TaskDetailContent", () => {
|
||||||
render(
|
const { container } = render(
|
||||||
<TaskDetailContent
|
<TaskDetailContent
|
||||||
task={doneTask()}
|
task={doneTask()}
|
||||||
embedded
|
embedded
|
||||||
@@ -464,7 +474,8 @@ describe("TaskDetailModal Summary tab", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expectButtonActive(screen.getByRole("button", { name: "Summary" }));
|
expectButtonActive(screen.getByRole("button", { name: "Activity" }));
|
||||||
expect(screen.getByText("Completion summary")).toBeTruthy();
|
expect(container.querySelector(".detail-tabs")?.firstElementChild?.textContent).toBe("Activity");
|
||||||
|
expect(screen.queryByText("Completion summary")).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
FNXC:TaskDetailTabs 2026-06-17-08:20:
|
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.
|
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 { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, waitFor } from "@testing-library/react";
|
import { render, screen, waitFor } from "@testing-library/react";
|
||||||
@@ -689,7 +689,7 @@ describe("TaskDetailModal in-review stall diagnostics", () => {
|
|||||||
|
|
||||||
await user.click(screen.getByRole("button", { name: "View activity log" }));
|
await user.click(screen.getByRole("button", { name: "View activity log" }));
|
||||||
expect(screen.getByRole("button", { name: "Logs" })).toHaveClass("detail-tab-active");
|
expect(screen.getByRole("button", { name: "Logs" })).toHaveClass("detail-tab-active");
|
||||||
expect(screen.getByRole("button", { name: "Activity" })).toHaveClass("log-subview-btn-active");
|
expect(document.querySelector(".log-subview-toggle .log-subview-btn-active")).toHaveTextContent("Activity");
|
||||||
const highlighted = document.querySelector(".detail-log-entry--stall-highlight .detail-log-action");
|
const highlighted = document.querySelector(".detail-log-entry--stall-highlight .detail-log-action");
|
||||||
expect(highlighted?.textContent).toContain("In-review stall surfaced [merge-blocker]");
|
expect(highlighted?.textContent).toContain("In-review stall surfaced [merge-blocker]");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import type { SectionId } from "../components/SettingsModal";
|
|||||||
import type { ToastType } from "./useToast";
|
import type { ToastType } from "./useToast";
|
||||||
import { removeScopedItem } from "../utils/projectStorage";
|
import { removeScopedItem } from "../utils/projectStorage";
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
|
Keep `chat` as the public initial-tab id for the renamed Activity task-detail tab so existing dashboard callers and deep links remain compatible until the future planner Chat tab ships under its own contract.
|
||||||
|
*/
|
||||||
export type DetailTaskTab =
|
export type DetailTaskTab =
|
||||||
| "summary"
|
| "summary"
|
||||||
| "chat"
|
| "chat"
|
||||||
|
|||||||
@@ -11,7 +11,12 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import type { Task, TaskDetail, WorkflowStep } from "@fusion/core";
|
import type { Task, TaskDetail, WorkflowStep } from "@fusion/core";
|
||||||
|
|
||||||
/** Tab identifiers for the task detail modal. Mirrors the dashboard's local enum. */
|
/**
|
||||||
|
* Tab identifiers for the task detail modal. Mirrors the dashboard's local enum.
|
||||||
|
*
|
||||||
|
* FNXC:TaskDetailActivityTab 2026-06-30-00:00:
|
||||||
|
* Plugins should continue passing `chat` to open the renamed Activity tab; the id is stable compatibility surface, while the top-level label is no longer Chat.
|
||||||
|
*/
|
||||||
export type DetailTaskTab = "summary" | "chat" | "definition" | "logs" | "changes" | "comments" | "model" | "workflow" | "pr" | "retries";
|
export type DetailTaskTab = "summary" | "chat" | "definition" | "logs" | "changes" | "comments" | "model" | "workflow" | "pr" | "retries";
|
||||||
|
|
||||||
export type PluginToastType = "success" | "error" | "warning" | "info";
|
export type PluginToastType = "success" | "error" | "warning" | "info";
|
||||||
|
|||||||
Reference in New Issue
Block a user