Files
fusion/packages/dashboard/src/__tests__/dev-server-detect.test.ts
Fusion 5de8c8dca7 feat(FN-2181): add dashboard dev-server process manager and API routes
- Introduce DevServerStore and DevServerProcessManager with a simplified store API contract
- Add dev-server script detection and route legacy manager behavior through the new process manager
- Add a dedicated dev-server router with start/stop/status/log endpoints and SSE event streaming
- Wire dev-server routes into the main route table and hook manager shutdown into server teardown
- Expand tests and architecture docs for store, process, route, detection, and SSE coverage
2026-04-19 20:48:14 -07:00

188 lines
6.2 KiB
TypeScript

// @vitest-environment node
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { detectDevServerScripts } from "../dev-server-detect.js";
function writeJson(filePath: string, value: unknown): void {
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
}
describe("detectDevServerScripts", () => {
let tempDir: string;
beforeEach(() => {
tempDir = mkdtempSync(path.join(os.tmpdir(), "fn-dev-detect-"));
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
it("detects dev script in root package.json", async () => {
writeJson(path.join(tempDir, "package.json"), {
scripts: { dev: "vite" },
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toHaveLength(1);
expect(result.candidates[0]).toMatchObject({
name: "dev",
command: "vite",
source: "root",
});
});
it("detects multiple matching scripts", async () => {
writeJson(path.join(tempDir, "package.json"), {
scripts: {
dev: "vite",
start: "next dev",
preview: "vite preview",
},
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
expect(result.candidates.map((candidate) => candidate.name).sort()).toEqual(["dev", "preview", "start"]);
});
it("returns empty candidates when no matching scripts", async () => {
writeJson(path.join(tempDir, "package.json"), {
scripts: { test: "vitest", build: "vite build" },
});
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toEqual([]);
});
it("returns empty candidates when package.json does not exist", async () => {
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toEqual([]);
});
it("returns empty candidates when package.json has no scripts field", async () => {
writeJson(path.join(tempDir, "package.json"), { name: "demo" });
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toEqual([]);
});
it("monorepo detection scans packages/*/package.json", async () => {
writeJson(path.join(tempDir, "package.json"), { name: "root" });
const pkgDir = path.join(tempDir, "packages", "web");
mkdirSync(pkgDir, { recursive: true });
writeJson(path.join(pkgDir, "package.json"), {
name: "@demo/web",
scripts: { dev: "vite" },
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toContainEqual(
expect.objectContaining({
name: "dev",
source: "packages/web",
}),
);
});
it("monorepo detection scans apps/*/package.json", async () => {
writeJson(path.join(tempDir, "package.json"), { name: "root" });
const appDir = path.join(tempDir, "apps", "client");
mkdirSync(appDir, { recursive: true });
writeJson(path.join(appDir, "package.json"), {
name: "client",
scripts: { dev: "vite" },
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toContainEqual(
expect.objectContaining({
name: "dev",
source: "apps/client",
}),
);
});
it("confidence scoring gives dev higher score than serve", async () => {
writeJson(path.join(tempDir, "package.json"), {
scripts: { dev: "vite", serve: "vite preview" },
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
const dev = result.candidates.find((candidate) => candidate.name === "dev");
const serve = result.candidates.find((candidate) => candidate.name === "serve");
expect(dev).toBeDefined();
expect(serve).toBeDefined();
expect((dev?.confidence ?? 0)).toBeGreaterThan(serve?.confidence ?? 0);
});
it("framework indicators boost confidence", async () => {
writeJson(path.join(tempDir, "package.json"), { name: "root" });
const withFrameworkDir = path.join(tempDir, "apps", "with-framework");
mkdirSync(withFrameworkDir, { recursive: true });
writeJson(path.join(withFrameworkDir, "package.json"), {
scripts: { serve: "vite" },
devDependencies: { vite: "^6.0.0" },
private: true,
});
const withoutFrameworkDir = path.join(tempDir, "apps", "without-framework");
mkdirSync(withoutFrameworkDir, { recursive: true });
writeJson(path.join(withoutFrameworkDir, "package.json"), {
scripts: { serve: "custom-server" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
const withFramework = result.candidates.find((candidate) => candidate.source === "apps/with-framework");
const withoutFramework = result.candidates.find((candidate) => candidate.source === "apps/without-framework");
expect(withFramework).toBeDefined();
expect(withoutFramework).toBeDefined();
expect((withFramework?.confidence ?? 0)).toBeGreaterThan(withoutFramework?.confidence ?? 0);
});
it("results are sorted by confidence descending", async () => {
writeJson(path.join(tempDir, "package.json"), {
scripts: {
preview: "vite preview",
dev: "vite",
serve: "vite serve",
},
devDependencies: { vite: "^6.0.0" },
private: true,
});
const result = await detectDevServerScripts(tempDir);
const confidences = result.candidates.map((candidate) => candidate.confidence);
expect(confidences).toEqual([...confidences].sort((a, b) => b - a));
});
it("handles malformed package.json gracefully", async () => {
writeFileSync(path.join(tempDir, "package.json"), "{invalid", "utf-8");
const pkgDir = path.join(tempDir, "packages", "web");
mkdirSync(pkgDir, { recursive: true });
writeFileSync(path.join(pkgDir, "package.json"), "{broken", "utf-8");
const result = await detectDevServerScripts(tempDir);
expect(result.candidates).toEqual([]);
});
});