FN-5849: shard dashboard quality Vitest runs
Split the dashboard quality Vitest lane into bounded heap-managed shards to avoid signal exits. - replace the single dashboard quality test command with sequential app and API shard scripts - add a heap wrapper plus Vitest config shard definitions for foundation, component, App, ChatView, and SettingsModal coverage - add guard tests for the sharded dashboard test configuration and restore spinner-animation.css.test.ts to foundation-ui coverage - update the CLI package-config test expectations for the new dashboard test script layout Files changed: packages/cli/src/__tests__/package-config.test.ts | 8 +- packages/dashboard/package.json | 18 +- packages/dashboard/scripts/run-vitest-with-heap.mjs | 41 ++++ packages/dashboard/src/__tests__/dashboard-test-config-guard.test.ts | 77 ++++++ packages/dashboard/vitest.config.ts | 273 ++++++++++++++++++++- 5 files changed, 407 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-5849 Fusion-Task-Lineage: 522f3963-4647-4a59-bb6b-a3dacfbf620e
This commit is contained in:
@@ -272,10 +272,14 @@ describe("Workspace bootstrap script contract", () => {
|
|||||||
|
|
||||||
it("keeps dashboard's default test lane curated with explicit deep coverage", () => {
|
it("keeps dashboard's default test lane curated with explicit deep coverage", () => {
|
||||||
const defaultTest = dashboardPkg.scripts?.test;
|
const defaultTest = dashboardPkg.scripts?.test;
|
||||||
|
const defaultAppQuality = dashboardPkg.scripts?.["test:quality:app"];
|
||||||
|
const defaultApiQuality = dashboardPkg.scripts?.["test:quality:api"];
|
||||||
const deepTest = dashboardPkg.scripts?.["test:deep"];
|
const deepTest = dashboardPkg.scripts?.["test:deep"];
|
||||||
|
|
||||||
expect(hasProjectArg(defaultTest, "dashboard-app-quality")).toBe(true);
|
expect(defaultTest).toBe("pnpm run test:quality:app && pnpm run test:quality:api");
|
||||||
expect(hasProjectArg(defaultTest, "dashboard-api-quality")).toBe(true);
|
expect(defaultAppQuality).toContain("test:quality:app:foundation-api");
|
||||||
|
expect(defaultAppQuality).toContain("test:quality:app:settings");
|
||||||
|
expect(hasProjectArg(defaultApiQuality, "dashboard-api-quality")).toBe(true);
|
||||||
expect(hasProjectArg(defaultTest, "dashboard-app")).toBe(false);
|
expect(hasProjectArg(defaultTest, "dashboard-app")).toBe(false);
|
||||||
expect(hasProjectArg(defaultTest, "dashboard-api")).toBe(false);
|
expect(hasProjectArg(defaultTest, "dashboard-api")).toBe(false);
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,23 @@
|
|||||||
"dev": "pnpm build && pnpm typecheck && pnpm dev:serve",
|
"dev": "pnpm build && pnpm typecheck && pnpm dev:serve",
|
||||||
"dev:serve": "vite dev",
|
"dev:serve": "vite dev",
|
||||||
"pretest": "node ../../scripts/ensure-test-artifacts.mjs",
|
"pretest": "node ../../scripts/ensure-test-artifacts.mjs",
|
||||||
"test": "vitest run --project dashboard-app-quality --project dashboard-api-quality --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
"test": "pnpm run test:quality:app && pnpm run test:quality:api",
|
||||||
|
"test:quality:app": "pnpm run test:quality:app:foundation-api && pnpm run test:quality:app:foundation-ui && pnpm run test:quality:app:foundation-hooks-utils && pnpm run test:quality:app:components-a && pnpm run test:quality:app:components-b && pnpm run test:quality:app:app && pnpm run test:quality:app:chat && pnpm run test:quality:app:settings",
|
||||||
|
"test:quality:app:foundation-api": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-foundation-api --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:foundation-ui": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-foundation-ui --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:foundation-hooks-utils": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-foundation-hooks-utils --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:components-a": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-components-a --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:components-b": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-components-b --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:app": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-app --reporter=default --silent=passed-only --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:chat": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-chat --reporter=default --silent=passed-only --exclude '**/build-output.test.ts'",
|
||||||
|
"test:quality:app:settings": "pnpm run test:quality:app:settings-a1 && pnpm run test:quality:app:settings-a2 && pnpm run test:quality:app:settings-a3 && pnpm run test:quality:app:settings-b && pnpm run test:quality:app:settings-c && pnpm run test:quality:app:settings-d",
|
||||||
|
"test:quality:app:settings-a1": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"applies keyboard CSS variables|defaults to the global General section|honors an explicit initialSection override|legacy pi-extensions initialSection alias|shows a Secrets entry|renders the SecretsView|direct merge commit routing|reuse-task-worktree when the server omits|persists cwd-main through the save payload|does NOT render the warning banner|legacy cwd-main mode is selected|removes the warning banner|legacy sibling branch rename escape hatch|agent provisioning approval settings|deferred settings fetches|Global General\"",
|
||||||
|
"test:quality:app:settings-a2": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"Project General|Appearance|Project Models\"",
|
||||||
|
"test:quality:app:settings-a3": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"settings header actions|settings version display|settings export filename\"",
|
||||||
|
"test:quality:app:settings-b": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"Authentication provider icon wrappers|Droid plugin Settings integration|Plugins section navigation\"",
|
||||||
|
"test:quality:app:settings-c": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"Scheduling overlap ignore paths|Number input clearing|Worktrunk integration|Memory section|Merge section|Experimental Features section\"",
|
||||||
|
"test:quality:app:settings-d": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-app-quality-settings --reporter=default --silent=passed-only --exclude '**/build-output.test.ts' -t \"Remote section|Notifications provider cards|scheduled eval settings section|memory backups settings|research settings sections|memory dream trigger|plugin structured contribution contract fixtures\"",
|
||||||
|
"test:quality:api": "vitest run --project dashboard-api-quality --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
"test:app": "vitest run --project dashboard-app --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
"test:app": "vitest run --project dashboard-app --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
"test:api": "vitest run --project dashboard-api --silent=passed-only --reporter=dot",
|
"test:api": "vitest run --project dashboard-api --silent=passed-only --reporter=dot",
|
||||||
"test:deep": "vitest run --project dashboard-app --project dashboard-api --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
"test:deep": "vitest run --project dashboard-app --project dashboard-api --silent=passed-only --reporter=dot --exclude '**/build-output.test.ts'",
|
||||||
|
|||||||
41
packages/dashboard/scripts/run-vitest-with-heap.mjs
Normal file
41
packages/dashboard/scripts/run-vitest-with-heap.mjs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/* global clearInterval, console, process, setInterval */
|
||||||
|
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
const rawArgs = process.argv.slice(2);
|
||||||
|
const heapArg = rawArgs.find((arg) => arg.startsWith("--heap="));
|
||||||
|
const heapMb = heapArg?.slice("--heap=".length) || "6144";
|
||||||
|
const vitestArgs = rawArgs.filter((arg) => !arg.startsWith("--heap="));
|
||||||
|
|
||||||
|
if (vitestArgs.length === 0) {
|
||||||
|
console.error("Usage: node scripts/run-vitest-with-heap.mjs [--heap=6144] <vitest args...>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeOptions = [`--max-old-space-size=${heapMb}`, process.env.NODE_OPTIONS || ""]
|
||||||
|
.join(" ")
|
||||||
|
.trim();
|
||||||
|
const child = spawn("pnpm", ["exec", "vitest", ...vitestArgs], {
|
||||||
|
stdio: "inherit",
|
||||||
|
env: { ...process.env, NODE_OPTIONS: nodeOptions },
|
||||||
|
});
|
||||||
|
|
||||||
|
const heartbeat = setInterval(() => {
|
||||||
|
console.log(`[dashboard-vitest] still running: ${vitestArgs.join(" ")}`);
|
||||||
|
}, 5_000);
|
||||||
|
|
||||||
|
const forwardSignal = (signal) => {
|
||||||
|
child.kill(signal);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on("SIGINT", () => forwardSignal("SIGINT"));
|
||||||
|
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
|
||||||
|
|
||||||
|
child.on("close", (code, signal) => {
|
||||||
|
clearInterval(heartbeat);
|
||||||
|
if (signal) {
|
||||||
|
process.kill(process.pid, signal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.exit(code ?? 1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// @vitest-environment node
|
||||||
|
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const dashboardRoot = join(__dirname, "..", "..");
|
||||||
|
const dashboardPackageJsonPath = join(dashboardRoot, "package.json");
|
||||||
|
const vitestConfigPath = join(dashboardRoot, "vitest.config.ts");
|
||||||
|
|
||||||
|
function readDashboardPackageJson(): { scripts: Record<string, string> } {
|
||||||
|
return JSON.parse(readFileSync(dashboardPackageJsonPath, "utf8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("dashboard test config guard", () => {
|
||||||
|
it("keeps the dashboard quality gate split into sequential sub-runs", () => {
|
||||||
|
const { scripts } = readDashboardPackageJson();
|
||||||
|
|
||||||
|
expect(scripts.test).toBe("pnpm run test:quality:app && pnpm run test:quality:api");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:foundation-api");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:foundation-ui");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:foundation-hooks-utils");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:components-a");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:components-b");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:app");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:chat");
|
||||||
|
expect(scripts["test:quality:app"]).toContain("test:quality:app:settings");
|
||||||
|
expect(scripts["test:quality:app"]).not.toContain("dashboard-app-quality --project dashboard-api-quality");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("pins every app-quality shard to the heap wrapper and keeps split settings shards", () => {
|
||||||
|
const { scripts } = readDashboardPackageJson();
|
||||||
|
|
||||||
|
for (const key of [
|
||||||
|
"test:quality:app:foundation-api",
|
||||||
|
"test:quality:app:foundation-ui",
|
||||||
|
"test:quality:app:foundation-hooks-utils",
|
||||||
|
"test:quality:app:components-a",
|
||||||
|
"test:quality:app:components-b",
|
||||||
|
"test:quality:app:app",
|
||||||
|
"test:quality:app:chat",
|
||||||
|
"test:quality:app:settings-a1",
|
||||||
|
"test:quality:app:settings-a2",
|
||||||
|
"test:quality:app:settings-a3",
|
||||||
|
"test:quality:app:settings-b",
|
||||||
|
"test:quality:app:settings-c",
|
||||||
|
"test:quality:app:settings-d",
|
||||||
|
]) {
|
||||||
|
expect(scripts[key]).toContain("node scripts/run-vitest-with-heap.mjs --heap=6144");
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(scripts["test:quality:app:settings"]).toContain("settings-a1");
|
||||||
|
expect(scripts["test:quality:app:settings"]).toContain("settings-d");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the split quality projects declared in vitest config", () => {
|
||||||
|
const vitestConfig = readFileSync(vitestConfigPath, "utf8");
|
||||||
|
|
||||||
|
for (const projectName of [
|
||||||
|
"dashboard-app-quality-foundation-api",
|
||||||
|
"dashboard-app-quality-foundation-ui",
|
||||||
|
"dashboard-app-quality-foundation-hooks-utils",
|
||||||
|
"dashboard-app-quality-components-a",
|
||||||
|
"dashboard-app-quality-components-b",
|
||||||
|
"dashboard-app-quality-app",
|
||||||
|
"dashboard-app-quality-chat",
|
||||||
|
"dashboard-app-quality-settings",
|
||||||
|
"dashboard-api-quality",
|
||||||
|
]) {
|
||||||
|
expect(vitestConfig).toContain(`name: \"${projectName}\"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(vitestConfig).toContain('"app/__tests__/spinner-animation.css.test.ts"');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,24 +5,211 @@ import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
|
|||||||
|
|
||||||
const maxWorkers = computeMaxWorkers({ defaultCap: 3 });
|
const maxWorkers = computeMaxWorkers({ defaultCap: 3 });
|
||||||
|
|
||||||
const qualityAppTests = [
|
const qualityAppFoundationApiTests = [
|
||||||
// Top-level API-client, mobile layout, styling, auth, and shell regressions.
|
// API-client regressions are numerous but lightweight; keep them in their
|
||||||
"app/__tests__/*.test.{ts,tsx}",
|
// own shard so the jsdom heap can reset before broader UI/layout coverage.
|
||||||
"app/__tests__/browser-layout-smoke-fixture.test.ts",
|
"app/__tests__/api-*.test.ts",
|
||||||
"app/api/**/*.test.ts",
|
"app/api/**/*.test.ts",
|
||||||
// Representative workflow/component coverage. Exhaustive modal/view suites
|
];
|
||||||
// stay available in the full `dashboard-app` project.
|
|
||||||
"app/components/__tests__/{ActiveAgentsPanel,ActivityLogModal,AgentMentionPopup,AgentMetricsBar,AgentOnboardingModal,AgentReflectionsTab,AgentTokenStatsPanel,App,AuthTokenRecoveryDialog,Board,board-mobile,board-mobile-view-switch,BranchGroupCard,ChatView,ChatView.autosize,ChatView.chat-input-autosize,ChatView.default-model-icon,ChatView.draft,ChatView.hash-mention,ChatView.rooms,ChatView.scroll-to-top,ChatView.swipe-back,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DevServerView.mobile,DirectoryPicker,DuplicateWarningModal,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,GitHubBadge,GroupTaskModal,InlineCreateCard,LoginInstructions,MemoryView,MergeAdvanceNotice,MessageComposer,MessageComposer.autosize,MobileNavBar,NewTaskModal,NewTaskModal.shared-cache,NodeCard,NodeHealthDot,NodeStatusIndicator,PlanningModeModal.autosize,PrChecksList,PrCreateModal,PrCreateModal.layout,ProjectCard,ProjectSelector,ProviderIcon,PrPanel,PrPanel.merge,PrPanel.reviews,QuickChatFAB,QuickChatFAB.shared-cache,ReliabilityView,ResearchView,SecretsView,SecretsView.mobile,SettingsModal,SettingsModal.testMode,SettingsModal.worktrunk,StashConflictModal,StashRecoveryView,TaskCard,TaskCard.badge-height,TaskCard.badge-wrap,TaskCard.footer-wrap,TaskChangesTab,TaskComments,TaskDetailModal,TaskDetailModal.allow-resurrection,TaskDetailModal.create-pr-e2e,TestModeBanner,TaskDetailModal.create-pr-integration,TaskDetailModal.github-tracking-header,TaskDetailModal.github-tracking-stale,TaskDetailModal.rebind-banner,TaskDocumentsTab,TaskForm,TaskIdIntegrityBanner,TrackingRepoSelect,WorkflowResultsTab,WorktrunkInstallApprovalDetails}.test.tsx",
|
const qualityAppFoundationUiTests = [
|
||||||
|
// Top-level UI, auth, shell, mobile layout, and styling regressions.
|
||||||
|
// Keep these as explicit file paths: this shard completes reliably when the
|
||||||
|
// worker starts from a concrete file list instead of the broad glob.
|
||||||
|
"app/__tests__/AgentMentionPopup.css.test.ts",
|
||||||
|
"app/__tests__/App.boot-gate.test.tsx",
|
||||||
|
"app/__tests__/App.shell-onboarding.test.tsx",
|
||||||
|
"app/__tests__/ShellContext.test.tsx",
|
||||||
|
"app/__tests__/activity-feed-theme-styling.test.ts",
|
||||||
|
"app/__tests__/activity-log-mobile-layout.test.ts",
|
||||||
|
"app/__tests__/agent-css-classes.test.ts",
|
||||||
|
"app/__tests__/agent-runs-ui.test.ts",
|
||||||
|
"app/__tests__/auth.test.ts",
|
||||||
|
"app/__tests__/board-mobile-corner-rendering.test.ts",
|
||||||
|
"app/__tests__/board-tablet-overflow.test.ts",
|
||||||
|
"app/__tests__/browser-layout-smoke-fixture.test.ts",
|
||||||
|
"app/__tests__/chat-tool-calls-mobile-layout.test.ts",
|
||||||
|
"app/__tests__/column-fixed-width.test.ts",
|
||||||
|
"app/__tests__/component-css-no-raw-rgba.test.ts",
|
||||||
|
"app/__tests__/dashboard-component-color-tokenization.test.ts",
|
||||||
|
"app/__tests__/dashboard-footer-mobile-layout.test.ts",
|
||||||
|
"app/__tests__/detail-body-mobile-overflow.test.ts",
|
||||||
|
"app/__tests__/dev-server-layout-css.test.ts",
|
||||||
|
"app/__tests__/executor-status-bar-theme.test.ts",
|
||||||
|
"app/__tests__/footer-safe-layout.test.ts",
|
||||||
|
"app/__tests__/git-manager-theme-styling.test.ts",
|
||||||
|
"app/__tests__/index-html-theme-link.test.ts",
|
||||||
|
"app/__tests__/insight-model-selector.test.tsx",
|
||||||
|
"app/__tests__/lazy-loaded-views-docs.test.ts",
|
||||||
|
"app/__tests__/mission-planning-modals-mobile.test.ts",
|
||||||
|
"app/__tests__/mobile-bottom-bars-keyboard-layout.test.ts",
|
||||||
|
"app/__tests__/mobile-feature-access-regression.test.tsx",
|
||||||
|
"app/__tests__/mobile-header-controls.test.ts",
|
||||||
|
"app/__tests__/mobile-input-font-size.test.ts",
|
||||||
|
"app/__tests__/mobile-nav-bar-css.test.ts",
|
||||||
|
"app/__tests__/mobile-planning-input-font-size.test.ts",
|
||||||
|
"app/__tests__/mobile-scripts.test.ts",
|
||||||
|
"app/__tests__/mobile-scroll-snap.test.ts",
|
||||||
|
"app/__tests__/no-legacy-public.test.ts",
|
||||||
|
"app/__tests__/onboarding-overlay-layering.test.ts",
|
||||||
|
"app/__tests__/pwa.test.ts",
|
||||||
|
"app/__tests__/quick-chat-mobile-keyboard-layout.test.ts",
|
||||||
|
"app/__tests__/quick-chat-session-dropdown.test.ts",
|
||||||
|
"app/__tests__/quick-chat-tool-calls-mobile-layout.test.ts",
|
||||||
|
"app/__tests__/quick-entry-expanded-height.test.tsx",
|
||||||
|
"app/__tests__/settings-mobile-wrap.test.ts",
|
||||||
|
"app/__tests__/setup-wizard-modal-layout.test.ts",
|
||||||
|
"app/__tests__/shell-host.test.ts",
|
||||||
|
"app/__tests__/shell-native.test.ts",
|
||||||
|
"app/__tests__/spinner-animation.css.test.ts",
|
||||||
|
"app/__tests__/sse-bus.test.ts",
|
||||||
|
"app/__tests__/status-colors-theme.test.ts",
|
||||||
|
"app/__tests__/swUpdate.test.ts",
|
||||||
|
"app/__tests__/tablet-header-controls.test.tsx",
|
||||||
|
"app/__tests__/task-detail-modal-tablet-width.test.ts",
|
||||||
|
"app/__tests__/terminal-input.test.ts",
|
||||||
|
"app/__tests__/terminal-mobile-header-row.test.ts",
|
||||||
|
"app/__tests__/terminal-mobile-keyboard-layout.test.ts",
|
||||||
|
"app/__tests__/text-token-canonicalization.test.ts",
|
||||||
|
"app/__tests__/versionCheck.test.ts",
|
||||||
|
"app/__tests__/viewport-compensation-keyboard.test.ts",
|
||||||
|
];
|
||||||
|
|
||||||
|
const qualityAppHooksAndUtilsTests = [
|
||||||
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
||||||
"app/context/**/*.test.tsx",
|
"app/context/**/*.test.tsx",
|
||||||
"app/hooks/__tests__/{useAgents,useAgentLogs,useAgentLogs.resume-instrumentation,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodes.resume-instrumentation,useNodeSettingsSync,useProjects,useProjects.resume-instrumentation,useMeshState.resume-instrumentation,useManagedDockerNodes.resume-instrumentation,usePrChecksStream.resume-instrumentation,useDevServerLogs.resume-instrumentation,useResearch.resume-instrumentation,useBackgroundSessions.resume-instrumentation,useQuickChat,useTasks,useTasks.resume-instrumentation,useChatRooms.resume-instrumentation,useTerminalSessions,useTheme,useToast,useUsageData,useViewState,useMergeAdvanceNotice}.test.{ts,tsx}",
|
"app/hooks/__tests__/{useAgents,useAgentLogs,useAgentLogs.resume-instrumentation,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodes.resume-instrumentation,useNodeSettingsSync,useProjects,useProjects.resume-instrumentation,useMeshState.resume-instrumentation,useManagedDockerNodes.resume-instrumentation,usePrChecksStream.resume-instrumentation,useDevServerLogs.resume-instrumentation,useResearch.resume-instrumentation,useBackgroundSessions.resume-instrumentation,useQuickChat,useTasks,useTasks.resume-instrumentation,useChatRooms.resume-instrumentation,useTerminalSessions,useTheme,useToast,useUsageData,useViewState,useMergeAdvanceNotice}.test.{ts,tsx}",
|
||||||
"app/utils/**/*.test.{ts,tsx}",
|
"app/utils/**/*.test.{ts,tsx}",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const qualityAppComponentTests = [
|
||||||
|
"ActiveAgentsPanel",
|
||||||
|
"ActivityLogModal",
|
||||||
|
"AgentMentionPopup",
|
||||||
|
"AgentMetricsBar",
|
||||||
|
"AgentOnboardingModal",
|
||||||
|
"AgentReflectionsTab",
|
||||||
|
"AgentTokenStatsPanel",
|
||||||
|
"App",
|
||||||
|
"AuthTokenRecoveryDialog",
|
||||||
|
"Board",
|
||||||
|
"board-mobile",
|
||||||
|
"board-mobile-view-switch",
|
||||||
|
"BranchGroupCard",
|
||||||
|
"ChatView",
|
||||||
|
"ChatView.autosize",
|
||||||
|
"ChatView.chat-input-autosize",
|
||||||
|
"ChatView.default-model-icon",
|
||||||
|
"ChatView.draft",
|
||||||
|
"ChatView.hash-mention",
|
||||||
|
"ChatView.rooms",
|
||||||
|
"ChatView.scroll-to-top",
|
||||||
|
"ChatView.swipe-back",
|
||||||
|
"Column",
|
||||||
|
"ConfirmDialog",
|
||||||
|
"ConversationHistory",
|
||||||
|
"DashboardLoader",
|
||||||
|
"DevServerView.mobile",
|
||||||
|
"DirectoryPicker",
|
||||||
|
"DuplicateWarningModal",
|
||||||
|
"ErrorBoundary",
|
||||||
|
"ExecutorStatusBar",
|
||||||
|
"FileBrowser",
|
||||||
|
"FileEditor",
|
||||||
|
"GitHubBadge",
|
||||||
|
"GroupTaskModal",
|
||||||
|
"InlineCreateCard",
|
||||||
|
"LoginInstructions",
|
||||||
|
"MemoryView",
|
||||||
|
"MergeAdvanceNotice",
|
||||||
|
"MessageComposer",
|
||||||
|
"MessageComposer.autosize",
|
||||||
|
"MobileNavBar",
|
||||||
|
"NewTaskModal",
|
||||||
|
"NewTaskModal.shared-cache",
|
||||||
|
"NodeCard",
|
||||||
|
"NodeHealthDot",
|
||||||
|
"NodeStatusIndicator",
|
||||||
|
"PlanningModeModal.autosize",
|
||||||
|
"PrChecksList",
|
||||||
|
"PrCreateModal",
|
||||||
|
"PrCreateModal.layout",
|
||||||
|
"ProjectCard",
|
||||||
|
"ProjectSelector",
|
||||||
|
"ProviderIcon",
|
||||||
|
"PrPanel",
|
||||||
|
"PrPanel.merge",
|
||||||
|
"PrPanel.reviews",
|
||||||
|
"QuickChatFAB",
|
||||||
|
"QuickChatFAB.shared-cache",
|
||||||
|
"ReliabilityView",
|
||||||
|
"ResearchView",
|
||||||
|
"SecretsView",
|
||||||
|
"SecretsView.mobile",
|
||||||
|
"SettingsModal",
|
||||||
|
"SettingsModal.testMode",
|
||||||
|
"SettingsModal.worktrunk",
|
||||||
|
"StashConflictModal",
|
||||||
|
"StashRecoveryView",
|
||||||
|
"TaskCard",
|
||||||
|
"TaskCard.badge-height",
|
||||||
|
"TaskCard.badge-wrap",
|
||||||
|
"TaskCard.footer-wrap",
|
||||||
|
"TaskChangesTab",
|
||||||
|
"TaskComments",
|
||||||
|
"TaskDetailModal",
|
||||||
|
"TaskDetailModal.allow-resurrection",
|
||||||
|
"TaskDetailModal.create-pr-e2e",
|
||||||
|
"TestModeBanner",
|
||||||
|
"TaskDetailModal.create-pr-integration",
|
||||||
|
"TaskDetailModal.github-tracking-header",
|
||||||
|
"TaskDetailModal.github-tracking-stale",
|
||||||
|
"TaskDetailModal.rebind-banner",
|
||||||
|
"TaskDocumentsTab",
|
||||||
|
"TaskForm",
|
||||||
|
"TaskIdIntegrityBanner",
|
||||||
|
"TrackingRepoSelect",
|
||||||
|
"WorkflowResultsTab",
|
||||||
|
"WorktrunkInstallApprovalDetails",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const isolatedQualityAppComponentTests = ["App", "ChatView", "SettingsModal"] as const;
|
||||||
|
const batchedQualityAppComponentTests = qualityAppComponentTests.filter(
|
||||||
|
(testName) => !isolatedQualityAppComponentTests.includes(testName),
|
||||||
|
);
|
||||||
|
const batchedQualityAppSplitIndex = Math.ceil(batchedQualityAppComponentTests.length / 2);
|
||||||
|
const batchedQualityAppComponentTestsA = batchedQualityAppComponentTests.slice(0, batchedQualityAppSplitIndex);
|
||||||
|
const batchedQualityAppComponentTestsB = batchedQualityAppComponentTests.slice(batchedQualityAppSplitIndex);
|
||||||
|
|
||||||
|
function buildComponentQualityInclude(testNames: readonly string[]): string[] {
|
||||||
|
return testNames.length > 0
|
||||||
|
? [`app/components/__tests__/{${testNames.join(",")}}.test.tsx`]
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const qualityAppTests = [
|
||||||
|
...qualityAppFoundationApiTests,
|
||||||
|
...qualityAppFoundationUiTests,
|
||||||
|
...qualityAppHooksAndUtilsTests,
|
||||||
|
...buildComponentQualityInclude(qualityAppComponentTests),
|
||||||
|
];
|
||||||
|
|
||||||
|
const qualityAppFoundationApiShardTests = [...qualityAppFoundationApiTests];
|
||||||
|
const qualityAppFoundationUiShardTests = [...qualityAppFoundationUiTests];
|
||||||
|
const qualityAppFoundationHooksAndUtilsTests = [...qualityAppHooksAndUtilsTests];
|
||||||
|
const qualityAppComponentBatchATests = buildComponentQualityInclude(batchedQualityAppComponentTestsA);
|
||||||
|
const qualityAppComponentBatchBTests = buildComponentQualityInclude(batchedQualityAppComponentTestsB);
|
||||||
|
|
||||||
|
const qualityAppAppOnlyTests = ["app/components/__tests__/App.test.tsx"];
|
||||||
|
const qualityAppChatOnlyTests = ["app/components/__tests__/ChatView.test.tsx"];
|
||||||
|
const qualityAppSettingsOnlyTests = ["app/components/__tests__/SettingsModal.test.tsx"];
|
||||||
|
|
||||||
|
|
||||||
const qualityApiTests = [
|
const qualityApiTests = [
|
||||||
// Critical HTTP/server behavior: auth, task/project/settings mutation,
|
// Critical HTTP/server behavior: auth, task/project/settings mutation,
|
||||||
// git/GitHub, agents, nodes, chat/files, realtime, and isolation guards.
|
// git/GitHub, agents, nodes, chat/files, realtime, and isolation guards.
|
||||||
"src/__tests__/{api-error,auth-middleware,auth-middleware-integration,chat-attachment-routes,chat-routes,file-service,github,github-webhooks,initialize,planning-flow-diagnostics-guardrail,pr-routes-auto-merge,pr-routes.contract,project-routes,project-store-resolver,register-git-github.pr-options-preflight-metadata,remote-access-routes,remote-auth,routes-agent-budget,routes-agent-keys,routes-agent-permissions,routes-agent-ratings,routes-agent-runs,routes-agent-soul-memory,routes-agents,routes-automation,routes-branch-groups,routes-git,routes-github,routes-merge-advance-push-origin,routes-nodes,routes-nodes-sync-contract,routes-planning,routes-secrets-sync,routes-settings,routes-task-commit-associations,routes-tasks,routes-tasks-deterministic-dedup,routes-tasks-duplicate-check,routes-tasks-explicit-duplicate-marker,server,server-static-assets,server-webhook,server.events,setup-routes,sse,sse-buffer,test-isolation-guard,update-check-route,websocket,recover-branch-binding-route}.test.ts",
|
"src/__tests__/{api-error,auth-middleware,auth-middleware-integration,chat-attachment-routes,chat-routes,file-service,github,github-webhooks,initialize,planning-flow-diagnostics-guardrail,pr-routes-auto-merge,pr-routes.contract,project-routes,project-store-resolver,register-git-github.pr-options-preflight-metadata,remote-access-routes,remote-auth,routes-agent-budget,routes-agent-keys,routes-agent-permissions,routes-agent-ratings,routes-agent-runs,routes-agent-soul-memory,routes-agents,routes-automation,routes-branch-groups,routes-git,routes-github,routes-merge-advance-push-origin,routes-nodes,routes-nodes-sync-contract,routes-planning,routes-secrets-sync,routes-settings,routes-task-commit-associations,routes-tasks,routes-tasks-deterministic-dedup,routes-tasks-duplicate-check,routes-tasks-explicit-duplicate-marker,server,server-static-assets,server-webhook,server.events,setup-routes,sse,sse-buffer,test-isolation-guard,update-check-route,websocket,recover-branch-binding-route}.test.ts",
|
||||||
|
"src/__tests__/dashboard-test-config-guard.test.ts",
|
||||||
"src/routes/__tests__/{custom-provider-routes,custom-providers,register-docker-node-routes,register-diagnostics-routes,stash-recovery-routes}.test.ts",
|
"src/routes/__tests__/{custom-provider-routes,custom-providers,register-docker-node-routes,register-diagnostics-routes,stash-recovery-routes}.test.ts",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -92,6 +279,78 @@ export default defineConfig({
|
|||||||
css: { include: [/app\//] },
|
css: { include: [/app\//] },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-foundation-api",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppFoundationApiShardTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-foundation-ui",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppFoundationUiShardTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-foundation-hooks-utils",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppFoundationHooksAndUtilsTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-components-a",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppComponentBatchATests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-components-b",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppComponentBatchBTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-app",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppAppOnlyTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-chat",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppChatOnlyTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
test: {
|
||||||
|
name: "dashboard-app-quality-settings",
|
||||||
|
environment: "jsdom",
|
||||||
|
include: qualityAppSettingsOnlyTests,
|
||||||
|
css: { include: [/app\//] },
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
extends: true,
|
extends: true,
|
||||||
test: {
|
test: {
|
||||||
|
|||||||
Reference in New Issue
Block a user