diff --git a/packages/dashboard/app/__tests__/styles-css-rgba-tokenization.test.ts b/packages/dashboard/app/__tests__/styles-css-rgba-tokenization.test.ts index 24f25c7150..f3dff107e3 100644 --- a/packages/dashboard/app/__tests__/styles-css-rgba-tokenization.test.ts +++ b/packages/dashboard/app/__tests__/styles-css-rgba-tokenization.test.ts @@ -15,10 +15,10 @@ const convertedSelectors: SelectorExpectation[] = [ selector: ".modal-actions", expectedColorMix: "color-mix(in srgb, var(--surface) 60%, transparent)", }, - { - selector: ".settings-sidebar", - expectedColorMix: "color-mix(in srgb, var(--surface) 60%, transparent)", - }, + // FN-7825 made .settings-sidebar structural-only (display/flex/scrollbar) and removed its + // background color-mix, so it is no longer a rgba->color-mix converted selector. The remaining + // entries are the selectors that still carry a tokenized color-mix value. + // FNXC:CssTokenization 2026-07-13-14:00 (round 11): { selector: ".step-progress-segment[data-tooltip]:hover::after", expectedColorMix: "color-mix(in srgb, var(--text) 20%, transparent)", diff --git a/packages/dashboard/app/components/__tests__/SettingsModalNodeRouting.test.tsx b/packages/dashboard/app/components/__tests__/SettingsModalNodeRouting.test.tsx index 7b075f4a22..5b7bcd2da1 100644 --- a/packages/dashboard/app/components/__tests__/SettingsModalNodeRouting.test.tsx +++ b/packages/dashboard/app/components/__tests__/SettingsModalNodeRouting.test.tsx @@ -112,7 +112,12 @@ const baseSettings = { }; function renderModal() { - return render( {}} addToast={() => {}} />); + // FNXC:DashboardMocks 2026-07-13-14:00 (round 11): + // node-routing is in ADVANCED_SETTINGS_SECTION_IDS, so the nav item is hidden + // unless Advanced settings is enabled. Passing initialSection="node-routing" + // enables advanced mode (SettingsModal derives showAdvancedSettings from the + // requested section) so the "Node Routing" nav button renders and is clickable. + return render( {}} addToast={() => {}} initialSection="node-routing" />); } async function ready() { diff --git a/packages/dashboard/app/components/__tests__/TaskCard.cli-states.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.cli-states.test.tsx index b1f16d1b79..9b5d8f824d 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.cli-states.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.cli-states.test.tsx @@ -6,10 +6,33 @@ import type { Task } from "@fusion/core"; vi.mock("lucide-react", () => { const Stub = () => null; + // FNXC:DashboardMocks 2026-07-13-14:00 (round 11): + // TaskCard now imports priorityIndicator.tsx, which reads ArrowDown/ArrowUp/Flag/TriangleAlert + // from lucide-react at module-init time. A get-only Proxy is not enough: Vitest validates named + // ESM exports via `in`/`getOwnPropertyDescriptor` before reading the binding, so a bare Proxy + // over {} reports no exports and throws "No ArrowDown export is defined on the lucide-react mock". + // Expose every string key as an own enumerable Stub so any icon resolves to a null stub. return new Proxy({}, { get: (_target, prop) => prop === "then" ? undefined : Stub, + has: (_target, prop) => typeof prop === "string" && prop !== "then", + getOwnPropertyDescriptor: (_target, prop) => + typeof prop === "string" && prop !== "then" + ? { configurable: true, enumerable: true, value: Stub, writable: true } + : undefined, }); }); +// FNXC:DashboardMocks 2026-07-13-14:00 (round 11): +// TaskCard embeds RuntimeFallbackBadge, which calls the shared useToast() hook +// directly (not the addToast prop). This file renders outside a +// ToastProvider, so mock the hook to avoid "useToast must be used within +// ToastProvider", matching the TaskCard.test.tsx pattern. +vi.mock("../../hooks/useToast", () => ({ + useToast: () => ({ + addToast: vi.fn(), + removeToast: vi.fn(), + toasts: [], + }), +})); vi.mock("../ProviderIcon", () => ({ ProviderIcon: ({ provider }: { provider: string }) => , diff --git a/packages/dashboard/src/__tests__/mcp-helper-forwarding.test.ts b/packages/dashboard/src/__tests__/mcp-helper-forwarding.test.ts index e2c0bad036..56b0db63b9 100644 --- a/packages/dashboard/src/__tests__/mcp-helper-forwarding.test.ts +++ b/packages/dashboard/src/__tests__/mcp-helper-forwarding.test.ts @@ -30,6 +30,12 @@ vi.mock("@fusion/engine", () => ({ createFnAgent: mockCreateFnAgent, promptWithFallback: mockPromptWithFallback, resolveMcpServersForStore: mockResolveMcpServersForStore, + // FNXC:DashboardMocks 2026-07-13-14:00 (round 11): + // Insight extraction now resolves a per-run thinking level via resolvePlanningThinkingLevel + // (imported from @fusion/engine) before reaching resolveMcpServersForStore. Without this mock + // export the call throws TypeError, the run is marked failed (still HTTP 201), and the MCP + // forwarding assertion sees 0 calls. Mirror the insights-routes.test.ts mock shape. + resolvePlanningThinkingLevel: vi.fn((_settings: unknown, thinkingLevel?: string) => thinkingLevel), })); const resolvedMcpServers = [ diff --git a/packages/dashboard/src/__tests__/pr-conflict-resolver.test.ts b/packages/dashboard/src/__tests__/pr-conflict-resolver.test.ts index 68a9effb6e..a6ec5192f5 100644 --- a/packages/dashboard/src/__tests__/pr-conflict-resolver.test.ts +++ b/packages/dashboard/src/__tests__/pr-conflict-resolver.test.ts @@ -16,6 +16,8 @@ vi.mock("../routes/resolve-diff-base.js", () => ({ })); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + resolveMcpServersForStore: vi.fn(() => []), createResolvedAgentSession: mockCreateResolvedAgentSession, })); diff --git a/packages/dashboard/src/__tests__/project-pause-resume-routes.test.ts b/packages/dashboard/src/__tests__/project-pause-resume-routes.test.ts index 34bfd20531..22601639f8 100644 --- a/packages/dashboard/src/__tests__/project-pause-resume-routes.test.ts +++ b/packages/dashboard/src/__tests__/project-pause-resume-routes.test.ts @@ -33,6 +33,18 @@ vi.mock("@fusion/core", async () => { }); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + getExemptToolNames: vi.fn(() => []), + promptWithFallback: vi.fn(), + reloadExemptTools: vi.fn(), + resolveIntegrationBranch: vi.fn(), + discoverMcpServers: vi.fn(() => []), + resolveMcpServersForRuntime: vi.fn(() => []), + resolveMcpServersForStore: vi.fn(() => []), + validateMcpServer: vi.fn(), + isInProcessBackupCommand: vi.fn(), + isInProcessMemoryBackupCommand: vi.fn(), + formatInProcessBackupError: vi.fn(), listCliAdapterDescriptors: () => [], createFnAgent: vi.fn(async () => ({ session: { diff --git a/packages/dashboard/src/__tests__/routes-approval-sandbox-provisioning.test.ts b/packages/dashboard/src/__tests__/routes-approval-sandbox-provisioning.test.ts index 0c2537fb45..500e6605ee 100644 --- a/packages/dashboard/src/__tests__/routes-approval-sandbox-provisioning.test.ts +++ b/packages/dashboard/src/__tests__/routes-approval-sandbox-provisioning.test.ts @@ -55,6 +55,9 @@ vi.mock("@fusion/core", async () => { }); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + assertNoSecretPlaintext: vi.fn(), + executeApprovedWorktrunkInstall: vi.fn(), listCliAdapterDescriptors: () => [], executeApprovedAgentProvisioning: vi.fn(async () => undefined), })); diff --git a/packages/dashboard/src/__tests__/routes-approval.test.ts b/packages/dashboard/src/__tests__/routes-approval.test.ts index c4bd9d2aee..7aa317bab4 100644 --- a/packages/dashboard/src/__tests__/routes-approval.test.ts +++ b/packages/dashboard/src/__tests__/routes-approval.test.ts @@ -87,6 +87,10 @@ vi.mock("@fusion/core", async (importOriginal) => ({ const executeApprovedWorktrunkInstall = vi.fn(async () => ({ binaryPath: "~/.fusion/bin/wt", source: "installed-release" })); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + assertNoSecretPlaintext: vi.fn(), + executeApprovedAgentProvisioning: vi.fn(), + executeApprovedWorktrunkInstall: vi.fn(), listCliAdapterDescriptors: () => [], executeApprovedAgentProvisioning, executeApprovedWorktrunkInstall, diff --git a/packages/dashboard/src/__tests__/session-reconnect.test.ts b/packages/dashboard/src/__tests__/session-reconnect.test.ts index 61b559b548..70e99d6f6e 100644 --- a/packages/dashboard/src/__tests__/session-reconnect.test.ts +++ b/packages/dashboard/src/__tests__/session-reconnect.test.ts @@ -46,6 +46,16 @@ const { mockCreateFnAgent } = vi.hoisted(() => ({ })); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + getExemptToolNames: vi.fn(() => []), + reloadExemptTools: vi.fn(), + resolveIntegrationBranch: vi.fn(), + discoverMcpServers: vi.fn(() => []), + resolveMcpServersForRuntime: vi.fn(() => []), + validateMcpServer: vi.fn(), + isInProcessBackupCommand: vi.fn(), + isInProcessMemoryBackupCommand: vi.fn(), + formatInProcessBackupError: vi.fn(), listCliAdapterDescriptors: () => [], // FNXC:DashboardSessionTests 2026-06-14-09:06: planning.ts spreads createWorkflowAuthoringTools into agent customTools; this focused engine mock must export it to keep AI-session tests aligned with production planning setup. createWorkflowAuthoringTools: vi.fn(() => []), diff --git a/packages/dashboard/src/__tests__/setup-routes.test.ts b/packages/dashboard/src/__tests__/setup-routes.test.ts index 59f87247c8..13216e90dd 100644 --- a/packages/dashboard/src/__tests__/setup-routes.test.ts +++ b/packages/dashboard/src/__tests__/setup-routes.test.ts @@ -34,6 +34,18 @@ vi.mock("@fusion/core", async () => { }); vi.mock("@fusion/engine", () => ({ + // FNXC:TestInfrastructure 2026-07-13-11:05: Missing @fusion/engine barrel exports added for mock completeness (check-mock-completeness.mjs gate). + getExemptToolNames: vi.fn(() => []), + promptWithFallback: vi.fn(), + reloadExemptTools: vi.fn(), + resolveIntegrationBranch: vi.fn(), + discoverMcpServers: vi.fn(() => []), + resolveMcpServersForRuntime: vi.fn(() => []), + resolveMcpServersForStore: vi.fn(() => []), + validateMcpServer: vi.fn(), + isInProcessBackupCommand: vi.fn(), + isInProcessMemoryBackupCommand: vi.fn(), + formatInProcessBackupError: vi.fn(), listCliAdapterDescriptors: () => [], createFnAgent: vi.fn(async () => ({ session: { diff --git a/scripts/check-mock-completeness.mjs b/scripts/check-mock-completeness.mjs index 0df3703c84..7dfccd04b5 100644 --- a/scripts/check-mock-completeness.mjs +++ b/scripts/check-mock-completeness.mjs @@ -46,6 +46,8 @@ const BARRELS = [ join(root, "packages/cli/src/__tests__"), join(root, "packages/cli/src/commands/__tests__"), join(root, "packages/cli/src/plugins/__tests__"), + // FNXC:TestInfrastructure 2026-07-13-11:00: Dashboard API tests also mock @fusion/engine; include them to prevent the same barrel-export drift. + join(root, "packages/dashboard/src/__tests__"), ], cliSrc: join(root, "packages/cli/src"), }, @@ -124,9 +126,9 @@ function resolveSourceFiles(testPath, cliSrc) { if (existsSync(resolved)) sources.add(resolved); } - // Convention: __tests__/foo.test.ts → ../foo.ts + // Convention: __tests__/foo.test.ts → ../foo.ts, __tests__/foo.test.tsx → ../foo.tsx const noTests = testPath.replace(/__tests\//, ""); - const convPath = noTests.replace(/\.test\.ts$/, ".ts"); + const convPath = noTests.replace(/\.test\.ts$/, ".ts").replace(/\.test\.tsx$/, ".tsx"); if (existsSync(convPath)) sources.add(convPath); // bin.test.ts special case @@ -165,7 +167,8 @@ function extractMockKeys(testSrc, moduleName, testPath) { const body = testSrc.slice(bodyStart, i - 1); const keys = new Set(); - const keyRe = /(?:^|\n)\s*([A-Za-z_$][\w$]*)\s*(?::)/g; + // Match both regular properties (key: value) and shorthand (key, or key}). + const keyRe = /(?:^|\n)\s*([A-Za-z_$][\w$]*)\s*(?::|[,}])/g; let km; while ((km = keyRe.exec(body)) !== null) { keys.add(km[1]); @@ -216,7 +219,7 @@ function collectTs(dir) { const full = join(dir, entry); const st = statSync(full); if (st.isDirectory()) out = out.concat(collectTs(full)); - else if (entry.endsWith(".ts")) out.push(full); + else if (entry.endsWith(".ts") || entry.endsWith(".tsx")) out.push(full); } } catch { /* dir may not exist */ } return out; @@ -235,7 +238,7 @@ for (const cfg of BARRELS) { const testFiles = new Set(); for (const dir of cfg.testDirs) { for (const f of collectTs(dir)) { - if (f.endsWith(".test.ts")) testFiles.add(f); + if (f.endsWith(".test.ts") || f.endsWith(".test.tsx")) testFiles.add(f); } }