fix(dashboard): index the FN-8348 Report row in settings search; re-green backfill test lane

Product fixes surfaced by the drift guards:
- GeneralSection.search.ts: FN-8348 added the Report row (bug/feedback/idea/help)
  without a search-index entry, so settings search could not find it.
- WorkflowNodeEditor.css: FN-8526 introduced the undefined deprecated alias
  --text-secondary; substitute the FN-8043 migration token --text-muted.

Stale-test alignment (no assertions weakened):
- settings-sections: builtin:coding-ideas is no longer deprecated post-#2378.
- TaskDetailModal workflow/oversight suites: mocked workflows gain the columns
  array FN-8476 metadata resolution requires; rerender-refetch tests use
  persistent mocks since the board-workflows effect now keys on task identity.
- ChatView.chat-commands: sendMessage gained the FN-8502 delivery-callback arg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-23 22:13:25 -07:00
parent 1d4cd27c73
commit 0a6f84bbdf
7 changed files with 70 additions and 15 deletions

View File

@@ -143,7 +143,13 @@ describe("GeneralSection", () => {
await waitFor(() => expect(screen.getByLabelText("Coding")).toBeInTheDocument());
expect(screen.queryByLabelText("Brainstorming")).not.toBeInTheDocument();
expect(screen.queryByLabelText("Coding (Ideas)")).not.toBeInTheDocument();
/*
FNXC:SettingsWorkflowToggles 2026-07-23-22:05:
PR #2378 restored builtin:coding-ideas (removed from DEPRECATED_BUILTIN_WORKFLOW_IDS),
so Coding (Ideas) is a live selectable built-in again and must render a toggle.
Brainstorming remains the deprecated built-in that must stay hidden.
*/
expect(screen.getByLabelText("Coding (Ideas)")).toBeInTheDocument();
});
it("emits the absolute file-browser path toggle via setForm", () => {

View File

@@ -2072,10 +2072,18 @@ Fusion tokens so light/dark themes never show browser-default white controls.
}
.wf-column-description-field {
/*
FNXC:DashboardTextTokens 2026-07-23-22:30:
FN-8526 shipped this label with the undefined deprecated --text-secondary alias; per the FN-8043
migration guarded by deprecated-text-secondary-token-migration.test.ts, secondary text uses the
canonical --text-muted token, which resolves in both base and light themes.
(Comment lives inside the block so WorkflowColumnPanel.test.tsx's brace-scanning selector parser
still sees the bare selector.)
*/
display: flex;
flex-direction: column;
gap: var(--space-xs);
color: var(--text-secondary);
color: var(--text-muted);
font-size: 0.75rem;
}

View File

@@ -164,7 +164,17 @@ describe("ChatView slash-command dispatch (/steer)", () => {
fireEvent.change(textarea, { target: { value: "hello there" } });
fireEvent.keyDown(textarea, { key: "Enter" });
await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("hello there", []));
/*
FNXC:ChatAttachments 2026-07-23-22:35:
FN-8502 made ChatView pass attachment delivery callbacks as a third
sendMessage argument ({ onDelivered, onFailed }); assert them structurally
so the /steer dispatch contract (text + empty attachments, no steering
comment) stays the protected invariant.
*/
await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("hello there", [], expect.objectContaining({
onDelivered: expect.any(Function),
onFailed: expect.any(Function),
})));
expect(mockAddSteeringComment).not.toHaveBeenCalled();
});
@@ -181,7 +191,11 @@ describe("ChatView slash-command dispatch (/steer)", () => {
fireEvent.change(textarea, { target: { value: "please /steer this" } });
fireEvent.keyDown(textarea, { key: "Enter" });
await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("please /steer this", []));
// FNXC:ChatAttachments 2026-07-23-22:35: FN-8502 delivery-callback third arg (see above).
await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("please /steer this", [], expect.objectContaining({
onDelivered: expect.any(Function),
onFailed: expect.any(Function),
})));
expect(mockAddSteeringComment).not.toHaveBeenCalled();
});

View File

