diff --git a/packages/dashboard/app/components/command-center/areas/__tests__/areas.github-signals.test.tsx b/packages/dashboard/app/components/command-center/areas/__tests__/areas.github-signals.test.tsx
new file mode 100644
index 0000000000..2cfe828c3a
--- /dev/null
+++ b/packages/dashboard/app/components/command-center/areas/__tests__/areas.github-signals.test.tsx
@@ -0,0 +1,406 @@
+/*
+FNXC:CommandCenter 2026-06-25-00:00:
+FN-7044 extracts the GitHub and Signals Command Center area suites from areas.test.tsx so each focused test file remains under the 2000-line hard cap while preserving every assertion.
+*/
+import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
+import { render, screen, fireEvent, waitFor, within } from "@testing-library/react";
+
+// Mock the api() helper so the areas fetch deterministic fixtures. Keep these Vitest hoisted mocks inline in each split suite to avoid TDZ failures.
+const mocks = vi.hoisted(() => ({
+ api: vi.fn(),
+ backfillGithubSourceIssueClosedAt: vi.fn(),
+ backfillCommitAssociationDiffStats: vi.fn(),
+ fetchOrgTree: vi.fn(),
+ fetchExecutorStats: vi.fn(),
+ fetchSettings: vi.fn(),
+ updateSettings: vi.fn(),
+ toggleEnginePause: vi.fn(),
+ appSettings: { globalPaused: false, enginePaused: false },
+}));
+const apiMock = mocks.api;
+const backfillGithubSourceIssueClosedAtMock = mocks.backfillGithubSourceIssueClosedAt;
+const backfillCommitAssociationDiffStatsMock = mocks.backfillCommitAssociationDiffStats;
+const fetchOrgTreeMock = mocks.fetchOrgTree;
+const fetchExecutorStatsMock = mocks.fetchExecutorStats;
+const toggleEnginePauseMock = mocks.toggleEnginePause;
+const appSettingsMock = mocks.appSettings;
+vi.mock("../../../../api/legacy", () => ({
+ api: (path: string, opts?: RequestInit) => mocks.api(path, opts),
+ apiBackfillGithubSourceIssueClosedAt: (options?: { offset?: number; limit?: number }, projectId?: string) =>
+ mocks.backfillGithubSourceIssueClosedAt(options, projectId),
+ backfillCommitAssociationDiffStats: (options?: { dryRun?: boolean }, projectId?: string) =>
+ mocks.backfillCommitAssociationDiffStats(options, projectId),
+ fetchOrgTree: mocks.fetchOrgTree,
+ fetchExecutorStats: mocks.fetchExecutorStats,
+ fetchSettings: mocks.fetchSettings,
+ updateSettings: mocks.updateSettings,
+}));
+
+vi.mock("../../../../hooks/useAppSettings", () => ({
+ useAppSettings: () => ({
+ globalPaused: mocks.appSettings.globalPaused,
+ enginePaused: mocks.appSettings.enginePaused,
+ toggleEnginePause: mocks.toggleEnginePause,
+ }),
+}));
+
+import { GithubArea } from "../GithubArea";
+import { SignalsArea } from "../SignalsArea";
+import { customRange, githubFixture, range7d } from "./areas.test-harness";
+
+beforeEach(() => {
+ apiMock.mockReset();
+ backfillGithubSourceIssueClosedAtMock.mockReset();
+ backfillCommitAssociationDiffStatsMock.mockReset();
+ fetchOrgTreeMock.mockReset();
+ fetchOrgTreeMock.mockResolvedValue([]);
+ fetchExecutorStatsMock.mockReset();
+ fetchExecutorStatsMock.mockResolvedValue({
+ globalPause: false,
+ enginePaused: false,
+ maxConcurrent: 2,
+ lastActivityAt: "2026-06-19T12:00:00.000Z",
+ });
+ mocks.fetchSettings.mockReset();
+ mocks.fetchSettings.mockResolvedValue({ heartbeatMultiplier: 1 });
+ mocks.updateSettings.mockReset();
+ mocks.updateSettings.mockResolvedValue({});
+ toggleEnginePauseMock.mockReset();
+ appSettingsMock.globalPaused = false;
+ appSettingsMock.enginePaused = false;
+});
+
+afterEach(() => {
+ vi.useRealTimers();
+});
+
+describe("GithubArea", () => {
+ it("renders filed/fixed/net stats, daily trend, and by-repo bars", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ render();
+
+ await screen.findByTestId("cc-area-github");
+ expect(screen.getByTestId("cc-github-filed").textContent).toContain("5");
+ expect(screen.getByTestId("cc-github-fixed").textContent).toContain("3");
+ expect(screen.getByTestId("cc-github-net").textContent).toContain("2");
+ expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
+ expect(screen.getByTestId("cc-github-line")).toBeTruthy();
+ expect(screen.getByRole("img", { name: "Filed vs fixed share" })).toBeTruthy();
+ expect(screen.getByRole("img", { name: "Filed vs fixed line" })).toBeTruthy();
+ expect(screen.getByTestId("cc-github-daily-trend")).toBeTruthy();
+ expect(screen.getByRole("img", { name: "Filed" })).toBeTruthy();
+ expect(screen.getByRole("img", { name: "Fixed" })).toBeTruthy();
+ const repoChart = screen.getByRole("list", { name: "By repository" });
+ expect(within(repoChart).getByText("acme/alpha")).toBeTruthy();
+ expect(within(repoChart).getByLabelText("acme/alpha: 4 filed / 1 fixed")).toBeTruthy();
+ });
+
+ it("renders resolved issues with safe outbound links and approximation labels", async () => {
+ apiMock.mockResolvedValue({
+ ...githubFixture(),
+ resolved: [
+ {
+ taskId: "FN-100",
+ taskTitle: "Fix alpha crash",
+ repo: "acme/alpha",
+ issueNumber: 123,
+ url: "https://github.com/acme/alpha/issues/123",
+ resolvedAt: "2026-06-10T12:34:56.000Z",
+ resolvedAtExact: true,
+ },
+ {
+ taskId: "FN-101",
+ taskTitle: "Patch unknown import",
+ repo: "(unknown)",
+ issueNumber: null,
+ url: null,
+ resolvedAt: "2026-06-09T08:00:00.000Z",
+ resolvedAtExact: false,
+ },
+ ],
+ });
+
+ render();
+
+ const section = await screen.findByTestId("cc-github-resolved");
+ expect(section.textContent).toContain("Resolved issues");
+ expect(section.textContent).toContain("acme/alpha#123");
+ expect(section.textContent).toContain("Fix alpha crash");
+ expect(section.textContent).toContain("FN-100");
+ expect(section.textContent).toContain("(unknown)");
+ expect(section.textContent).toContain("Patch unknown import");
+ expect(section.textContent).toContain("approx");
+ expect(section.textContent).toContain("2026");
+
+ const linkedIssue = within(section).getByRole("link", { name: "Open GitHub issue acme/alpha#123" });
+ expect(linkedIssue.getAttribute("href")).toBe("https://github.com/acme/alpha/issues/123");
+ expect(linkedIssue.getAttribute("target")).toBe("_blank");
+ expect(linkedIssue.getAttribute("rel")).toBe("noopener noreferrer");
+ expect(within(section).queryByRole("link", { name: /unknown/i })).toBeNull();
+ });
+
+ it("omits the resolved issues section for an empty resolved list", async () => {
+ apiMock.mockResolvedValue({ ...githubFixture(), resolved: [] });
+
+ render();
+
+ await screen.findByTestId("cc-area-github");
+ expect(screen.queryByTestId("cc-github-resolved")).toBeNull();
+ expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
+ });
+
+ it("renders the empty state without empty chart shells", async () => {
+ apiMock.mockResolvedValue({ ...githubFixture(), filed: 0, fixed: 0, net: 0, daily: [], byRepo: [] });
+ render();
+
+ await screen.findByTestId("cc-area-github");
+ expect(screen.getByTestId("cc-area-github").textContent).toContain("No GitHub issue activity");
+ expect(screen.getByTestId("cc-github-backfill-button")).toBeTruthy();
+ expect(screen.queryByTestId("cc-github-pie")).toBeNull();
+ expect(screen.queryByTestId("cc-github-line")).toBeNull();
+ expect(screen.queryByTestId("cc-github-daily-trend")).toBeNull();
+ expect(screen.queryByTestId("cc-github-by-repo")).toBeNull();
+ });
+
+ it("renders loading and error states", async () => {
+ apiMock.mockImplementationOnce(() => new Promise(() => undefined));
+ const { unmount } = render();
+ expect(screen.getByTestId("cc-area-github-loading")).toBeTruthy();
+ expect(screen.queryByTestId("cc-github-pie")).toBeNull();
+ expect(screen.queryByTestId("cc-github-line")).toBeNull();
+ unmount();
+
+ apiMock.mockRejectedValueOnce(new Error("github failed"));
+ render();
+ await screen.findByTestId("cc-area-github-error");
+ expect(screen.getByTestId("cc-area-github-error").textContent).toContain("github failed");
+ expect(screen.queryByTestId("cc-github-pie")).toBeNull();
+ expect(screen.queryByTestId("cc-github-line")).toBeNull();
+ });
+
+ it("handles undefined chart arrays and zero values without NaN output", async () => {
+ apiMock.mockResolvedValue({ ...githubFixture(), filed: 1, fixed: 0, net: 1, daily: undefined, byRepo: undefined });
+ render();
+
+ await screen.findByTestId("cc-area-github");
+ expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
+ expect(screen.queryByTestId("cc-github-line")).toBeNull();
+ expect(screen.queryByTestId("cc-github-daily-trend")).toBeNull();
+ expect(screen.queryByTestId("cc-github-by-repo")).toBeNull();
+ expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
+ });
+
+ it("keeps GitHub recharts safe for single-item and non-finite daily data", async () => {
+ apiMock.mockResolvedValue({
+ ...githubFixture(),
+ filed: 1,
+ fixed: 1,
+ net: 0,
+ daily: [{ date: "2026-06-08", filed: Number.NaN, fixed: Number.POSITIVE_INFINITY }],
+ byRepo: [{ repo: "acme/broken", filed: Number.NaN, fixed: -1 }],
+ });
+ render();
+
+ await screen.findByTestId("cc-area-github");
+ expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
+ expect(screen.getByTestId("cc-github-line")).toBeTruthy();
+ expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
+ expect(screen.getByTestId("cc-area-github").textContent).not.toContain("Infinity");
+ });
+
+ it("rejects an inverted custom range client-side without fetching", async () => {
+ render();
+ await waitFor(() => expect(apiMock).not.toHaveBeenCalled());
+ });
+
+ it("runs a single backfill batch and renders accumulated result counts", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
+ scanned: 4,
+ filled: 2,
+ skipped: 1,
+ errors: 0,
+ hasMore: false,
+ });
+
+ render();
+ await screen.findByTestId("cc-area-github");
+
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ await screen.findByText(/Backfill complete/i);
+ expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledWith({ offset: 0, limit: 100 }, undefined);
+ const result = screen.getByTestId("cc-github-backfill-result");
+ expect(result.textContent).toContain("Scanned 4, filled 2, skipped 1, errors 0");
+ });
+
+ it("paginates multi-batch backfills with advancing offsets and summed counts", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock
+ .mockResolvedValueOnce({ scanned: 100, filled: 4, skipped: 90, errors: 1, hasMore: true })
+ .mockResolvedValueOnce({ scanned: 25, filled: 3, skipped: 22, errors: 0, hasMore: false });
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ await waitFor(() => expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(2));
+ expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenNthCalledWith(1, { offset: 0, limit: 100 }, undefined);
+ expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenNthCalledWith(2, { offset: 100, limit: 100 }, undefined);
+ const result = await screen.findByTestId("cc-github-backfill-result");
+ expect(result.textContent).toContain("Scanned 125, filled 7, skipped 112, errors 1");
+ expect(result.className).toContain("cc-github-backfill-status--error");
+ });
+
+ it("shows the all-zero backfill as nothing to backfill instead of an error", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
+ scanned: 0,
+ filled: 0,
+ skipped: 0,
+ errors: 0,
+ hasMore: false,
+ });
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ const result = await screen.findByTestId("cc-github-backfill-result");
+ expect(result.textContent).toContain("Nothing to backfill");
+ expect(result.className).not.toContain("cc-github-backfill-status--error");
+ });
+
+ it("surfaces nonzero backfill error counts without throwing", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
+ scanned: 8,
+ filled: 1,
+ skipped: 5,
+ errors: 2,
+ hasMore: false,
+ });
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ const result = await screen.findByTestId("cc-github-backfill-result");
+ expect(result.textContent).toContain("errors 2");
+ expect(result.className).toContain("cc-github-backfill-status--error");
+ });
+
+ it("captures endpoint failures in local error UI", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock.mockRejectedValueOnce(new Error("endpoint failed"));
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ const result = await screen.findByTestId("cc-github-backfill-result");
+ expect(result.textContent).toContain("endpoint failed");
+ expect(result.className).toContain("cc-github-backfill-status--error");
+ });
+
+ it("disables and guards the button while a backfill is in flight", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ let resolveBackfill: ((value: { scanned: number; filled: number; skipped: number; errors: number; hasMore: boolean }) => void) | null = null;
+ backfillGithubSourceIssueClosedAtMock.mockImplementationOnce(
+ () => new Promise((resolve) => {
+ resolveBackfill = resolve;
+ }),
+ );
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ const button = screen.getByTestId("cc-github-backfill-button") as HTMLButtonElement;
+ fireEvent.click(button);
+
+ await waitFor(() => expect(button.disabled).toBe(true));
+ fireEvent.click(button);
+ expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(1);
+
+ resolveBackfill?.({ scanned: 1, filled: 1, skipped: 0, errors: 0, hasMore: false });
+ await screen.findByText(/Backfill complete/i);
+ });
+
+ it("stops a pathological always-has-more response at the max iteration guard", async () => {
+ apiMock.mockResolvedValue(githubFixture());
+ backfillGithubSourceIssueClosedAtMock.mockResolvedValue({
+ scanned: 1,
+ filled: 0,
+ skipped: 1,
+ errors: 0,
+ hasMore: true,
+ });
+
+ render();
+ await screen.findByTestId("cc-area-github");
+ fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
+
+ const result = await screen.findByText(/safety limit/i);
+ expect(result.textContent).toContain("safety limit");
+ expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(1000);
+ expect(screen.getByTestId("cc-github-backfill-result").textContent).toContain("Scanned 1000");
+ });
+});
+
+// FN-6684 Mission Control decision: no extra pie/line test here because MissionControlPanel already renders the live SDLC Funnel for its only quantitative distribution; adding a pie would duplicate that affordance.
+describe("SignalsArea", () => {
+ it("renders the empty state (not an error) when the signals endpoint is missing", async () => {
+ apiMock.mockRejectedValue(new Error("API returned HTML instead of JSON (404)"));
+ render();
+ await screen.findByTestId("cc-area-signals-empty");
+ // Must not surface the error UI.
+ expect(screen.queryByTestId("cc-area-signals-error")).toBeNull();
+ expect(screen.queryByTestId("cc-signals-pie")).toBeNull();
+ });
+
+ it("renders signal metrics and status pie when data is present", async () => {
+ apiMock.mockResolvedValue({
+ totalSignals: 8,
+ open: 3,
+ resolved: 5,
+ mttr: { value: 42, unavailable: false },
+ bySource: [{ source: "sentry", count: 8 }],
+ bySeverity: [{ severity: "error", count: 8 }],
+ });
+ render();
+ await screen.findByTestId("cc-area-signals");
+ expect(screen.getByTestId("cc-signals-total").textContent).toContain("8");
+ expect(screen.getByTestId("cc-signals-mttr").textContent).toContain("42");
+ expect(screen.getByTestId("cc-signals-pie")).toBeTruthy();
+ expect(screen.getByRole("img", { name: "Signal status share" })).toBeTruthy();
+ });
+
+ it("keeps signals pie safe for single-item and non-finite source/severity data", async () => {
+ apiMock.mockResolvedValue({
+ totalSignals: 1,
+ open: 1,
+ resolved: 0,
+ mttr: { value: null, unavailable: true },
+ bySource: [{ source: "broken", count: Number.NaN }],
+ bySeverity: [{ severity: "broken", count: Number.POSITIVE_INFINITY }],
+ });
+ render();
+ await screen.findByTestId("cc-area-signals");
+ expect(screen.getByTestId("cc-signals-pie")).toBeTruthy();
+ expect(screen.getByTestId("cc-area-signals").textContent).not.toContain("NaN");
+ expect(screen.getByTestId("cc-area-signals").textContent).not.toContain("Infinity");
+ });
+
+ it("renders settled zero signals without a pie shell", async () => {
+ apiMock.mockResolvedValue({
+ totalSignals: 0,
+ open: 0,
+ resolved: 0,
+ mttr: { value: null, unavailable: true },
+ bySource: [],
+ bySeverity: [],
+ });
+ render();
+ await screen.findByTestId("cc-area-signals-empty");
+ expect(screen.queryByTestId("cc-signals-pie")).toBeNull();
+ });
+});
diff --git a/packages/dashboard/app/components/command-center/areas/__tests__/areas.test-harness.tsx b/packages/dashboard/app/components/command-center/areas/__tests__/areas.test-harness.tsx
new file mode 100644
index 0000000000..28f8c015c0
--- /dev/null
+++ b/packages/dashboard/app/components/command-center/areas/__tests__/areas.test-harness.tsx
@@ -0,0 +1,302 @@
+/*
+FNXC:CommandCenter 2026-06-25-00:00:
+FN-7044 extracted shared Command Center area test fixtures into this harness so focused sibling suites can stay under the 2000-line hard cap without duplicating fixture logic or moving Vitest hoisted mocks.
+*/
+import { expect, vi } from "vitest";
+import { screen, within } from "@testing-library/react";
+import type { OrgTreeNode } from "@fusion/core";
+import type { DateRange } from "../../DateRangePicker";
+
+export const range7d: DateRange = { from: "2026-06-08", to: null, preset: "7d" };
+export const customRange = (from: string, to: string): DateRange => ({ from, to, preset: "custom" });
+
+export function providerIconIn(element: HTMLElement, provider: string): Element | null {
+ return element.querySelector(`.provider-icon[data-provider="${provider}"]`);
+}
+
+export function tokenFixture(totalTokens = 1500) {
+ return {
+ from: "2026-06-08",
+ to: null,
+ groupBy: "model",
+ totals: {
+ inputTokens: Math.round(totalTokens * 0.6),
+ outputTokens: Math.round(totalTokens * 0.3),
+ cachedTokens: Math.round(totalTokens * 0.1),
+ cacheWriteTokens: 0,
+ totalTokens,
+ nTasks: totalTokens > 0 ? 5 : 0,
+ },
+ cost: { usd: 12.5, unavailable: false, stale: false },
+ series: [
+ {
+ bucket: "2026-06-08",
+ inputTokens: 400,
+ outputTokens: 200,
+ cachedTokens: 0,
+ cacheWriteTokens: 0,
+ totalTokens: 600,
+ nTasks: 2,
+ cost: { usd: 4.5, unavailable: false, stale: false },
+ },
+ {
+ bucket: "2026-06-09",
+ inputTokens: 600,
+ outputTokens: 300,
+ cachedTokens: 0,
+ cacheWriteTokens: 0,
+ totalTokens: 900,
+ nTasks: 3,
+ cost: { usd: 8, unavailable: false, stale: false },
+ },
+ ],
+ groups: [
+ {
+ key: "gpt-4o",
+ inputTokens: 600,
+ outputTokens: 300,
+ cachedTokens: 100,
+ cacheWriteTokens: 0,
+ totalTokens: 900,
+ nTasks: 3,
+ cost: { usd: 9.0, unavailable: false, stale: false },
+ },
+ {
+ key: "claude-sonnet",
+ inputTokens: 400,
+ outputTokens: 200,
+ cachedTokens: 100,
+ cacheWriteTokens: 0,
+ totalTokens: 600,
+ nTasks: 2,
+ cost: { usd: 3.5, unavailable: false, stale: false },
+ },
+ ],
+ };
+}
+
+export function githubFixture() {
+ return {
+ from: "2026-06-08",
+ to: null,
+ filed: 5,
+ fixed: 3,
+ net: 2,
+ daily: [
+ { date: "2026-06-08", filed: 2, fixed: 1 },
+ { date: "2026-06-09", filed: 3, fixed: 2 },
+ ],
+ byRepo: [
+ { repo: "acme/alpha", filed: 4, fixed: 1 },
+ { repo: "acme/beta", filed: 1, fixed: 2 },
+ ],
+ resolved: [],
+ };
+}
+
+export function productivityFixture() {
+ return {
+ from: "2026-06-08",
+ to: null,
+ modifiedFiles: 7,
+ commits: 4,
+ pullRequests: 2,
+ byLanguage: [{ language: "TypeScript", count: 7 }],
+ loc: { value: null, unavailable: true },
+ hoursSaved: { value: null, unavailable: true },
+ taskDuration: {
+ completedTasks: 3,
+ averageMs: 90 * 60 * 1000,
+ medianMs: 60 * 60 * 1000,
+ p90Ms: 2 * 60 * 60 * 1000,
+ totalMs: 270 * 60 * 1000,
+ unavailable: false,
+ },
+ };
+}
+
+export function installElementClientWidth(width: number) {
+ return vi.spyOn(HTMLElement.prototype, "clientWidth", "get").mockReturnValue(width);
+}
+
+export function agentNode(id: string, name: string, children: OrgTreeNode[] = [], title = "Team Lead"): OrgTreeNode {
+ return {
+ agent: {
+ id,
+ name,
+ title,
+ role: "executor",
+ state: "idle",
+ createdAt: "2026-06-19T00:00:00.000Z",
+ updatedAt: "2026-06-19T00:00:00.000Z",
+ metadata: {},
+ },
+ children,
+ };
+}
+
+export function emptyTeamFixture() {
+ return {
+ from: null,
+ to: null,
+ totals: {
+ tokens: { inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, totalTokens: 0, nTasks: 0 },
+ cost: { usd: null, unavailable: false, stale: false },
+ filesChanged: 0,
+ tasksCompleted: 0,
+ tasksInProgress: 0,
+ tasksInReview: 0,
+ },
+ agents: [],
+ };
+}
+
+export function populatedTeamFixture(totalTokens = 1500) {
+ return {
+ ...emptyTeamFixture(),
+ totals: {
+ tokens: {
+ inputTokens: Math.round(totalTokens * 0.6),
+ outputTokens: Math.round(totalTokens * 0.3),
+ cachedTokens: Math.round(totalTokens * 0.1),
+ cacheWriteTokens: 0,
+ totalTokens,
+ nTasks: 2,
+ },
+ cost: { usd: 4.25, unavailable: false, stale: false },
+ filesChanged: 7,
+ tasksCompleted: 3,
+ tasksInProgress: 1,
+ tasksInReview: 0,
+ },
+ agents: [
+ {
+ agentId: "agent-alpha",
+ agentName: "Alpha Agent",
+ role: "executor",
+ state: "running",
+ tokens: {
+ inputTokens: Math.round(totalTokens * 0.6),
+ outputTokens: Math.round(totalTokens * 0.3),
+ cachedTokens: Math.round(totalTokens * 0.1),
+ cacheWriteTokens: 0,
+ totalTokens,
+ nTasks: 2,
+ },
+ cost: { usd: 4.25, unavailable: false, stale: false },
+ filesChanged: 7,
+ tasksCompleted: 3,
+ tasksInProgress: 1,
+ tasksInReview: 0,
+ },
+ ],
+ };
+}
+
+export function activityFixture() {
+ return {
+ from: "2026-06-08",
+ to: null,
+ sessions: 4,
+ messages: 12,
+ activeNodes: 3,
+ activeAgents: 2,
+ agentRuns: { total: 8, active: 1, completed: 6, failed: 1 },
+ daily: [
+ { day: "2026-06-08", messages: 2, activeNodes: 1, activeAgents: 1, agentRuns: 2 },
+ { day: "2026-06-09", messages: 4, activeNodes: 2, activeAgents: 1, agentRuns: 3 },
+ { day: "2026-06-10", messages: 6, activeNodes: 3, activeAgents: 2, agentRuns: 3 },
+ ],
+ stickiness: 0.5,
+ mttr: { value: null, unavailable: true, sampleCount: 0 },
+ monitor: {
+ mttr: { value: null, unavailable: true, sampleCount: 0 },
+ incidentsOpened: 0,
+ incidentsResolved: 0,
+ openIncidents: 0,
+ deployments: 0,
+ },
+ funnel: {
+ stages: [],
+ enteredInRange: 0,
+ doneInRange: 0,
+ completionRate: null,
+ throughputPerDay: 0,
+ rangeDays: 7,
+ },
+ };
+}
+
+export function expectRechartsWrapperWithin(testId: string, label: string): void {
+ const section = screen.getByTestId(testId);
+ const chart = within(section).getByRole("img", { name: label });
+ expect(chart.classList.contains("cc-recharts-chart") || chart.classList.contains("cc-recharts-empty")).toBe(true);
+ expect(chart.outerHTML).not.toMatch(/NaN|Infinity/);
+}
+
+export function expectBarFillsFinite(testId: string): void {
+ const section = screen.getByTestId(testId);
+ for (const fill of Array.from(section.querySelectorAll(".cc-bar-fill"))) {
+ expect(fill.style.width).toMatch(/^\d+(?:\.\d+)?%$/);
+ expect(fill.style.width).not.toMatch(/NaN|Infinity/);
+ }
+}
+
+export function expectSparklineHeightsFinite(testId: string): void {
+ const section = screen.getByTestId(testId);
+ for (const bar of Array.from(section.querySelectorAll(".cc-sparkline-bar"))) {
+ expect(bar.style.height).toMatch(/^\d+(?:\.\d+)?%$/);
+ expect(bar.style.height).not.toMatch(/NaN|Infinity/);
+ }
+}
+
+export function viewBoxNumbers(chart: Element): [number, number, number, number] {
+ return (chart.getAttribute("viewBox") ?? "")
+ .split(/\s+/)
+ .map(Number) as [number, number, number, number];
+}
+
+export function expectSvgLinePointsInsideViewBox(testId: string, label: string): void {
+ const section = screen.getByTestId(testId);
+ const chart = within(section).getByRole("img", { name: label });
+ const [, , viewBoxWidth, viewBoxHeight] = viewBoxNumbers(chart);
+ for (const point of Array.from(chart.querySelectorAll(".cc-line-chart-point"))) {
+ const cx = Number(point.getAttribute("cx"));
+ const cy = Number(point.getAttribute("cy"));
+ const r = Number(point.getAttribute("r"));
+ expect(cx).toBeGreaterThanOrEqual(r);
+ expect(cx).toBeLessThanOrEqual(viewBoxWidth - r);
+ expect(cy).toBeGreaterThanOrEqual(r);
+ expect(cy).toBeLessThanOrEqual(viewBoxHeight - r);
+ }
+}
+
+export function expectSvgLineFillsBoxAndKeepsRoundMarkers(testId: string, label: string): void {
+ const section = screen.getByTestId(testId);
+ const chart = within(section).getByRole("img", { name: label });
+ const [, , viewBoxWidth, viewBoxHeight] = viewBoxNumbers(chart);
+ const line = chart.querySelector(".cc-line-chart-path");
+ const pointPairs = (line?.getAttribute("points") ?? "")
+ .trim()
+ .split(/\s+/)
+ .filter(Boolean)
+ .map((pair) => pair.split(",").map(Number) as [number, number]);
+ expect(viewBoxWidth).toBeGreaterThan(viewBoxHeight);
+ expect(chart.getAttribute("preserveAspectRatio")).toBe("none");
+ expect(chart.getAttribute("viewBox")).not.toBe("0 0 100 100");
+ expect(chart.querySelectorAll(".cc-line-chart-point").length).toBeGreaterThan(0);
+ expect(pointPairs[0]?.[0]).toBe(3);
+ expect(pointPairs.at(-1)?.[0]).toBe(viewBoxWidth - 3);
+}
+
+export function pluginActivationFixture(overrides: Partial<{ activations: number; unavailable: boolean }> = {}) {
+ const activations = overrides.activations ?? 0;
+ const unavailable = overrides.unavailable ?? true;
+ return {
+ from: "2026-06-08",
+ to: null,
+ activations,
+ byPlugin: unavailable ? [] : [{ pluginId: "fusion-plugin-example", count: activations }],
+ unavailable,
+ };
+}
diff --git a/packages/dashboard/app/components/command-center/areas/__tests__/areas.test.tsx b/packages/dashboard/app/components/command-center/areas/__tests__/areas.test.tsx
index a5c8fa5490..98188c5923 100644
--- a/packages/dashboard/app/components/command-center/areas/__tests__/areas.test.tsx
+++ b/packages/dashboard/app/components/command-center/areas/__tests__/areas.test.tsx
@@ -1,12 +1,14 @@
/*
FNXC:CommandCenter 2026-06-16-09:42:
Command Center area component tests (PR #1683). Pin loading/error/unavailable-vs-zero rendering for each analytics area against mocked fixtures so the "—" sentinel and cost-unavailable contracts can't regress.
+
+FNXC:CommandCenter 2026-06-25-00:00:
+FN-7044 split shared fixtures into areas.test-harness.tsx so this suite can be partitioned into focused sibling files under the 2000-line hard cap without losing coverage.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, waitFor, within, act, renderHook } from "@testing-library/react";
-import type { OrgTreeNode } from "@fusion/core";
// Mock the api() helper so the areas fetch deterministic fixtures.
const mocks = vi.hoisted(() => ({
@@ -50,235 +52,31 @@ vi.mock("../../../../hooks/useAppSettings", () => ({
import { TokensArea } from "../TokensArea";
import { ToolsArea } from "../ToolsArea";
import { ProductivityArea } from "../ProductivityArea";
-import { GithubArea } from "../GithubArea";
-import { SignalsArea } from "../SignalsArea";
import { TeamArea } from "../TeamArea";
import { ActivityArea } from "../ActivityArea";
import { EcosystemArea } from "../EcosystemArea";
import { useAnalyticsArea } from "../useAnalyticsArea";
import { ConfirmDialogProvider } from "../../../../hooks/useConfirm";
import { rangeQuery } from "../areaShared";
-import { defaultPresets, rangeFromPreset, type DateRange } from "../../DateRangePicker";
-
-const range7d: DateRange = { from: "2026-06-08", to: null, preset: "7d" };
-const customRange = (from: string, to: string): DateRange => ({ from, to, preset: "custom" });
-
-function providerIconIn(element: HTMLElement, provider: string): Element | null {
- return element.querySelector(`.provider-icon[data-provider="${provider}"]`);
-}
-
-function tokenFixture(totalTokens = 1500) {
- return {
- from: "2026-06-08",
- to: null,
- groupBy: "model",
- totals: {
- inputTokens: Math.round(totalTokens * 0.6),
- outputTokens: Math.round(totalTokens * 0.3),
- cachedTokens: Math.round(totalTokens * 0.1),
- cacheWriteTokens: 0,
- totalTokens,
- nTasks: totalTokens > 0 ? 5 : 0,
- },
- cost: { usd: 12.5, unavailable: false, stale: false },
- series: [
- {
- bucket: "2026-06-08",
- inputTokens: 400,
- outputTokens: 200,
- cachedTokens: 0,
- cacheWriteTokens: 0,
- totalTokens: 600,
- nTasks: 2,
- cost: { usd: 4.5, unavailable: false, stale: false },
- },
- {
- bucket: "2026-06-09",
- inputTokens: 600,
- outputTokens: 300,
- cachedTokens: 0,
- cacheWriteTokens: 0,
- totalTokens: 900,
- nTasks: 3,
- cost: { usd: 8, unavailable: false, stale: false },
- },
- ],
- groups: [
- {
- key: "gpt-4o",
- inputTokens: 600,
- outputTokens: 300,
- cachedTokens: 100,
- cacheWriteTokens: 0,
- totalTokens: 900,
- nTasks: 3,
- cost: { usd: 9.0, unavailable: false, stale: false },
- },
- {
- key: "claude-sonnet",
- inputTokens: 400,
- outputTokens: 200,
- cachedTokens: 100,
- cacheWriteTokens: 0,
- totalTokens: 600,
- nTasks: 2,
- cost: { usd: 3.5, unavailable: false, stale: false },
- },
- ],
- };
-}
-
-function githubFixture() {
- return {
- from: "2026-06-08",
- to: null,
- filed: 5,
- fixed: 3,
- net: 2,
- daily: [
- { date: "2026-06-08", filed: 2, fixed: 1 },
- { date: "2026-06-09", filed: 3, fixed: 2 },
- ],
- byRepo: [
- { repo: "acme/alpha", filed: 4, fixed: 1 },
- { repo: "acme/beta", filed: 1, fixed: 2 },
- ],
- resolved: [],
- };
-}
-
-function productivityFixture() {
- return {
- from: "2026-06-08",
- to: null,
- modifiedFiles: 7,
- commits: 4,
- pullRequests: 2,
- byLanguage: [{ language: "TypeScript", count: 7 }],
- loc: { value: null, unavailable: true },
- hoursSaved: { value: null, unavailable: true },
- taskDuration: {
- completedTasks: 3,
- averageMs: 90 * 60 * 1000,
- medianMs: 60 * 60 * 1000,
- p90Ms: 2 * 60 * 60 * 1000,
- totalMs: 270 * 60 * 1000,
- unavailable: false,
- },
- };
-}
-
-function installElementClientWidth(width: number) {
- return vi.spyOn(HTMLElement.prototype, "clientWidth", "get").mockReturnValue(width);
-}
-
-function agentNode(id: string, name: string, children: OrgTreeNode[] = [], title = "Team Lead"): OrgTreeNode {
- return {
- agent: {
- id,
- name,
- title,
- role: "executor",
- state: "idle",
- createdAt: "2026-06-19T00:00:00.000Z",
- updatedAt: "2026-06-19T00:00:00.000Z",
- metadata: {},
- },
- children,
- };
-}
-
-function emptyTeamFixture() {
- return {
- from: null,
- to: null,
- totals: {
- tokens: { inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, totalTokens: 0, nTasks: 0 },
- cost: { usd: null, unavailable: false, stale: false },
- filesChanged: 0,
- tasksCompleted: 0,
- tasksInProgress: 0,
- tasksInReview: 0,
- },
- agents: [],
- };
-}
-
-function populatedTeamFixture(totalTokens = 1500) {
- return {
- ...emptyTeamFixture(),
- totals: {
- tokens: {
- inputTokens: Math.round(totalTokens * 0.6),
- outputTokens: Math.round(totalTokens * 0.3),
- cachedTokens: Math.round(totalTokens * 0.1),
- cacheWriteTokens: 0,
- totalTokens,
- nTasks: 2,
- },
- cost: { usd: 4.25, unavailable: false, stale: false },
- filesChanged: 7,
- tasksCompleted: 3,
- tasksInProgress: 1,
- tasksInReview: 0,
- },
- agents: [
- {
- agentId: "agent-alpha",
- agentName: "Alpha Agent",
- role: "executor",
- state: "running",
- tokens: {
- inputTokens: Math.round(totalTokens * 0.6),
- outputTokens: Math.round(totalTokens * 0.3),
- cachedTokens: Math.round(totalTokens * 0.1),
- cacheWriteTokens: 0,
- totalTokens,
- nTasks: 2,
- },
- cost: { usd: 4.25, unavailable: false, stale: false },
- filesChanged: 7,
- tasksCompleted: 3,
- tasksInProgress: 1,
- tasksInReview: 0,
- },
- ],
- };
-}
-
-function activityFixture() {
- return {
- from: "2026-06-08",
- to: null,
- sessions: 4,
- messages: 12,
- activeNodes: 3,
- activeAgents: 2,
- agentRuns: { total: 8, active: 1, completed: 6, failed: 1 },
- daily: [
- { day: "2026-06-08", messages: 2, activeNodes: 1, activeAgents: 1, agentRuns: 2 },
- { day: "2026-06-09", messages: 4, activeNodes: 2, activeAgents: 1, agentRuns: 3 },
- { day: "2026-06-10", messages: 6, activeNodes: 3, activeAgents: 2, agentRuns: 3 },
- ],
- stickiness: 0.5,
- mttr: { value: null, unavailable: true, sampleCount: 0 },
- monitor: {
- mttr: { value: null, unavailable: true, sampleCount: 0 },
- incidentsOpened: 0,
- incidentsResolved: 0,
- openIncidents: 0,
- deployments: 0,
- },
- funnel: {
- stages: [],
- enteredInRange: 0,
- doneInRange: 0,
- completionRate: null,
- throughputPerDay: 0,
- rangeDays: 7,
- },
- };
-}
+import { defaultPresets, rangeFromPreset } from "../../DateRangePicker";
+import {
+ activityFixture,
+ agentNode,
+ customRange,
+ emptyTeamFixture,
+ expectBarFillsFinite,
+ expectRechartsWrapperWithin,
+ expectSparklineHeightsFinite,
+ expectSvgLineFillsBoxAndKeepsRoundMarkers,
+ expectSvgLinePointsInsideViewBox,
+ installElementClientWidth,
+ pluginActivationFixture,
+ populatedTeamFixture,
+ productivityFixture,
+ providerIconIn,
+ range7d,
+ tokenFixture,
+} from "./areas.test-harness";
beforeEach(() => {
apiMock.mockReset();
@@ -306,67 +104,6 @@ afterEach(() => {
vi.useRealTimers();
});
-function expectRechartsWrapperWithin(testId: string, label: string): void {
- const section = screen.getByTestId(testId);
- const chart = within(section).getByRole("img", { name: label });
- expect(chart.classList.contains("cc-recharts-chart") || chart.classList.contains("cc-recharts-empty")).toBe(true);
- expect(chart.outerHTML).not.toMatch(/NaN|Infinity/);
-}
-
-function expectBarFillsFinite(testId: string): void {
- const section = screen.getByTestId(testId);
- for (const fill of Array.from(section.querySelectorAll(".cc-bar-fill"))) {
- expect(fill.style.width).toMatch(/^\d+(?:\.\d+)?%$/);
- expect(fill.style.width).not.toMatch(/NaN|Infinity/);
- }
-}
-
-function expectSparklineHeightsFinite(testId: string): void {
- const section = screen.getByTestId(testId);
- for (const bar of Array.from(section.querySelectorAll(".cc-sparkline-bar"))) {
- expect(bar.style.height).toMatch(/^\d+(?:\.\d+)?%$/);
- expect(bar.style.height).not.toMatch(/NaN|Infinity/);
- }
-}
-
-function viewBoxNumbers(chart: Element): [number, number, number, number] {
- return (chart.getAttribute("viewBox") ?? "")
- .split(/\s+/)
- .map(Number) as [number, number, number, number];
-}
-
-function expectSvgLinePointsInsideViewBox(testId: string, label: string): void {
- const section = screen.getByTestId(testId);
- const chart = within(section).getByRole("img", { name: label });
- const [, , viewBoxWidth, viewBoxHeight] = viewBoxNumbers(chart);
- for (const point of Array.from(chart.querySelectorAll(".cc-line-chart-point"))) {
- const cx = Number(point.getAttribute("cx"));
- const cy = Number(point.getAttribute("cy"));
- const r = Number(point.getAttribute("r"));
- expect(cx).toBeGreaterThanOrEqual(r);
- expect(cx).toBeLessThanOrEqual(viewBoxWidth - r);
- expect(cy).toBeGreaterThanOrEqual(r);
- expect(cy).toBeLessThanOrEqual(viewBoxHeight - r);
- }
-}
-
-function expectSvgLineFillsBoxAndKeepsRoundMarkers(testId: string, label: string): void {
- const section = screen.getByTestId(testId);
- const chart = within(section).getByRole("img", { name: label });
- const [, , viewBoxWidth, viewBoxHeight] = viewBoxNumbers(chart);
- const line = chart.querySelector(".cc-line-chart-path");
- const pointPairs = (line?.getAttribute("points") ?? "")
- .trim()
- .split(/\s+/)
- .filter(Boolean)
- .map((pair) => pair.split(",").map(Number) as [number, number]);
- expect(viewBoxWidth).toBeGreaterThan(viewBoxHeight);
- expect(chart.getAttribute("preserveAspectRatio")).toBe("none");
- expect(chart.getAttribute("viewBox")).not.toBe("0 0 100 100");
- expect(chart.querySelectorAll(".cc-line-chart-point").length).toBeGreaterThan(0);
- expect(pointPairs[0]?.[0]).toBe(3);
- expect(pointPairs.at(-1)?.[0]).toBe(viewBoxWidth - 3);
-}
describe("rangeQuery / rangeFromPreset", () => {
it("serializes every default preset into a distinct server-resolvable query", () => {
@@ -1626,17 +1363,6 @@ describe("TeamArea", () => {
});
});
-function pluginActivationFixture(overrides: Partial<{ activations: number; unavailable: boolean }> = {}) {
- const activations = overrides.activations ?? 0;
- const unavailable = overrides.unavailable ?? true;
- return {
- from: "2026-06-08",
- to: null,
- activations,
- byPlugin: unavailable ? [] : [{ pluginId: "fusion-plugin-example", count: activations }],
- unavailable,
- };
-}
function mockEcosystemResponses(tokens: unknown, activations: unknown): void {
apiMock.mockImplementation((path: string) => {
@@ -1735,333 +1461,3 @@ describe("EcosystemArea", () => {
});
});
-describe("GithubArea", () => {
- it("renders filed/fixed/net stats, daily trend, and by-repo bars", async () => {
- apiMock.mockResolvedValue(githubFixture());
- render();
-
- await screen.findByTestId("cc-area-github");
- expect(screen.getByTestId("cc-github-filed").textContent).toContain("5");
- expect(screen.getByTestId("cc-github-fixed").textContent).toContain("3");
- expect(screen.getByTestId("cc-github-net").textContent).toContain("2");
- expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
- expect(screen.getByTestId("cc-github-line")).toBeTruthy();
- expect(screen.getByRole("img", { name: "Filed vs fixed share" })).toBeTruthy();
- expect(screen.getByRole("img", { name: "Filed vs fixed line" })).toBeTruthy();
- expect(screen.getByTestId("cc-github-daily-trend")).toBeTruthy();
- expect(screen.getByRole("img", { name: "Filed" })).toBeTruthy();
- expect(screen.getByRole("img", { name: "Fixed" })).toBeTruthy();
- const repoChart = screen.getByRole("list", { name: "By repository" });
- expect(within(repoChart).getByText("acme/alpha")).toBeTruthy();
- expect(within(repoChart).getByLabelText("acme/alpha: 4 filed / 1 fixed")).toBeTruthy();
- });
-
- it("renders resolved issues with safe outbound links and approximation labels", async () => {
- apiMock.mockResolvedValue({
- ...githubFixture(),
- resolved: [
- {
- taskId: "FN-100",
- taskTitle: "Fix alpha crash",
- repo: "acme/alpha",
- issueNumber: 123,
- url: "https://github.com/acme/alpha/issues/123",
- resolvedAt: "2026-06-10T12:34:56.000Z",
- resolvedAtExact: true,
- },
- {
- taskId: "FN-101",
- taskTitle: "Patch unknown import",
- repo: "(unknown)",
- issueNumber: null,
- url: null,
- resolvedAt: "2026-06-09T08:00:00.000Z",
- resolvedAtExact: false,
- },
- ],
- });
-
- render();
-
- const section = await screen.findByTestId("cc-github-resolved");
- expect(section.textContent).toContain("Resolved issues");
- expect(section.textContent).toContain("acme/alpha#123");
- expect(section.textContent).toContain("Fix alpha crash");
- expect(section.textContent).toContain("FN-100");
- expect(section.textContent).toContain("(unknown)");
- expect(section.textContent).toContain("Patch unknown import");
- expect(section.textContent).toContain("approx");
- expect(section.textContent).toContain("2026");
-
- const linkedIssue = within(section).getByRole("link", { name: "Open GitHub issue acme/alpha#123" });
- expect(linkedIssue.getAttribute("href")).toBe("https://github.com/acme/alpha/issues/123");
- expect(linkedIssue.getAttribute("target")).toBe("_blank");
- expect(linkedIssue.getAttribute("rel")).toBe("noopener noreferrer");
- expect(within(section).queryByRole("link", { name: /unknown/i })).toBeNull();
- });
-
- it("omits the resolved issues section for an empty resolved list", async () => {
- apiMock.mockResolvedValue({ ...githubFixture(), resolved: [] });
-
- render();
-
- await screen.findByTestId("cc-area-github");
- expect(screen.queryByTestId("cc-github-resolved")).toBeNull();
- expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
- });
-
- it("renders the empty state without empty chart shells", async () => {
- apiMock.mockResolvedValue({ ...githubFixture(), filed: 0, fixed: 0, net: 0, daily: [], byRepo: [] });
- render();
-
- await screen.findByTestId("cc-area-github");
- expect(screen.getByTestId("cc-area-github").textContent).toContain("No GitHub issue activity");
- expect(screen.getByTestId("cc-github-backfill-button")).toBeTruthy();
- expect(screen.queryByTestId("cc-github-pie")).toBeNull();
- expect(screen.queryByTestId("cc-github-line")).toBeNull();
- expect(screen.queryByTestId("cc-github-daily-trend")).toBeNull();
- expect(screen.queryByTestId("cc-github-by-repo")).toBeNull();
- });
-
- it("renders loading and error states", async () => {
- apiMock.mockImplementationOnce(() => new Promise(() => undefined));
- const { unmount } = render();
- expect(screen.getByTestId("cc-area-github-loading")).toBeTruthy();
- expect(screen.queryByTestId("cc-github-pie")).toBeNull();
- expect(screen.queryByTestId("cc-github-line")).toBeNull();
- unmount();
-
- apiMock.mockRejectedValueOnce(new Error("github failed"));
- render();
- await screen.findByTestId("cc-area-github-error");
- expect(screen.getByTestId("cc-area-github-error").textContent).toContain("github failed");
- expect(screen.queryByTestId("cc-github-pie")).toBeNull();
- expect(screen.queryByTestId("cc-github-line")).toBeNull();
- });
-
- it("handles undefined chart arrays and zero values without NaN output", async () => {
- apiMock.mockResolvedValue({ ...githubFixture(), filed: 1, fixed: 0, net: 1, daily: undefined, byRepo: undefined });
- render();
-
- await screen.findByTestId("cc-area-github");
- expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
- expect(screen.queryByTestId("cc-github-line")).toBeNull();
- expect(screen.queryByTestId("cc-github-daily-trend")).toBeNull();
- expect(screen.queryByTestId("cc-github-by-repo")).toBeNull();
- expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
- });
-
- it("keeps GitHub recharts safe for single-item and non-finite daily data", async () => {
- apiMock.mockResolvedValue({
- ...githubFixture(),
- filed: 1,
- fixed: 1,
- net: 0,
- daily: [{ date: "2026-06-08", filed: Number.NaN, fixed: Number.POSITIVE_INFINITY }],
- byRepo: [{ repo: "acme/broken", filed: Number.NaN, fixed: -1 }],
- });
- render();
-
- await screen.findByTestId("cc-area-github");
- expect(screen.getByTestId("cc-github-pie")).toBeTruthy();
- expect(screen.getByTestId("cc-github-line")).toBeTruthy();
- expect(screen.getByTestId("cc-area-github").textContent).not.toContain("NaN");
- expect(screen.getByTestId("cc-area-github").textContent).not.toContain("Infinity");
- });
-
- it("rejects an inverted custom range client-side without fetching", async () => {
- render();
- await waitFor(() => expect(apiMock).not.toHaveBeenCalled());
- });
-
- it("runs a single backfill batch and renders accumulated result counts", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
- scanned: 4,
- filled: 2,
- skipped: 1,
- errors: 0,
- hasMore: false,
- });
-
- render();
- await screen.findByTestId("cc-area-github");
-
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- await screen.findByText(/Backfill complete/i);
- expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledWith({ offset: 0, limit: 100 }, undefined);
- const result = screen.getByTestId("cc-github-backfill-result");
- expect(result.textContent).toContain("Scanned 4, filled 2, skipped 1, errors 0");
- });
-
- it("paginates multi-batch backfills with advancing offsets and summed counts", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock
- .mockResolvedValueOnce({ scanned: 100, filled: 4, skipped: 90, errors: 1, hasMore: true })
- .mockResolvedValueOnce({ scanned: 25, filled: 3, skipped: 22, errors: 0, hasMore: false });
-
- render();
- await screen.findByTestId("cc-area-github");
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- await waitFor(() => expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(2));
- expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenNthCalledWith(1, { offset: 0, limit: 100 }, undefined);
- expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenNthCalledWith(2, { offset: 100, limit: 100 }, undefined);
- const result = await screen.findByTestId("cc-github-backfill-result");
- expect(result.textContent).toContain("Scanned 125, filled 7, skipped 112, errors 1");
- expect(result.className).toContain("cc-github-backfill-status--error");
- });
-
- it("shows the all-zero backfill as nothing to backfill instead of an error", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
- scanned: 0,
- filled: 0,
- skipped: 0,
- errors: 0,
- hasMore: false,
- });
-
- render();
- await screen.findByTestId("cc-area-github");
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- const result = await screen.findByTestId("cc-github-backfill-result");
- expect(result.textContent).toContain("Nothing to backfill");
- expect(result.className).not.toContain("cc-github-backfill-status--error");
- });
-
- it("surfaces nonzero backfill error counts without throwing", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock.mockResolvedValueOnce({
- scanned: 8,
- filled: 1,
- skipped: 5,
- errors: 2,
- hasMore: false,
- });
-
- render();
- await screen.findByTestId("cc-area-github");
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- const result = await screen.findByTestId("cc-github-backfill-result");
- expect(result.textContent).toContain("errors 2");
- expect(result.className).toContain("cc-github-backfill-status--error");
- });
-
- it("captures endpoint failures in local error UI", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock.mockRejectedValueOnce(new Error("endpoint failed"));
-
- render();
- await screen.findByTestId("cc-area-github");
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- const result = await screen.findByTestId("cc-github-backfill-result");
- expect(result.textContent).toContain("endpoint failed");
- expect(result.className).toContain("cc-github-backfill-status--error");
- });
-
- it("disables and guards the button while a backfill is in flight", async () => {
- apiMock.mockResolvedValue(githubFixture());
- let resolveBackfill: ((value: { scanned: number; filled: number; skipped: number; errors: number; hasMore: boolean }) => void) | null = null;
- backfillGithubSourceIssueClosedAtMock.mockImplementationOnce(
- () => new Promise((resolve) => {
- resolveBackfill = resolve;
- }),
- );
-
- render();
- await screen.findByTestId("cc-area-github");
- const button = screen.getByTestId("cc-github-backfill-button") as HTMLButtonElement;
- fireEvent.click(button);
-
- await waitFor(() => expect(button.disabled).toBe(true));
- fireEvent.click(button);
- expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(1);
-
- resolveBackfill?.({ scanned: 1, filled: 1, skipped: 0, errors: 0, hasMore: false });
- await screen.findByText(/Backfill complete/i);
- });
-
- it("stops a pathological always-has-more response at the max iteration guard", async () => {
- apiMock.mockResolvedValue(githubFixture());
- backfillGithubSourceIssueClosedAtMock.mockResolvedValue({
- scanned: 1,
- filled: 0,
- skipped: 1,
- errors: 0,
- hasMore: true,
- });
-
- render();
- await screen.findByTestId("cc-area-github");
- fireEvent.click(screen.getByTestId("cc-github-backfill-button"));
-
- const result = await screen.findByText(/safety limit/i);
- expect(result.textContent).toContain("safety limit");
- expect(backfillGithubSourceIssueClosedAtMock).toHaveBeenCalledTimes(1000);
- expect(screen.getByTestId("cc-github-backfill-result").textContent).toContain("Scanned 1000");
- });
-});
-
-// FN-6684 Mission Control decision: no extra pie/line test here because MissionControlPanel already renders the live SDLC Funnel for its only quantitative distribution; adding a pie would duplicate that affordance.
-describe("SignalsArea", () => {
- it("renders the empty state (not an error) when the signals endpoint is missing", async () => {
- apiMock.mockRejectedValue(new Error("API returned HTML instead of JSON (404)"));
- render();
- await screen.findByTestId("cc-area-signals-empty");
- // Must not surface the error UI.
- expect(screen.queryByTestId("cc-area-signals-error")).toBeNull();
- expect(screen.queryByTestId("cc-signals-pie")).toBeNull();
- });
-
- it("renders signal metrics and status pie when data is present", async () => {
- apiMock.mockResolvedValue({
- totalSignals: 8,
- open: 3,
- resolved: 5,
- mttr: { value: 42, unavailable: false },
- bySource: [{ source: "sentry", count: 8 }],
- bySeverity: [{ severity: "error", count: 8 }],
- });
- render();
- await screen.findByTestId("cc-area-signals");
- expect(screen.getByTestId("cc-signals-total").textContent).toContain("8");
- expect(screen.getByTestId("cc-signals-mttr").textContent).toContain("42");
- expect(screen.getByTestId("cc-signals-pie")).toBeTruthy();
- expect(screen.getByRole("img", { name: "Signal status share" })).toBeTruthy();
- });
-
- it("keeps signals pie safe for single-item and non-finite source/severity data", async () => {
- apiMock.mockResolvedValue({
- totalSignals: 1,
- open: 1,
- resolved: 0,
- mttr: { value: null, unavailable: true },
- bySource: [{ source: "broken", count: Number.NaN }],
- bySeverity: [{ severity: "broken", count: Number.POSITIVE_INFINITY }],
- });
- render();
- await screen.findByTestId("cc-area-signals");
- expect(screen.getByTestId("cc-signals-pie")).toBeTruthy();
- expect(screen.getByTestId("cc-area-signals").textContent).not.toContain("NaN");
- expect(screen.getByTestId("cc-area-signals").textContent).not.toContain("Infinity");
- });
-
- it("renders settled zero signals without a pie shell", async () => {
- apiMock.mockResolvedValue({
- totalSignals: 0,
- open: 0,
- resolved: 0,
- mttr: { value: null, unavailable: true },
- bySource: [],
- bySeverity: [],
- });
- render();
- await screen.findByTestId("cc-area-signals-empty");
- expect(screen.queryByTestId("cc-signals-pie")).toBeNull();
- });
-});