From b3e16b3988e2563cfe162b69f5f1dad7dc0f1616 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 30 Jul 2026 18:09:46 -0700 Subject: [PATCH] =?UTF-8?q?fix(tests):=20rendering.test=20queries=20contai?= =?UTF-8?q?ner;=20the=20modal=20is=20portalled=20(28=20=E2=86=92=204)=20(#?= =?UTF-8?q?2895)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Fourth file, same defect `TaskDetailModal.rendering.test.tsx` is the whole of the dashboard `app:backfill 4/4` shard bar one case (28 of 29). **57** container-rooted lookups converted. `TaskDetailModal` mounts through `createPortal`, so `container` is empty and every one returned null — visible in the two failure shapes this file produced: ``` 10x expected null to be truthy ~14x expected undefined to be '' ← container.querySelector(x)?.textContent ``` ## Four remain, deliberately They assert the modal's **wrapper structure**, not its contents: ```ts expect(document.querySelector(".modal-overlay.open")).toBeTruthy(); ``` `FloatingWindow` renders `.floating-window-overlay` (`FloatingWindow.tsx:627`). The only `.modal-overlay open` left in `TaskDetailModal` is the unrelated *refine* overlay at `:6801`. So these pin the **pre-FloatingWindow** wrapper. Re-pointing them means encoding the *current* modal-shell contract — a UI structure decision that belongs with whoever owns the FloatingWindow adoption, not bundled into a query-root fix where it would be easy to miss. Left failing and flagged rather than guessed at. It is also a different failure shape from the rest: `expected
to be null` on a mobile-variant badge, i.e. an assertion that *found* something, versus 24 that found nothing. Different cause, different fix, different reviewer. ## Evidence | | result | |---|---| | the file | **120 tests, 4 failed** (was 28) | | mutation: rename `.detail-id` in `TaskDetailModal` | **5 failed** | The mutation matters because the change is "query a different root" — the risk is assertions that now find *something* and stop discriminating. They still observe the real component. `pnpm lint` clean. Test-only; `TaskDetailModal.tsx` restored clean. ## The portal defect, totalled | PR | file | cleared | |---|---|---| | #2885 | `TaskDetailModal.models-progress-workflow` | 30 | | #2890 | `settings-mobile` | 17 | | #2893 | `TaskDetailModal.definition-actions` | 12 | | this | `TaskDetailModal.rendering` | 24 | **83 of the ~111 backfill failures**, one defect: tests querying `container` for components that render through a portal. It hid for so long because `screen.*` queries in the same files always worked — they query the document — so the failures read as *"the component never rendered"* rather than *"we asked the wrong root"*. And it was invisible to CI: the quality runner stops after the first failing lane, and `app:app` failed ahead of every backfill shard. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) --- .../TaskDetailModal.rendering.test.tsx | 186 ++++++++++-------- 1 file changed, 104 insertions(+), 82 deletions(-) diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx index 912207274c..6027b278dd 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx @@ -12,7 +12,7 @@ query ambiguous once the trigger stopped being a mobile-only affordance. import { readFileSync } from "node:fs"; import { resolve } from "node:path"; 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, cleanup } from "@testing-library/react"; // FNXC:Markdown 2026-06-23-03:30: Mock the heavy `mermaid` library so the shared // markdown pipeline's MermaidDiagram resolves without loading the real renderer. @@ -89,7 +89,7 @@ describe("TaskDetailModal", () => { expect(badge).toHaveTextContent("Docs"); expect(badge.closest(".detail-timestamps")).toBeTruthy(); expect(badge.closest(".detail-title-row")).toBeNull(); - expect(container.querySelector(".detail-title-row .detail-workflow-badge")).toBeNull(); + expect(document.querySelector(".detail-title-row .detail-workflow-badge")).toBeNull(); expect(screen.getAllByTestId("task-detail-workflow-badge")).toHaveLength(1); expect(screen.getByText("FN-101")).toBeInTheDocument(); expect(screen.getByText("Todo")).toBeInTheDocument(); @@ -179,7 +179,7 @@ describe("TaskDetailModal", () => { await waitFor(() => expect(dashboardApi.fetchBoardWorkflows).toHaveBeenCalledTimes(1)); expect(screen.queryByTestId("task-detail-workflow-badge")).toBeNull(); - expect(container.querySelector(".detail-workflow-badge")).toBeNull(); + expect(document.querySelector(".detail-workflow-badge")).toBeNull(); }); it("renders the canonical badge beside the Updated timestamp in the mobile back-header variant", async () => { @@ -200,14 +200,14 @@ describe("TaskDetailModal", () => { ); const badge = await screen.findByTestId("task-detail-workflow-badge"); - const timestamps = container.querySelector(".detail-timestamps"); + const timestamps = document.querySelector(".detail-timestamps"); const updatedLabel = screen.getByText("Updated").closest(".detail-timestamp-item"); expect(badge).toHaveTextContent("Docs"); expect(badge.parentElement).toBe(timestamps); expect(updatedLabel?.nextElementSibling).toBe(badge); expect(screen.getAllByTestId("task-detail-workflow-badge")).toHaveLength(1); expect(screen.queryByTestId("task-detail-workflow-badge-mobile")).toBeNull(); - expect(container.querySelector(".detail-title-row .detail-workflow-badge")).toBeNull(); + expect(document.querySelector(".detail-title-row .detail-workflow-badge")).toBeNull(); expect(screen.getByRole("button", { name: "Back to task list" })).toBeInTheDocument(); }); }); @@ -281,7 +281,7 @@ describe("TaskDetailModal", () => { ); // Raw
/ renders as a real disclosure element. - const details = container.querySelector(".markdown-body details"); + const details = document.querySelector(".markdown-body details"); expect(details).not.toBeNull(); expect(details?.querySelector("summary")?.textContent).toBe("Disclosure title"); expect(details?.textContent).toContain("Hidden detail body."); @@ -290,7 +290,7 @@ describe("TaskDetailModal", () => { expect(container.textContent).not.toContain("secret comment"); //