From c8daf21cd099d954544e1624b5487b2f21fcee63 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 19 Jun 2026 02:14:31 -0700 Subject: [PATCH] FN-6685: add Command Center chart browser smoke Add real-browser coverage for Command Center chart sizing and overflow across mobile and desktop. - Include emitted lazy CSS chunks in dashboard browser-layout smoke fixtures. - Add a Command Center charts smoke fixture with populated pie/line charts and an empty state. - Assert chart heights, SVG visibility, overflow containment, and scroll-owner behavior at mobile and desktop breakpoints. - Document the browser smoke requirement alongside the Command Center responsive chart testing guidance. Files changed: docs/dashboard-guide.md | 1 + docs/testing.md | 3 +- .../CommandCenter.mobile-chart-layout.test.ts | 6 + .../dashboard/scripts/browser-layout-smoke.mjs | 220 ++++++++++++++++++++- 4 files changed, 226 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-6685 Fusion-Task-Lineage: 456d51e2-ffaa-442d-bfa2-5ba17ff6724f --- docs/dashboard-guide.md | 1 + docs/testing.md | 3 +- .../CommandCenter.mobile-chart-layout.test.ts | 6 + .../scripts/browser-layout-smoke.mjs | 220 +++++++++++++++++- 4 files changed, 226 insertions(+), 4 deletions(-) diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 012f923d5a..3eea47adae 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -681,6 +681,7 @@ Rendering invariants: - Mobile chart text must not rely on min-content luck: bar labels, values, token-series axis labels, funnel headers, radial labels, legends, and chart tracks need explicit `min-inline-size: 0`, wrapping, or ellipsis rules so long model/agent/repo labels cannot crush the track or create hidden horizontal overflow in a real browser. - On tablet (`min-width: 769px` and `max-width: 1024px`), `.project-content`, `.command-center`, and `.cc-tabpanel` keep the same definite flex/min-height scroll-owner chain, while the live strip and chart grids collapse before they can create document-level horizontal overflow. - Command Center stat cards, overview chart cards, live strips, table wrappers, Team chart panels, token-series plots, system control cards, and gauge/chart cards share the same tokenized rhythm: `--space-3` gaps/padding for card-like surfaces, `--border-width` borders, `--radius-md` radii, and `--surface-1` backgrounds. Area-specific accents may use `color-mix(...)`, but layout, border, radius, text color, and motion must stay on design tokens. +- The dashboard browser-layout smoke includes a `[data-smoke="command-center-charts"]` fixture that loads emitted lazy Command Center CSS and verifies representative recharts pie, line, and empty states at mobile (390×844) and desktop breakpoints. The fixture asserts non-zero chart and SVG heights, visible empty-state text, no internal/page horizontal overflow, and no chart-level vertical scroll owner before chart layout changes are considered verified. Data states: - Overview shows a loading state while core analytics settle, then shows `No usage data yet. Run some agents to populate the Command Center.` only after the selected range has settled with no core usage data. Overview, Tokens, Tools, Activity, Productivity, Team, Ecosystem, GitHub, Signals, and System omit their additive recharts cards in loading/error/empty states, so non-populated data never leaves an empty chart shell. diff --git a/docs/testing.md b/docs/testing.md index f24810c4a2..83dd19dd27 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -65,7 +65,8 @@ pnpm --filter @fusion/dashboard test:build # built client output contra Run `test:deep` when changing broad dashboard architecture, shared modal/view infrastructure, or route registration. Run `test:browser-smoke` for layout/responsive/navigation/modal/CSS changes. Run `test:build` for Vite output, lazy-loading, chunking, or client-dist changes. -Command Center responsive chart fixes need evidence beyond jsdom. Keep the jsdom scroll-owner tests for rule/structure coverage, but pair them with `packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts`, which reads the co-located Command Center CSS files directly and asserts the mobile shrink/height/border rules that real layout depends on. For visible defects, also capture a real browser/device (or headless Chrome/Blink) reproduction with `scrollWidth > clientWidth`, zero/clipped `clientHeight`, or stretch measurements; do not close a Command Center mobile chart bug on jsdom-green assertions alone. + +Command Center responsive chart fixes need evidence beyond jsdom. Keep the jsdom scroll-owner tests for rule/structure coverage, but pair them with `packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts`, which reads the co-located Command Center CSS files directly and asserts the mobile shrink/height/border rules that real layout depends on. For visible defects, also capture a real browser/device (or headless Chrome/Blink) reproduction with `scrollWidth > clientWidth`, zero/clipped `clientHeight`, or stretch measurements; do not close a Command Center mobile chart bug on jsdom-green assertions alone. The local `pnpm --filter @fusion/dashboard test:browser-smoke --require-browser` lane now includes `[data-smoke="command-center-charts"]` and gates representative Command Center recharts pie, line, and empty states at 390×844 mobile plus desktop viewports for visible SVG/container height, overflow containment, empty-state text, and chart scroll-owner violations. The shared mobile/tablet overflow-containment net lives at `packages/dashboard/app/__tests__/dashboard-overflow-containment.test.tsx`. It covers board/kanban columns, task-detail modal shell, workflow/simple workflow editors, and Activity Log modal at mobile, tablet, and landscape-phone breakpoints. Run it directly when touching dashboard viewport containment or shared modal/workflow CSS: diff --git a/packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts b/packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts index dabee2f403..e2f654a6d4 100644 --- a/packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts +++ b/packages/dashboard/app/components/command-center/__tests__/CommandCenter.mobile-chart-layout.test.ts @@ -64,6 +64,12 @@ describe("CommandCenter.mobile-chart-layout.css", () => { expect(mobileCss).toMatch(/\.cc-system-chart-grid\s*\{[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\)/); }); + it("keeps recharts ResponsiveContainer parents sized without becoming scroll owners", () => { + expect(cssContent).toMatch(/\.cc-overview-chart-card \.cc-recharts-chart,[\s\S]*\.cc-overview-chart-card \.cc-recharts-empty\s*\{[\s\S]*inline-size:\s*100%;[\s\S]*block-size:\s*calc\(var\(--space-20\)\s*\*\s*3\);[\s\S]*min-inline-size:\s*0/); + expect(cssContent).toMatch(/\.cc-area \.cc-recharts-chart,[\s\S]*\.cc-area \.cc-recharts-empty\s*\{[\s\S]*inline-size:\s*100%;[\s\S]*block-size:\s*calc\(var\(--space-20\)\s*\*\s*3\);[\s\S]*min-inline-size:\s*0/); + expect(cssContent).not.toMatch(/\.cc-recharts-(?:chart|empty)[^{]*\{[^}]*overflow-y:\s*(?:auto|scroll)/); + }); + it("normalizes chart/card/table border rhythm with design tokens only", () => { expect(cssContent).toMatch(/\.cc-stat-card\s*\{[^}]*padding:\s*var\(--space-3\);[^}]*border:\s*var\(--border-width\)\s+solid\s+var\(--border-subtle\);[^}]*border-radius:\s*var\(--radius-md\);[^}]*background:\s*var\(--surface-1\)/); expect(cssContent).toMatch(/\.cc-table-wrap\s*\{[^}]*border:\s*var\(--border-width\)\s+solid\s+var\(--border-subtle\);[^}]*border-radius:\s*var\(--radius-md\);[^}]*background:\s*var\(--surface-1\);[^}]*overflow-x:\s*auto;[^}]*overflow-y:\s*hidden/); diff --git a/packages/dashboard/scripts/browser-layout-smoke.mjs b/packages/dashboard/scripts/browser-layout-smoke.mjs index f6e4b064ca..007344292b 100644 --- a/packages/dashboard/scripts/browser-layout-smoke.mjs +++ b/packages/dashboard/scripts/browser-layout-smoke.mjs @@ -4,7 +4,7 @@ import { spawn } from "node:child_process"; import { createServer } from "node:http"; import { superviseSpawn } from "@fusion/core"; -import { readFile, rm, stat, mkdtemp } from "node:fs/promises"; +import { readFile, readdir, rm, stat, mkdtemp } from "node:fs/promises"; import { existsSync } from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -43,8 +43,21 @@ async function readEmittedClientCss() { } const chunks = []; - for (const href of hrefs) { - const file = path.join(clientDistRoot, href.replace(/^\//, "")); + const cssFiles = new Set(hrefs.map((href) => path.join(clientDistRoot, href.replace(/^\//, "")))); + /* + FNXC:CommandCenterTesting 2026-06-19-02:19: + Command Center is lazy-loaded, so its emitted CSS lives in a dynamic chunk that index.html does not link directly. The browser smoke must include emitted CSS chunks as well as root links or chart layout assertions would test an unstyled fixture instead of the production Command Center contract. + */ + const assetsDir = path.join(clientDistRoot, "assets"); + if (existsSync(assetsDir)) { + for (const entry of await readdir(assetsDir)) { + if (entry.endsWith(".css")) { + cssFiles.add(path.join(assetsDir, entry)); + } + } + } + + for (const file of [...cssFiles].sort()) { chunks.push(`\n/* ${path.relative(dashboardRoot, file)} */\n${await readFile(file, "utf8")}`); } return chunks.join("\n"); @@ -148,6 +161,9 @@ export function createSmokeHtml() { + @@ -359,6 +375,97 @@ export function createSmokeHtml() { + + +