@@ -1065,7 +1065,8 @@ describe("TaskDetailModal", () => {
vi.mocked(fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "wf-edit",
workflows: [{ id: "wf-edit", name: "Edit Workflow" }],
// FNXC:TaskDetailWorkflow 2026-07-23-22:10: FN-8476 (restore Ideas detail move action) made resolveTaskWorkflowMetadata derive move targets from workflow.columns, so mocked workflows must carry a columns array or metadata resolution throws and the edit-mode optional-steps picker never mounts.
workflows: [{ id: "wf-edit", name: "Edit Workflow", columns: [] }],
taskWorkflowIds: { "FN-099": "wf-edit" },
} as any);
vi.mocked(fetchWorkflowOptionalSteps).mockResolvedValueOnce([
@@ -1108,7 +1109,8 @@ describe("TaskDetailModal", () => {
vi.mocked(fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "wf-edit",
workflows: [{ id: "wf-edit", name: "Edit Workflow" }],
// FNXC:TaskDetailWorkflow 2026-07-23-22:10: FN-8476 (restore Ideas detail move action) made resolveTaskWorkflowMetadata derive move targets from workflow.columns, so mocked workflows must carry a columns array or metadata resolution throws and the edit-mode optional-steps picker never mounts.
workflows: [{ id: "wf-edit", name: "Edit Workflow", columns: [] }],
taskWorkflowIds: { "FN-099": "wf-edit" },
} as any);
vi.mocked(fetchWorkflowOptionalSteps).mockResolvedValueOnce([

View File

@@ -92,7 +92,7 @@ describe("TaskDetailModal oversight controls", () => {
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "WF-advisor",
workflows: [{ id: "WF-advisor", name: "Advisor workflow" } as any],
workflows: [{ id: "WF-advisor", name: "Advisor workflow", columns: [] } as any],
taskWorkflowIds: { "FN-8247-workflow": "WF-advisor" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({
@@ -134,7 +134,7 @@ describe("TaskDetailModal oversight controls", () => {
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "WF-8263-project-default",
workflows: [{ id: "WF-8263-project-default", name: "Project default workflow" } as any],
workflows: [{ id: "WF-8263-project-default", name: "Project default workflow", columns: [] } as any],
taskWorkflowIds: { "FN-8263-project-default": "WF-8263-project-default" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({
@@ -177,10 +177,18 @@ describe("TaskDetailModal oversight controls", () => {
defaultPresetBySize: {},
sessionAdvisorEnabledByDefault: true,
} as any);
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
/*
FNXC:PlannerOversight 2026-07-23-22:20:
FN-8476 made the board-workflows lookup re-run whenever the task prop identity
changes (it derives move metadata from the payload), so the onTaskUpdated
rerender below refetches. A once-mock would leave the refetch on the
flagEnabled:false default and silently drop the workflow tier mid-test —
keep the payload persistent for every call in this test.
*/
vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({
flagEnabled: true,
defaultWorkflowId: "WF-8263-pending-advisor",
workflows: [{ id: "WF-8263-pending-advisor", name: "Pending advisor workflow" } as any],
workflows: [{ id: "WF-8263-pending-advisor", name: "Pending advisor workflow", columns: [] } as any],
taskWorkflowIds: { [currentTask.id]: "WF-8263-pending-advisor" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockImplementationOnce(() => new Promise(() => {}));
@@ -238,10 +246,12 @@ describe("TaskDetailModal oversight controls", () => {
defaultPresetBySize: {},
sessionAdvisorEnabledByDefault: false,
} as any);
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
// FNXC:PlannerOversight 2026-07-23-22:20: persistent mock — FN-8476 refetches
// board workflows on each task-prop identity change (see pending-advisor test).
vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({
flagEnabled: true,
defaultWorkflowId: "WF-advisor-explicit-off",
workflows: [{ id: "WF-advisor-explicit-off", name: "Advisor workflow" } as any],
workflows: [{ id: "WF-advisor-explicit-off", name: "Advisor workflow", columns: [] } as any],
taskWorkflowIds: { [currentTask.id]: "WF-advisor-explicit-off" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({

View File

@@ -83,7 +83,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => {
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "WF-8263-mobile-project-default",
workflows: [{ id: "WF-8263-mobile-project-default", name: "Mobile project default workflow" } as any],
workflows: [{ id: "WF-8263-mobile-project-default", name: "Mobile project default workflow", columns: [] } as any],
taskWorkflowIds: { "FN-8263-mobile-project-default": "WF-8263-mobile-project-default" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({
@@ -129,7 +129,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => {
vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({
flagEnabled: true,
defaultWorkflowId: "WF-advisor-mobile",
workflows: [{ id: "WF-advisor-mobile", name: "Advisor workflow" } as any],
workflows: [{ id: "WF-advisor-mobile", name: "Advisor workflow", columns: [] } as any],
taskWorkflowIds: { [currentTask.id]: "WF-advisor-mobile" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({
@@ -235,7 +235,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => {
vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({
flagEnabled: true,
defaultWorkflowId: "WF-mobile-test",
workflows: [{ id: "WF-mobile-test", name: "Mobile Test Workflow" } as any],
workflows: [{ id: "WF-mobile-test", name: "Mobile Test Workflow", columns: [] } as any],
taskWorkflowIds: { "FN-212": "WF-mobile-test" },
});
vi.mocked(api.fetchWorkflowSettingValues).mockImplementation(() => new Promise(() => {}));

View File

@@ -8,6 +8,21 @@
import type { SettingsSearchEntry } from "../search/types";
export const generalSearchEntries: SettingsSearchEntry[] = [
{
/*
FNXC:SettingsSearch 2026-07-23-22:20:
FN-8348 added the Report row (bug/feedback/idea/help entry point) to General
without indexing it, so settings search could not find it; the search-index
drift guard caught the gap. Labels/help mirror the section's t() copy verbatim.
*/
sectionId: "general",
key: "report-action-menu",
labelKey: "settings.general.report",
labelFallback: "Report",
helpKey: "settings.general.reportHelp",
helpFallback: "Report a bug, send feedback, share an idea, or get help from Fusion.",
keywords: ["bug", "feedback", "idea", "help", "support"],
},
{
sectionId: "general",
key: "taskPrefix",