From dc54acd7463aa39e77e22d8246d03b41f142614e Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 8 Jul 2026 22:30:29 -0700 Subject: [PATCH 1/2] test: fix TaskCard/GraphTaskNode useToast + TaskDetailModal CSS selector-list regex - TaskCard badge/footer tests + GraphTaskNode tests: mock useToast (RuntimeFallbackBadge now calls it; tests render TaskCard without ToastProvider) - TaskDetailModal.github-tracking-header: allow selector-list form in CSS regex (github+gitlab tracking rules consolidated) --- .../__tests__/TaskCard.badge-height.test.tsx | 7 +++++++ .../components/__tests__/TaskCard.badge-wrap.test.tsx | 7 +++++++ .../components/__tests__/TaskCard.footer-wrap.test.tsx | 7 +++++++ .../TaskDetailModal.github-tracking-header.test.tsx | 10 +++++++--- .../src/__tests__/GraphTaskNode.drag.test.tsx | 7 +++++++ .../src/__tests__/GraphTaskNode.test.tsx | 7 +++++++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx index 2ddf483b48..6c9e230b78 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx @@ -29,6 +29,13 @@ vi.mock("../ProviderIcon", () => ({ vi.mock("../../hooks/useTaskDiffStats", () => ({ useTaskDiffStats: () => ({ stats: null, loading: false }), })); +vi.mock("../../hooks/useToast", () => ({ + useToast: () => ({ + addToast: vi.fn(), + removeToast: vi.fn(), + toasts: [], + }), +})); const noop = () => {}; diff --git a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx index 0e008190b2..26bd13d82a 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx @@ -36,6 +36,13 @@ vi.mock("../PluginSlot", () => ({ vi.mock("../../hooks/useTaskDiffStats", () => ({ useTaskDiffStats: () => ({ stats: null, loading: false }), })); +vi.mock("../../hooks/useToast", () => ({ + useToast: () => ({ + addToast: vi.fn(), + removeToast: vi.fn(), + toasts: [], + }), +})); const noop = () => {}; diff --git a/packages/dashboard/app/components/__tests__/TaskCard.footer-wrap.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.footer-wrap.test.tsx index 2d778fcb8b..bf1c4d546a 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.footer-wrap.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.footer-wrap.test.tsx @@ -60,6 +60,13 @@ vi.mock("../../api", () => ({ vi.mock("../../hooks/useConfirm", () => ({ useConfirm: () => ({ confirm: vi.fn(async () => true) }), })); +vi.mock("../../hooks/useToast", () => ({ + useToast: () => ({ + addToast: vi.fn(), + removeToast: vi.fn(), + toasts: [], + }), +})); const noop = () => {}; diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.github-tracking-header.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.github-tracking-header.test.tsx index 5e7f15a5c5..a2e2bd251b 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.github-tracking-header.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.github-tracking-header.test.tsx @@ -35,14 +35,18 @@ describe("FN-4224 GitHub tracking header layout", () => { const css = loadAllAppCss(); + /* + FNXC:TaskDetailCSS 2026-07-08-13:00: + The github/gitlab tracking header rules were consolidated into a shared selector list (.detail-github-tracking-section .detail-source-header, .detail-gitlab-tracking-section .detail-source-header {…}), so the selector is no longer immediately followed by `{`. Allow the selector list (comma + sibling selector) between the tracked selector and the brace via [^{]* while still pinning the layout contract (flex-wrap/align-items/min-width). + */ expect(css).toMatch( - /\.detail-github-tracking-section\s+\.detail-source-header\s*\{[^}]*flex-wrap:\s*nowrap;[^}]*align-items:\s*center;[^}]*min-width:\s*0;/, + /\.detail-github-tracking-section\s+\.detail-source-header[^{]*\{[^}]*flex-wrap:\s*nowrap;[^}]*align-items:\s*center;[^}]*min-width:\s*0;/, ); expect(css).toMatch( - /\.detail-github-tracking-section\s+\.detail-source-summary\s*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;/, + /\.detail-github-tracking-section\s+\.detail-source-summary[^{]*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;/, ); expect(css).toMatch( - /@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-header\s*\{[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-summary\s*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}/, + /@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-header[^{]*\{[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}[\s\S]*?\.detail-github-tracking-section\s+\.detail-source-summary[^{]*\{[^}]*flex:\s*1 1 auto;[^}]*flex-wrap:\s*nowrap;[^}]*min-width:\s*0;[^}]*\}/, ); }); }); diff --git a/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx b/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx index ed2158a456..ec7345de98 100644 --- a/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +++ b/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx @@ -3,6 +3,13 @@ import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import type React from "react"; import type { Task } from "@fusion/core"; import { GraphTaskNode } from "../GraphTaskNode"; +/* +FNXC:DependencyGraphTests 2026-07-08-13:10: +GraphTaskNode renders the REAL TaskCard; TaskCard's RuntimeFallbackBadge calls the dashboard's useToast() hook, and this file has no ToastProvider. Mock useToast (same as the dashboard's own TaskCard.test.tsx) to avoid "useToast must be used within ToastProvider". +*/ +vi.mock("@fusion/dashboard/app/hooks/useToast", () => ({ + useToast: () => ({ addToast: vi.fn(), removeToast: vi.fn(), toasts: [] }), +})); function task(id = "FN-1"): Task { return { id, description: id, column: "todo", dependencies: [], steps: [], currentStep: 0, log: [] } as Task; diff --git a/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx b/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx index 7be0a0e2bf..8e5982fa8c 100644 --- a/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +++ b/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx @@ -3,6 +3,13 @@ import type { Task } from "@fusion/core"; import { afterEach, describe, expect, it, vi } from "vitest"; import { TaskCard } from "@fusion/dashboard/app/components/TaskCard"; import { GraphTaskNode } from "../GraphTaskNode"; +/* +FNXC:DependencyGraphTests 2026-07-08-13:10: +GraphTaskNode renders the REAL TaskCard (to verify prop pass-through), and TaskCard now renders RuntimeFallbackBadge which calls the dashboard's useToast() hook. This file has no ToastProvider, so mock useToast the same way the dashboard's own TaskCard.test.tsx does to avoid "useToast must be used within ToastProvider". +*/ +vi.mock("@fusion/dashboard/app/hooks/useToast", () => ({ + useToast: () => ({ addToast: vi.fn(), removeToast: vi.fn(), toasts: [] }), +})); function createTask(overrides: Partial = {}): Task { return { From c74c5f6d6768b4c2cd5aec8fcd06fe188c2dbf30 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 8 Jul 2026 22:37:29 -0700 Subject: [PATCH 2/2] perf(test): fast-forward fake timers in step-session terminal-activity test (was 22.6s real-time wait) The 'publishes failed terminal workflow step activity' test awaited executeAll() directly while the executor retried a failing step 3x with sleep() delays. Under useFakeTimers({ shouldAdvanceTime: true }) those sleeps consumed REAL wall-clock time (~22.6s locally, ballooning under CI load and busting the shard-2 watchdog). Fast-forward the retry sleeps via vi.advanceTimersByTimeAsync like sibling retry tests; the loop now completes in milliseconds. --- .../engine/src/__tests__/step-session-executor.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/engine/src/__tests__/step-session-executor.test.ts b/packages/engine/src/__tests__/step-session-executor.test.ts index dfd754189d..4fffc446f4 100644 --- a/packages/engine/src/__tests__/step-session-executor.test.ts +++ b/packages/engine/src/__tests__/step-session-executor.test.ts @@ -1399,7 +1399,15 @@ describe("StepSessionExecutor", () => { agentStore: { saveRun } as any, } as any); - const results = await executor.executeAll(); + // FNXC:EngineTests 2026-07-09-06:00: + // executeAll retries the failing step 3× with sleep() delays between attempts. With + // useFakeTimers({ shouldAdvanceTime: true }) these sleeps advance REAL wall-clock time if + // the test awaits executeAll directly (was 22.6s, ballooning under CI load and busting the + // shard-2 watchdog). Fast-forward the retry sleeps via fake timers like the sibling retry + // tests below, so the loop completes in milliseconds. + const resultsPromise = executor.executeAll(); + await vi.advanceTimersByTimeAsync(60_000); + const results = await resultsPromise; expect(results).toEqual([{ stepIndex: 0, success: false, error: "boom", retries: 3, tokenUsage: undefined }]); const terminalRun = saveRun.mock.calls.at(-1)?.[0];