diff --git a/docs/test-velocity-baseline.md b/docs/test-velocity-baseline.md index 24acadcf93..28e329a067 100644 --- a/docs/test-velocity-baseline.md +++ b/docs/test-velocity-baseline.md @@ -5,53 +5,63 @@ ## Latest baseline - Cycle: **2026-W26** -- Captured at: **2026-06-25T05:47:54.804Z** -- Timing snapshot: `scripts/test-timings.json` captured at **2026-06-25T05:45:08.116Z** +- Captured at: **2026-06-27T02:02:13.530Z** +- Timing snapshot: `scripts/test-timings.json` captured at **2026-06-25T05:45:08.116Z**; SettingsModal rows below include the FN-7087 scoped refresh captured from `dashboard-app-quality-settings` after the monolith split. - Quarantine ledger: `scripts/lib/test-quarantine.json` ## Metrics | Metric | Current | Delta vs previous | |---|---:|---:| -| Merge gate wall-time (`pnpm test:gate`) | 7.5s | -9.3s | -| Boot smoke wall-time (`pnpm smoke:boot`) | 18.2s | -4.7s | -| Changed-only test wall-time (`pnpm test`) | 9.4s | -10.2s | +| Merge gate wall-time (`pnpm test:gate`) | 8.1s | +649ms | +| Boot smoke wall-time (`pnpm smoke:boot`) | 17.6s | -603ms | +| Changed-only test wall-time (`pnpm test`) | unavailable | n/a | | Quarantine / flake count | 0 | 0 | | Deletion-due quarantines | 0 | n/a | ## Measurement failures -- None recorded. +- Changed-only tests (`pnpm test`): exit 1 after 31.0s. The failure was outside the FN-7087 SettingsModal scoped run, so the weekly generated snapshot was hand-corrected with the successful split SettingsModal measurement pending the next fully green weekly refresh. ## Timing snapshot notes -- No stale or missing timing metadata detected in the rendered slowest-file rows. +- FN-7087 manually replaced the obsolete `SettingsModal` monolith entry with the four current split files measured by `pnpm --filter @fusion/dashboard exec vitest run --project dashboard-app-quality-settings --reporter=default --silent=passed-only`. +- One historical slow-file entry in `scripts/test-timings.json` still points at a removed ChatView path; refresh `scripts/test-timings.json` after the changed-only lane is green before selecting the next slow-test rewrite from this report. -## Slowest 20 test files +## Slowest known current test files and settings split refresh | Rank | File | Package | Duration | |---:|---|---|---:| -| 1 | `packages/dashboard/app/components/__tests__/SettingsModal.test.tsx` | @fusion/dashboard | 1m 01s | -| 2 | `packages/dashboard/src/__tests__/insights-routes.test.ts` | @fusion/dashboard | 26.5s | -| 3 | `packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts` | @fusion/engine | 24.7s | -| 4 | `packages/dashboard/app/components/__tests__/ChatView.test.tsx` | @fusion/dashboard | 24.4s | -| 5 | `packages/dashboard/src/__tests__/workflow-routes.test.ts` | @fusion/dashboard | 22.0s | -| 6 | `packages/core/src/__tests__/db.test.ts` | @fusion/core | 21.2s | -| 7 | `packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx` | @fusion/dashboard | 16.9s | -| 8 | `packages/core/src/__tests__/mission-store.test.ts` | @fusion/core | 16.0s | -| 9 | `packages/cli/src/__tests__/extension.test.ts` | @runfusion/fusion | 15.7s | -| 10 | `packages/dashboard/app/components/__tests__/AgentPromptsManager.test.tsx` | @fusion/dashboard | 14.8s | -| 11 | `packages/dashboard/app/components/__tests__/App.test.tsx` | @fusion/dashboard | 14.6s | -| 12 | `packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx` | @fusion/dashboard | 14.1s | -| 13 | `packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx` | @fusion/dashboard | 13.7s | -| 14 | `packages/dashboard/src/__tests__/routes-auth.test.ts` | @fusion/dashboard | 13.6s | -| 15 | `packages/core/src/__tests__/agent-store.test.ts` | @fusion/core | 13.4s | -| 16 | `packages/engine/src/__tests__/workspace-merger-idempotency.test.ts` | @fusion/engine | 12.7s | -| 17 | `packages/engine/src/__tests__/self-healing-workspace.test.ts` | @fusion/engine | 11.8s | -| 18 | `packages/engine/src/__tests__/pr-response-run.test.ts` | @fusion/engine | 11.6s | -| 19 | `packages/dashboard/app/components/__tests__/ListView.test.tsx` | @fusion/dashboard | 11.3s | +| 1 | `packages/dashboard/src/__tests__/insights-routes.test.ts` | @fusion/dashboard | 26.5s | +| 2 | `packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts` | @fusion/engine | 24.7s | +| 3 | `packages/dashboard/src/__tests__/workflow-routes.test.ts` | @fusion/dashboard | 22.0s | +| 4 | `packages/core/src/__tests__/db.test.ts` | @fusion/core | 21.2s | +| 5 | `packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx` | @fusion/dashboard | 16.9s | +| 6 | `packages/core/src/__tests__/mission-store.test.ts` | @fusion/core | 16.0s | +| 7 | `packages/cli/src/__tests__/extension.test.ts` | @runfusion/fusion | 15.7s | +| 8 | `packages/dashboard/app/components/__tests__/AgentPromptsManager.test.tsx` | @fusion/dashboard | 14.8s | +| 9 | `packages/dashboard/app/components/__tests__/App.test.tsx` | @fusion/dashboard | 14.6s | +| 10 | `packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx` | @fusion/dashboard | 14.1s | +| 11 | `packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx` | @fusion/dashboard | 13.7s | +| 12 | `packages/dashboard/src/__tests__/routes-auth.test.ts` | @fusion/dashboard | 13.6s | +| 13 | `packages/core/src/__tests__/agent-store.test.ts` | @fusion/core | 13.4s | +| 14 | `packages/dashboard/app/components/__tests__/SettingsModal.scheduling-merge.test.tsx` | @fusion/dashboard | 12.8s | +| 15 | `packages/engine/src/__tests__/workspace-merger-idempotency.test.ts` | @fusion/engine | 12.7s | +| 16 | `packages/engine/src/__tests__/self-healing-workspace.test.ts` | @fusion/engine | 11.8s | +| 17 | `packages/engine/src/__tests__/pr-response-run.test.ts` | @fusion/engine | 11.6s | +| 18 | `packages/dashboard/app/components/__tests__/ListView.test.tsx` | @fusion/dashboard | 11.3s | +| 19 | `packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx` | @fusion/dashboard | 11.2s | | 20 | `plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts` | @fusion-plugin-examples/compound-engineering | 11.0s | +### FN-7087 SettingsModal split files + +| File | Tests | Duration | +|---|---:|---:| +| `packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx` | 59 | 10.6s | +| `packages/dashboard/app/components/__tests__/SettingsModal.models-auth.test.tsx` | 57 | 10.4s | +| `packages/dashboard/app/components/__tests__/SettingsModal.scheduling-merge.test.tsx` | 97 | 12.8s | +| `packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx` | 53 | 11.2s | + ## Quarantine age buckets | Age bucket | Count | @@ -71,16 +81,16 @@ | Row | Captured at | Gate | Boot smoke | `pnpm test` | Quarantine count | |---|---|---:|---:|---:|---:| -| Previous | 2026-06-23T18:43:21.941Z | 16.8s | 22.8s | 19.6s | 0 | -| Latest | 2026-06-25T05:47:54.804Z | 7.5s | 18.2s | 9.4s | 0 | -| Delta | — | -9.3s | -4.7s | -10.2s | 0 | +| Previous | 2026-06-25T05:47:54.804Z | 7.5s | 18.2s | 9.4s | 0 | +| Latest | 2026-06-27T02:02:13.530Z | 8.1s | 17.6s | unavailable | 0 | +| Delta | — | +649ms | -603ms | n/a | 0 | _Future weekly rows append to `scripts/test-velocity-history.json`; compare the latest row against the previous row before posting to #leads._ ## Post to #leads ```text -FN-6612 weekly test velocity: gate 7.5s (-9.3s), boot smoke 18.2s (-4.7s), pnpm test 9.4s (-10.2s), quarantine ledger 0 (0). Slowest file: packages/dashboard/app/components/__tests__/SettingsModal.test.tsx at 1m 01s. Deletion-due quarantines: 0. +FN-6612 weekly test velocity: gate 8.1s (+649ms), boot smoke 17.6s (-603ms), pnpm test unavailable (n/a), quarantine ledger 0 (0). Slowest known current file: packages/dashboard/src/__tests__/insights-routes.test.ts at 26.5s; SettingsModal split refresh slowest sibling: packages/dashboard/app/components/__tests__/SettingsModal.scheduling-merge.test.tsx at 12.8s. Deletion-due quarantines: 0. ``` ## How to refresh diff --git a/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx b/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx index d14a4f4f62..efadf9dc89 100644 --- a/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx +++ b/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx @@ -1,8 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent, waitFor, within } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import path from "path"; -import { SettingsModal } from "../SettingsModal"; import type { PluginUiContributionEntry } from "../../api"; import { mockFetchSettings, @@ -68,6 +65,7 @@ import { mockUseMobileKeyboard, defaultSettings, renderModal, + renderModalSection, waitForSettingsModalReady, settingsModalUser, installSettingsModalEnv, @@ -196,12 +194,6 @@ describe("SettingsModal", () => { installSettingsModalEnv(); describe("Remote section", () => { - let user: ReturnType; - - beforeEach(() => { - user = userEvent.setup({ delay: null, pointerEventsCheck: 0 }); - }); - beforeEach(() => { mockFetchSettings.mockResolvedValue({ ...defaultSettings, @@ -211,13 +203,13 @@ describe("SettingsModal", () => { const openRemoteSection = async () => { const [remoteSectionButton] = await screen.findAllByRole("button", { name: /Remote Access/i }); - await user.click(remoteSectionButton); + await settingsModalUser.click(remoteSectionButton); await screen.findByRole("heading", { name: "Remote Access" }); }; const openAdvancedSettings = async () => { const summary = screen.getByText("Advanced Settings"); - await user.click(summary); + await settingsModalUser.click(summary); }; it("opens the Remote Access section when the legacy experimental flag is absent", async () => { @@ -235,19 +227,17 @@ describe("SettingsModal", () => { describe("with default Remote render", () => { beforeEach(async () => { - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + await renderModalSection("remote", "Remote Access"); }); it("shows provider-specific settings when provider selected and auto-saves on Start Tunnel", async () => { - await user.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); expect(screen.queryByLabelText("Hostname label")).not.toBeInTheDocument(); expect(screen.queryByLabelText("Target port")).not.toBeInTheDocument(); expect(screen.getByLabelText("Accept routes")).toBeInTheDocument(); expect(screen.queryByLabelText("Tunnel name")).not.toBeInTheDocument(); - await user.click(screen.getByLabelText("Cloudflare")); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); expect(screen.queryByLabelText("Accept routes")).not.toBeInTheDocument(); if (!screen.queryByLabelText("Tunnel name")) { @@ -260,7 +250,7 @@ describe("SettingsModal", () => { fireEvent.change(screen.getByLabelText("Tunnel token"), { target: { value: "cf_token" } }); fireEvent.change(screen.getByLabelText("Ingress URL"), { target: { value: "https://remote.example.com" } }); - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); await waitFor(() => { expect(mockUpdateRemoteSettings).toHaveBeenCalledWith( @@ -278,15 +268,15 @@ describe("SettingsModal", () => { }); it("toggles Cloudflare named tunnel advanced section and persists quick tunnel state", async () => { - await user.click(screen.getByLabelText("Cloudflare")); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); const namedTunnelSummary = screen.getByText(/Advanced \(Named Tunnel\)/i, { selector: "summary" }); if (!screen.queryByLabelText("Tunnel name")) { - await user.click(namedTunnelSummary); + await settingsModalUser.click(namedTunnelSummary); } expect(await screen.findByLabelText("Tunnel name")).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); await waitFor(() => { expect(mockUpdateRemoteSettings).toHaveBeenCalledWith(expect.objectContaining({ remoteCloudflareQuickTunnel: false }), undefined); }); @@ -302,7 +292,7 @@ describe("SettingsModal", () => { it("Start Tunnel button is disabled when no provider is selected", async () => { const startButton = screen.getByRole("button", { name: "Start Tunnel" }); expect(startButton).toBeDisabled(); - await user.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); expect(startButton).not.toBeDisabled(); }); @@ -312,31 +302,31 @@ describe("SettingsModal", () => { }); it("only the selected provider's settings are rendered", async () => { - await user.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); expect(screen.getByLabelText("Accept routes")).toBeInTheDocument(); expect(screen.queryByText(/Advanced \(Named Tunnel\)/i)).not.toBeInTheDocument(); - await user.click(screen.getByLabelText("Cloudflare")); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); expect(screen.getByText(/Advanced \(Named Tunnel\)/i)).toBeInTheDocument(); expect(screen.queryByLabelText("Accept routes")).not.toBeInTheDocument(); }); it("sets quick tunnel false when opening Cloudflare advanced details", async () => { - await user.click(screen.getByLabelText("Cloudflare")); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); const namedTunnelSummary = screen.getByText(/Advanced \(Named Tunnel\)/i, { selector: "summary" }); if (!screen.queryByLabelText("Tunnel name")) { - await user.click(namedTunnelSummary); + await settingsModalUser.click(namedTunnelSummary); } - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); await waitFor(() => { expect(mockUpdateRemoteSettings).toHaveBeenCalledWith(expect.objectContaining({ remoteCloudflareQuickTunnel: false }), undefined); }); }); it("Start Tunnel auto-saves with enabled=true on selected provider before starting", async () => { - await user.click(screen.getByLabelText("Tailscale")); - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); await waitFor(() => { expect(mockUpdateRemoteSettings).toHaveBeenCalledWith( @@ -353,9 +343,7 @@ describe("SettingsModal", () => { it("renders remote-status-bar with stopped state and omits share block when not running", async () => { mockFetchRemoteStatus.mockResolvedValue({ provider: null, state: "stopped", url: null, lastError: null }); - const { container } = renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + const { container } = await renderModalSection("remote", "Remote Access"); const statusBar = container.querySelector(".remote-status-bar"); expect(statusBar).toBeInTheDocument(); @@ -365,9 +353,7 @@ describe("SettingsModal", () => { it("renders remote-share-block when tunnel is running with a URL", async () => { mockFetchRemoteStatus.mockResolvedValue({ provider: "tailscale", state: "running", url: "https://machine.ts.net/", lastError: null }); - const { container } = renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + const { container } = await renderModalSection("remote", "Remote Access"); const statusBar = container.querySelector(".remote-status-bar"); expect(statusBar).toBeInTheDocument(); @@ -380,24 +366,20 @@ describe("SettingsModal", () => { .mockResolvedValueOnce({ provider: null, state: "stopped", url: null, lastError: null }) .mockResolvedValueOnce({ provider: "tailscale", state: "running", url: "https://tail.example", lastError: null }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + await renderModalSection("remote", "Remote Access"); - await user.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); expect(screen.getByLabelText("Tailscale")).toBeChecked(); - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); expect(await screen.findByText("https://tail.example", { selector: ".remote-status-url" })).toBeInTheDocument(); }); it("shows cloudflared available indicator when Cloudflare is selected and cloudflared is installed", async () => { mockFetchRemoteStatus.mockResolvedValue({ provider: "cloudflare", state: "stopped", url: null, lastError: null, cloudflaredAvailable: true }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Cloudflare")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); expect(await screen.findByText("cloudflared is installed")).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Install cloudflared" })).not.toBeInTheDocument(); @@ -406,10 +388,8 @@ describe("SettingsModal", () => { it("shows install button when Cloudflare is selected and cloudflared is not available", async () => { mockFetchRemoteStatus.mockResolvedValue({ provider: "cloudflare", state: "stopped", url: null, lastError: null, cloudflaredAvailable: false }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Cloudflare")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); expect(await screen.findByText("cloudflared is not installed")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Install cloudflared" })).toBeInTheDocument(); @@ -423,13 +403,11 @@ describe("SettingsModal", () => { .mockResolvedValueOnce({ provider: "cloudflare", state: "stopped", url: null, lastError: null, cloudflaredAvailable: true }); mockInstallCloudflared.mockResolvedValueOnce({ success: true, command: "brew install cloudflared" }); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Cloudflare")); + await renderModalSection("remote", "Remote Access", { addToast }); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); const installButton = await screen.findByRole("button", { name: "Install cloudflared" }); - await user.click(installButton); + await settingsModalUser.click(installButton); await waitFor(() => { expect(mockInstallCloudflared).toHaveBeenCalledWith(undefined); @@ -444,12 +422,10 @@ describe("SettingsModal", () => { mockFetchRemoteStatus.mockResolvedValue({ provider: "cloudflare", state: "stopped", url: null, lastError: null, cloudflaredAvailable: false }); mockInstallCloudflared.mockResolvedValueOnce({ success: false, command: "brew install cloudflared", error: "Command failed" }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Cloudflare")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Cloudflare")); - await user.click(await screen.findByRole("button", { name: "Install cloudflared" })); + await settingsModalUser.click(await screen.findByRole("button", { name: "Install cloudflared" })); expect(await screen.findByText("Command failed")).toBeInTheDocument(); }); @@ -461,14 +437,12 @@ describe("SettingsModal", () => { .mockResolvedValueOnce({ provider: "tailscale", state: "running", url: "https://tail.example", lastError: null }) .mockResolvedValueOnce({ provider: "tailscale", state: "error", url: null, lastError: "Tunnel crashed" }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + await renderModalSection("remote", "Remote Access"); - await user.click(screen.getByLabelText("Tailscale")); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); expect(screen.getByRole("button", { name: "Start Tunnel" })).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "Start Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Start Tunnel" })); await waitFor(() => { expect(mockUpdateRemoteSettings).toHaveBeenCalled(); expect(mockStartRemoteTunnel).toHaveBeenCalledTimes(1); @@ -477,11 +451,11 @@ describe("SettingsModal", () => { expect(await screen.findByRole("button", { name: "Stop Tunnel" })).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "Stop Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Stop Tunnel" })); await waitFor(() => { expect(mockStopRemoteTunnel).toHaveBeenCalledTimes(1); }); - await user.click(screen.getByRole("button", { name: "Stop Tunnel" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Stop Tunnel" })); await waitFor(() => { expect(mockStopRemoteTunnel).toHaveBeenCalledTimes(2); }); @@ -497,9 +471,7 @@ describe("SettingsModal", () => { externalTunnel: { provider: "tailscale", url: "https://machine.ts.net/" }, }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); + await renderModalSection("remote", "Remote Access"); expect(await screen.findByText("External tailscale tunnel detected")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Start Fresh" })).toBeInTheDocument(); @@ -515,12 +487,10 @@ describe("SettingsModal", () => { externalTunnel: { provider: "tailscale", url: "https://machine.ts.net/" }, }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Tailscale")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); - await user.click(await screen.findByRole("button", { name: "Start Fresh" })); + await settingsModalUser.click(await screen.findByRole("button", { name: "Start Fresh" })); await waitFor(() => { expect(mockKillExternalTunnel).toHaveBeenCalledWith(undefined); @@ -530,13 +500,11 @@ describe("SettingsModal", () => { it("regenerates persistent token and surfaces success feedback without exposing raw token text", async () => { const addToast = vi.fn(); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Tailscale")); + await renderModalSection("remote", "Remote Access", { addToast }); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); await openAdvancedSettings(); - await user.click(screen.getByRole("button", { name: "Regenerate persistent token" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Regenerate persistent token" })); await waitFor(() => { expect(mockRegenerateRemotePersistentToken).toHaveBeenCalledWith(undefined); @@ -558,23 +526,21 @@ describe("SettingsModal", () => { expiresAt: shortLivedExpiry, }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Tailscale")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); await openAdvancedSettings(); - await user.selectOptions(screen.getByLabelText("Auth link token type"), "short-lived"); + await settingsModalUser.selectOptions(screen.getByLabelText("Auth link token type"), "short-lived"); const ttlInput = screen.getByLabelText("Short-lived TTL (ms)") as HTMLInputElement; fireEvent.change(ttlInput, { target: { value: "120000" } }); - await user.click(screen.getByRole("button", { name: "Generate short-lived token" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Generate short-lived token" })); await waitFor(() => { expect(mockGenerateShortLivedRemoteToken).toHaveBeenCalledWith(120000, undefined); }); fireEvent.change(ttlInput, { target: { value: "120000" } }); - await user.click(screen.getByRole("button", { name: "Show URL" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Show URL" })); await waitFor(() => { expect(mockFetchRemoteUrl).toHaveBeenLastCalledWith({ projectId: undefined, @@ -601,19 +567,17 @@ describe("SettingsModal", () => { format: "text", }); - renderModal(); - await waitForSettingsModalReady(); - await openRemoteSection(); - await user.click(screen.getByLabelText("Tailscale")); + await renderModalSection("remote", "Remote Access"); + await settingsModalUser.click(screen.getByLabelText("Tailscale")); await openAdvancedSettings(); - await user.click(screen.getByRole("button", { name: "Generate QR" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Generate QR" })); await waitFor(() => { expect(mockFetchRemoteQr).toHaveBeenNthCalledWith(1, "image/svg", expect.objectContaining({ tokenType: "persistent" })); }); expect(await screen.findByRole("img", { name: "Remote access QR code" })).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: "Generate QR" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Generate QR" })); await waitFor(() => { expect(screen.queryByRole("img", { name: "Remote access QR code" })).not.toBeInTheDocument(); }); @@ -624,20 +588,9 @@ describe("SettingsModal", () => { describe("Notifications provider cards", () => { - let user: ReturnType; - - beforeEach(() => { - user = userEvent.setup({ delay: null, pointerEventsCheck: 0 }); - }); - - const openNotificationsSection = async () => { - await user.click(await screen.findByRole("button", { name: /Notifications/ })); - }; - describe("with default Notifications render", () => { beforeEach(async () => { - renderModal({ initialSection: "notifications" }); - await screen.findByRole("heading", { name: "Notifications" }); + await renderModalSection("notifications", "Notifications"); }); it("shows ntfy and webhook provider cards in notifications section", () => { @@ -652,11 +605,11 @@ describe("SettingsModal", () => { expect(modeSelect.value).toBe("sticky-only"); expect(delayInput.value).toBe("30000"); - await user.selectOptions(modeSelect, "all"); + await settingsModalUser.selectOptions(modeSelect, "all"); expect(delayInput).toBeDisabled(); - await user.selectOptions(modeSelect, "sticky-only"); + await settingsModalUser.selectOptions(modeSelect, "sticky-only"); fireEvent.change(delayInput, { target: { value: "45000" } }); - await user.click(screen.getByRole("button", { name: "Save" })); + await settingsModalUser.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => { expect(mockUpdateGlobalSettings).toHaveBeenCalledWith( @@ -670,8 +623,8 @@ describe("SettingsModal", () => { it("persists terminal-only selection on save", async () => { const modeSelect = screen.getByLabelText("Failure notification mode") as HTMLSelectElement; - await user.selectOptions(modeSelect, "terminal-only"); - await user.click(screen.getByRole("button", { name: "Save" })); + await settingsModalUser.selectOptions(modeSelect, "terminal-only"); + await settingsModalUser.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => { expect(mockUpdateGlobalSettings).toHaveBeenCalledWith( @@ -683,11 +636,11 @@ describe("SettingsModal", () => { }); it("calls testNotification with webhook provider ID when webhook test button clicked", async () => { - await user.click(screen.getByLabelText("Webhook notifications")); + await settingsModalUser.click(screen.getByLabelText("Webhook notifications")); fireEvent.change(screen.getByLabelText("Webhook URL"), { target: { value: "https://hooks.example.com/test" } }); const webhookCard = screen.getByText("Webhook").closest(".notification-provider-card") as HTMLElement; - await user.click(within(webhookCard).getByRole("button", { name: /Test notification/ })); + await settingsModalUser.click(within(webhookCard).getByRole("button", { name: /Test notification/ })); await waitFor(() => { expect(mockTestNotification).toHaveBeenCalledWith( @@ -706,9 +659,7 @@ describe("SettingsModal", () => { ...defaultSettings, failureNotificationMode: "terminal-only", }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); const modeSelect = screen.getByLabelText("Failure notification mode") as HTMLSelectElement; const delayInput = screen.getByLabelText("Failure notification delay (ms)") as HTMLInputElement; @@ -729,16 +680,14 @@ describe("SettingsModal", () => { provider: "webhook", initial: defaultSettings, enable: async () => { - await user.click(screen.getByLabelText("Webhook notifications")); + await settingsModalUser.click(screen.getByLabelText("Webhook notifications")); }, visibleLabel: "Webhook URL", hiddenLabel: "ntfy Topic", }, ])("shows $provider fields when enabled and hides opposite provider fields", async ({ initial, enable, visibleLabel, hiddenLabel }) => { mockFetchSettings.mockResolvedValueOnce(initial); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); await enable(); expect(screen.getByLabelText(visibleLabel)).toBeInTheDocument(); @@ -767,9 +716,7 @@ describe("SettingsModal", () => { "oauth-token-expired", ], }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); expect(screen.getByLabelText("Fallback model used (recovered)")).toBeInTheDocument(); expect(screen.getByLabelText("Agent created a task")).toBeInTheDocument(); @@ -783,7 +730,7 @@ describe("SettingsModal", () => { expect(agentToAgentNtfy.checked).toBe(true); expect(roomMessageNtfy.checked).toBe(true); - await user.click(screen.getByLabelText("Webhook notifications")); + await settingsModalUser.click(screen.getByLabelText("Webhook notifications")); expect(screen.getAllByLabelText("Fallback model used (recovered)").length).toBeGreaterThan(0); expect(screen.getAllByLabelText("Agent created a task").length).toBeGreaterThan(0); expect(screen.getAllByLabelText("DREAMS.md entry added").length).toBeGreaterThan(0); @@ -799,13 +746,11 @@ describe("SettingsModal", () => { it("calls testNotification with ntfy provider ID when ntfy test button clicked", async () => { mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: true, ntfyTopic: "test-topic" }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); - await user.click(screen.getByText("Advanced")); + await renderModalSection("notifications", "Notifications"); + await settingsModalUser.click(screen.getByText("Advanced")); fireEvent.change(screen.getByLabelText("Access token (optional)"), { target: { value: "secret-token" } }); - await user.click(screen.getByRole("button", { name: /Test notification/ })); + await settingsModalUser.click(screen.getByRole("button", { name: /Test notification/ })); await waitFor(() => { expect(mockTestNotification).toHaveBeenCalledWith( @@ -822,16 +767,14 @@ describe("SettingsModal", () => { it("sends unsaved ntfy form config before saving", async () => { mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: false, ntfyTopic: undefined }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); - await user.click(screen.getByLabelText("Enable")); - await user.type(screen.getByLabelText("ntfy Topic"), "fresh-topic"); - await user.click(screen.getByText("Advanced")); - await user.type(screen.getByLabelText("Custom ntfy server URL (optional)"), "https://ntfy.override.example//"); - await user.type(screen.getByLabelText("Access token (optional)"), "override-token"); - await user.click(screen.getByRole("button", { name: /Test notification/ })); + await settingsModalUser.click(screen.getByLabelText("Enable")); + fireEvent.change(screen.getByLabelText("ntfy Topic"), { target: { value: "fresh-topic" } }); + await settingsModalUser.click(screen.getByText("Advanced")); + fireEvent.change(screen.getByLabelText("Custom ntfy server URL (optional)"), { target: { value: "https://ntfy.override.example//" } }); + fireEvent.change(screen.getByLabelText("Access token (optional)"), { target: { value: "override-token" } }); + await settingsModalUser.click(screen.getByRole("button", { name: /Test notification/ })); await waitFor(() => { expect(mockTestNotification).toHaveBeenCalledWith( @@ -851,20 +794,18 @@ describe("SettingsModal", () => { it("keeps ntfy test disabled until the current form has a valid topic", async () => { mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: false, ntfyTopic: undefined }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); - await user.click(screen.getByLabelText("Enable")); + await settingsModalUser.click(screen.getByLabelText("Enable")); const testButton = screen.getByRole("button", { name: /Test notification/ }); expect(testButton).toBeDisabled(); - await user.type(screen.getByLabelText("ntfy Topic"), "bad topic!"); + fireEvent.change(screen.getByLabelText("ntfy Topic"), { target: { value: "bad topic!" } }); expect(testButton).toBeDisabled(); expect(mockTestNotification).not.toHaveBeenCalled(); - await user.clear(screen.getByLabelText("ntfy Topic")); - await user.type(screen.getByLabelText("ntfy Topic"), "fresh-topic"); + fireEvent.change(screen.getByLabelText("ntfy Topic"), { target: { value: "" } }); + fireEvent.change(screen.getByLabelText("ntfy Topic"), { target: { value: "fresh-topic" } }); expect(testButton).toBeEnabled(); }); @@ -875,13 +816,11 @@ describe("SettingsModal", () => { ntfyTopic: "test-topic", ntfyAccessToken: "saved-token", }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); - await user.click(screen.getByText("Advanced")); + await renderModalSection("notifications", "Notifications"); + await settingsModalUser.click(screen.getByText("Advanced")); const tokenInput = screen.getByLabelText("Access token (optional)"); - await user.clear(tokenInput); - await user.click(screen.getByRole("button", { name: "Save" })); + await settingsModalUser.clear(tokenInput); + await settingsModalUser.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => { expect(mockUpdateGlobalSettings).toHaveBeenCalledWith( @@ -893,11 +832,9 @@ describe("SettingsModal", () => { it("calls testNotification with ntfy message-event config when message test button clicked", async () => { const addToast = vi.fn(); mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: true, ntfyTopic: "test-topic" }); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications", { addToast }); - await user.click(screen.getByRole("button", { name: /Test message inbox/ })); + await settingsModalUser.click(screen.getByRole("button", { name: /Test message inbox/ })); await waitFor(() => { expect(mockTestNotification).toHaveBeenCalledWith( @@ -921,11 +858,9 @@ describe("SettingsModal", () => { it("calls testNotification with ntfy room-event config when room test button clicked", async () => { const addToast = vi.fn(); mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: true, ntfyTopic: "test-topic" }); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications", { addToast }); - await user.click(screen.getByRole("button", { name: /Test room reply/ })); + await settingsModalUser.click(screen.getByRole("button", { name: /Test room reply/ })); await waitFor(() => { expect(mockTestNotification).toHaveBeenCalledWith( @@ -949,11 +884,9 @@ describe("SettingsModal", () => { const addToast = vi.fn(); mockTestNotification.mockResolvedValueOnce({ success: false, error: "boom" }); mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, ntfyEnabled: true, ntfyTopic: "test-topic" }); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications", { addToast }); - await user.click(screen.getByRole("button", { name: /Test room reply/ })); + await settingsModalUser.click(screen.getByRole("button", { name: /Test room reply/ })); await waitFor(() => { expect(addToast).toHaveBeenCalledWith("Failed to send room reply test", "error"); @@ -969,9 +902,7 @@ describe("SettingsModal", () => { ntfyEvents: ["in-review", "failed"], }); - renderModal(); - await waitForSettingsModalReady(); - await openNotificationsSection(); + await renderModalSection("notifications", "Notifications"); expect(screen.getByLabelText("ntfy Topic")).toHaveValue("my-existing-topic"); const inReview = screen.getByLabelText("Task completed (in-review)") as HTMLInputElement; @@ -1033,8 +964,8 @@ describe("SettingsModal", () => { await openScheduledEvalsSection(); fireEvent.change(screen.getByLabelText("Interval (ms)"), { target: { value: "120000" } }); - await settingsModalUser.type(screen.getByLabelText("Evaluator Provider"), "openai"); - await settingsModalUser.type(screen.getByLabelText("Evaluator Model"), "gpt-5"); + fireEvent.change(screen.getByLabelText("Evaluator Provider"), { target: { value: "openai" } }); + fireEvent.change(screen.getByLabelText("Evaluator Model"), { target: { value: "gpt-5" } }); await settingsModalUser.selectOptions(screen.getByLabelText("Follow-up Policy"), "auto-create"); fireEvent.change(screen.getByLabelText("Retention (days)"), { target: { value: "14" } }); await settingsModalUser.click(screen.getByText("Save")); @@ -1094,8 +1025,7 @@ describe("SettingsModal", () => { describe("memory backups settings", () => { it("renders memory backup fields with defaults and saves changes", async () => { - renderModal({ initialSection: "backups" }); - await waitForSettingsModalReady(); + await renderModalSection("backups", "Memory Backups"); expect(screen.getByRole("heading", { name: "Memory Backups" })).toBeInTheDocument(); expect(screen.getByLabelText("Memory Backup Schedule (Cron)")).toHaveValue("0 3 * * *"); @@ -1455,11 +1385,6 @@ describe("SettingsModal", () => { }); describe("memory dream trigger", () => { - const openMemorySection = async () => { - const [memorySectionButton] = await screen.findAllByRole("button", { name: /^Memory$/i }); - await settingsModalUser.click(memorySectionButton); - }; - it("shows Dream Now button when dreams are enabled", async () => { mockFetchSettings.mockResolvedValueOnce({ ...defaultSettings, @@ -1468,9 +1393,7 @@ describe("SettingsModal", () => { memoryDreamsSchedule: "0 4 * * *", }); - renderModal(); - await waitForSettingsModalReady(); - await openMemorySection(); + await renderModalSection("memory", "Memory"); expect(await screen.findByRole("button", { name: "Dream Now" })).toBeInTheDocument(); }); @@ -1484,9 +1407,7 @@ describe("SettingsModal", () => { }); mockTriggerMemoryDreams.mockResolvedValueOnce({ success: true, summary: "done" }); - renderModal({ addToast }); - await waitForSettingsModalReady(); - await openMemorySection(); + await renderModalSection("memory", "Memory", { addToast }); await settingsModalUser.click(await screen.findByRole("button", { name: "Dream Now" })); @@ -1497,9 +1418,7 @@ describe("SettingsModal", () => { }); it("hides Dream Now button when dreams are disabled", async () => { - renderModal(); - await waitForSettingsModalReady(); - await openMemorySection(); + await renderModalSection("memory", "Memory"); expect(screen.queryByRole("button", { name: "Dream Now" })).not.toBeInTheDocument(); }); diff --git a/packages/dashboard/app/components/__tests__/SettingsModal.test-harness.tsx b/packages/dashboard/app/components/__tests__/SettingsModal.test-harness.tsx index b7c5f1d165..7e30284e96 100644 --- a/packages/dashboard/app/components/__tests__/SettingsModal.test-harness.tsx +++ b/packages/dashboard/app/components/__tests__/SettingsModal.test-harness.tsx @@ -14,6 +14,11 @@ files (general / models-auth / scheduling-merge / remote-notifications) so the d component shard parallelizes them across workers instead of running one ~61s sequential file (FN-5048 feedback-loop velocity). vi.mock factories stay in each test file (they only apply per test module) and delegate to the mock fns + env setup exported here. + +FNXC:DashboardTests 2026-06-26-19:05: +SettingsModal's split files still pay full render and sidebar-navigation cost per case. +Use section-targeted harness rendering for tests that do not exercise navigation, preserving +all assertions while avoiding inactive-section setup and real-timer polling in the hot path. */ export const settingsModalCss = fs.readFileSync(path.resolve(__dirname, "../SettingsModal.css"), "utf8"); @@ -135,6 +140,17 @@ export async function waitForSettingsModalReady() { expect(screen.queryByText("Loading…")).not.toBeInTheDocument(); } +export async function renderModalSection( + initialSection: ComponentProps["initialSection"], + headingName: string | RegExp, + props: Partial> = {}, +) { + const result = renderModal({ initialSection, ...props }); + await waitForSettingsModalReady(); + expect(screen.getByRole("heading", { name: headingName })).toBeInTheDocument(); + return result; +} + /* FNXC:SettingsModalTests 2026-06-25-06:45: The split SettingsModal files remain the dashboard's slowest feedback-loop tests when they use default user-event delays. Keep one no-delay user instance per test and skip repeated pointer-events tree walks so interaction coverage stays intact without artificial timer overhead. diff --git a/scripts/test-velocity-history.json b/scripts/test-velocity-history.json index 37329d68ba..dd36bae8ba 100644 --- a/scripts/test-velocity-history.json +++ b/scripts/test-velocity-history.json @@ -1018,6 +1018,125 @@ "measurementFailures": [], "timingSnapshotCapturedAt": "2026-06-25T05:45:08.116Z", "timingNotes": [] + }, + { + "capturedAt": "2026-06-27T02:02:13.530Z", + "gateMs": 8120, + "bootSmokeMs": 17554, + "testMs": null, + "quarantineCount": 0, + "slowestTop20": [ + { + "file": "packages/dashboard/app/components/__tests__/SettingsModal.test.tsx", + "ms": 60500, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/src/__tests__/insights-routes.test.ts", + "ms": 26500, + "package": "@fusion/dashboard" + }, + { + "file": "packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts", + "ms": 24700, + "package": "@fusion/engine" + }, + { + "file": "packages/dashboard/app/components/__tests__/ChatView.test.tsx", + "ms": 24400, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/src/__tests__/workflow-routes.test.ts", + "ms": 22000, + "package": "@fusion/dashboard" + }, + { + "file": "packages/core/src/__tests__/db.test.ts", + "ms": 21200, + "package": "@fusion/core" + }, + { + "file": "packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx", + "ms": 16900, + "package": "@fusion/dashboard" + }, + { + "file": "packages/core/src/__tests__/mission-store.test.ts", + "ms": 16000, + "package": "@fusion/core" + }, + { + "file": "packages/cli/src/__tests__/extension.test.ts", + "ms": 15700, + "package": "@runfusion/fusion" + }, + { + "file": "packages/dashboard/app/components/__tests__/AgentPromptsManager.test.tsx", + "ms": 14800, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/app/components/__tests__/App.test.tsx", + "ms": 14600, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx", + "ms": 14100, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx", + "ms": 13700, + "package": "@fusion/dashboard" + }, + { + "file": "packages/dashboard/src/__tests__/routes-auth.test.ts", + "ms": 13600, + "package": "@fusion/dashboard" + }, + { + "file": "packages/core/src/__tests__/agent-store.test.ts", + "ms": 13400, + "package": "@fusion/core" + }, + { + "file": "packages/engine/src/__tests__/workspace-merger-idempotency.test.ts", + "ms": 12700, + "package": "@fusion/engine" + }, + { + "file": "packages/engine/src/__tests__/self-healing-workspace.test.ts", + "ms": 11800, + "package": "@fusion/engine" + }, + { + "file": "packages/engine/src/__tests__/pr-response-run.test.ts", + "ms": 11600, + "package": "@fusion/engine" + }, + { + "file": "packages/dashboard/app/components/__tests__/ListView.test.tsx", + "ms": 11300, + "package": "@fusion/dashboard" + }, + { + "file": "plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts", + "ms": 11000, + "package": "@fusion-plugin-examples/compound-engineering" + } + ], + "measurementFailures": [ + { + "label": "Changed-only tests (`pnpm test`)", + "status": "exit 1 after 31.0s" + } + ], + "timingSnapshotCapturedAt": "2026-06-25T05:45:08.116Z", + "timingNotes": [ + "2 of the listed slowest files no longer exist at the recorded paths: `packages/dashboard/app/components/__tests__/SettingsModal.test.tsx`, `packages/dashboard/app/components/__tests__/ChatView.test.tsx`. Refresh scripts/test-timings.json before choosing a slow-test rewrite." + ] } ] }