From b2d4813410a9e8a5b118cc7bc22764d1da9115c6 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 01:17:07 -0700 Subject: [PATCH] test(dashboard): cover the dock's renderTaskCard, the second producer #3025 fixed (#3027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Stacked on #3025** (its commit is the parent here) — that PR fixes two producers of a dock/plugin-rendered `TaskCard`, and this adds the coverage for the second one. ## The gap #3025 correctly fixes **both** producers, which is the Surface Enumeration discipline working. Its test covers only `MainContent`. Measured by deleting the identical line from `useRightDockController`: ``` MainContent.graph-popout 6 passed RightDock 33 passed TaskCard.host-inventory 1 passed ``` All green with the dock's wiring gone. I checked every suite that touches that hook; none observes the prop. Its own test comment says: > REVERT CHECK: drop `taskColumnFlags` from **either** `renderTaskCard` and this reads "none". For this producer that is not true, and it is the producer that draws cards into the **right dock**, where an operator actually sees them. So the pair could quietly become a single again with every test still green. ## The test | state | result | |---|---| | #3025 as merged | 2/2 pass | | delete the dock's `taskColumnFlags={…}` line | **1 failed / 1 passed** | Driven through the real `renderTaskCard`, captured off the `renderProps` the controller hands `RightDock` — it is not on the returned controller object. `RightDock` itself is stubbed so the assertion cannot fail for unrelated dock plumbing. The paired negative asserts an unresolved task receives `undefined` rather than a fabricated object. That direction matters: inventing flags would make a card claim traits its board never declared, which is worse than the legacy fallback it replaces — the same *report, don't guess* reasoning as #2999's `!target` refusal. ## One harness note My first mock replaced `../RightDock` wholesale and the hook died on `No "readStoredRightDockOpen" export is defined on the mock` — the controller imports its persistence helpers from that module. Spreading `importOriginal()` and overriding only the two components fixes it. Recorded in the file because the next person stubbing this module will hit the same thing. **Verified:** 2/2, `tsc -p tsconfig.app.json` 0 errors in the new file, lint clean, FNXC gate exit 0. If #3025 lands first this rebases to a single test commit; if the two are taken together the stack applies as-is. --------- Co-authored-by: Claude Opus 5 (1M context) --- ...tDockController.card-column-flags.test.tsx | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 packages/dashboard/app/components/__tests__/useRightDockController.card-column-flags.test.tsx diff --git a/packages/dashboard/app/components/__tests__/useRightDockController.card-column-flags.test.tsx b/packages/dashboard/app/components/__tests__/useRightDockController.card-column-flags.test.tsx new file mode 100644 index 0000000000..4434e76033 --- /dev/null +++ b/packages/dashboard/app/components/__tests__/useRightDockController.card-column-flags.test.tsx @@ -0,0 +1,125 @@ +/* +FNXC:WorkflowResolvedColumns 2026-07-31-02:20: +THE DOCK'S `renderTaskCard` IS THE SECOND PRODUCER OF THIS AFFORDANCE, AND IT HAD NO TEST. + +#3025 fixed both producers of a dock/plugin-rendered `TaskCard` — `MainContent.renderTaskCard` and +this hook's — so that the card receives `taskColumnFlags` instead of resolving nothing and falling +back to legacy lane ids for every role helper inside it (archive/revert affordances, progress, the +elapsed-time indicator, the planning badge). + +Its test covered `MainContent` only. MEASURED: deleting +`taskColumnFlags={input.columnFlagsByTaskId?.get(task.id)}` from `useRightDockController` left the +whole suite green — `MainContent.graph-popout` 6/6, `RightDock` 33/33, `TaskCard.host-inventory` 1/1. +That PR's own revert-check note says dropping the prop "from either `renderTaskCard`" would show up; +for this producer it did not. + +So the pair could silently become a single again, on the producer that draws cards into the right +dock where an operator actually sees them. One affordance with two producers needs two assertions — +the Surface Enumeration rule applies to the coverage, not only to the fix. + +Driven through the hook's real `renderTaskCard`, captured off the `renderProps` the controller hands +`RightDock`: that function IS the contract this pins. `RightDock` itself is stubbed so the assertion +cannot fail for unrelated dock plumbing. +*/ + +import React from "react"; +import { describe, it, expect, vi } from "vitest"; +import { render } from "@testing-library/react"; +import type { Task } from "@fusion/core"; +import { useRightDockController } from "../useRightDockController"; + +/* Capture the renderProps the controller builds; `renderTaskCard` is not on the returned controller. */ +const captured: { renderTaskCard?: (task: Task) => React.ReactNode } = {}; +vi.mock("../RightDock", async (importOriginal) => ({ + /* Keep the module's real exports — the controller imports its persistence helpers from here, and a + bare factory silently drops them ("No readStoredRightDockOpen export is defined on the mock"). */ + ...(await importOriginal>()), + RightDock: ({ renderProps }: { renderProps: { renderTaskCard?: (task: Task) => React.ReactNode } }) => { + captured.renderTaskCard = renderProps?.renderTaskCard; + return