Files
fusion/packages/cli/src/__tests__/plugin-sdk-export.test.ts
gsxdsm e3f98253cc feat: Quality plugin — Task QA tab, preview servers, tests, and suggested cases (#2127)
## Summary

Adds a bundled **Quality** plugin (`fusion-plugin-quality`) that makes
task QA easier and more visual:

- **Task QA tab** (action-first): preview/test server for the task
worktree, allowlisted test runs, report viewer, screenshots CTA,
suggested test cases, CI handoff
- **Quality hub** (left sidebar): project-wide run history and preset
launches
- Host **task-detail slot context** (`taskId`, worktree, `projectId`) so
plugin tabs can scope correctly
- `superviseSpawn` re-exported on the plugin packaging shim for
published plugins
- Plan: `docs/plans/2026-07-14-001-feat-quality-plugin-plan.md`

## Design constraints

- Does **not** replace the merge gate — advisory orchestration only
- Composes Dev Server process patterns and artifact registry (no second
browser stack)
- Never free-form shell; never port 4040
- Full-suite requires explicit confirm

## Test plan

- [x] `pnpm --filter @fusion-plugin-examples/quality test` (15 tests)
- [x] PluginSlot unit tests still pass
- [ ] Enable Quality plugin in dashboard Settings → Built-in Plugins
- [ ] Open Task Detail → **QA** tab with a worktree; start preview, run
verify:fast, generate suggestions
- [ ] Open left sidebar **Quality** hub and list runs
- [ ] Confirm merge gate / PR checks unchanged

## Residual / follow-up (same plan, later units)

- Deeper hub CI (host route)
- Full browser-verification toggle UX + agent QA sessions (U7/U9/U10)
- Richer screenshots gallery wiring to live artifacts API
- Test plans CRUD polish

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added the Quality plugin with a project Quality hub and task-focused
QA tab.
* Added test runs, reports, preview server controls, suggested test
cases, and run history.
* Added configurable test presets, cancellation, status tracking, and
safe command execution.
* Added experimental-feature controls for enabling Quality
functionality.
* Bundled Quality with the CLI and made it available through the plugin
manager.

* **Documentation**
* Added Quality plugin guidance, terminology, configuration details, and
implementation planning documentation.

* **Bug Fixes**
* Improved process supervision so command failures and shutdown timers
are handled safely.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 20:28:11 -07:00

76 lines
3.4 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { definePlugin, validatePluginManifest } from "@fusion/plugin-sdk";
import { applyPrepackTransform } from "../../scripts/prepare-publish-manifest.mjs";
const workspaceRoot = join(__dirname, "..", "..", "..", "..");
describe("plugin-sdk export surface", () => {
it("keeps definePlugin as identity and validates manifests", () => {
const plugin = { manifest: { id: "demo-plugin", name: "Demo", version: "1.0.0" } } as any;
expect(definePlugin(plugin)).toBe(plugin);
expect(validatePluginManifest(plugin.manifest)).toEqual({ valid: true, errors: [] });
expect(validatePluginManifest({ id: "Bad_ID", name: "", version: "nope" }).valid).toBe(false);
});
it("injects plugin-sdk subpath export into prepack manifest", () => {
const pkgPath = join(workspaceRoot, "packages", "cli", "package.json");
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
const transformed = applyPrepackTransform(pkg);
expect(transformed.exports["./plugin-sdk"]).toEqual({
types: "./dist/plugin-sdk/index.d.ts",
import: "./dist/plugin-sdk/index.js",
});
expect(transformed.exports["./package.json"]).toBe("./package.json");
// The runfusion.ai alias imports `@runfusion/fusion/dist/bin.js`; the
// injected exports field must keep `./dist/*` subpaths resolvable or the
// pre-publish smoke test fails with ERR_PACKAGE_PATH_NOT_EXPORTED.
expect(transformed.exports["./dist/*"]).toBe("./dist/*");
expect(transformed.bin).toEqual(pkg.bin);
expect(transformed.pi).toEqual(pkg.pi);
});
it("declares plugin-sdk tsup build entry with dts and fusion inlining", () => {
const tsupPath = join(workspaceRoot, "packages", "cli", "tsup.config.ts");
const tsupRaw = readFileSync(tsupPath, "utf-8");
expect(tsupRaw).toContain('"plugin-sdk/index"');
expect(tsupRaw).toContain('"..", "plugin-sdk", "src", "index.ts"');
expect(tsupRaw).toContain("dts:");
expect(tsupRaw).toContain("/^@fusion\\//");
});
it("uses a runtime-only core shim that bundles schema source and Quality supervision without core dist", () => {
const tsupPath = join(workspaceRoot, "packages", "cli", "tsup.config.ts");
const tsupRaw = readFileSync(tsupPath, "utf-8");
const shimPath = join(workspaceRoot, "packages", "cli", "src", "plugin-sdk-core-runtime-shim.mjs");
const shimRaw = readFileSync(shimPath, "utf-8");
expect(tsupRaw).toContain('"plugin-sdk-core-runtime-shim.mjs"');
expect(shimRaw).toContain('from "../../core/src/postgres/schema/index.js"');
expect(shimRaw).toContain("export function superviseSpawn");
expect(shimRaw).not.toContain("../../core/dist/");
});
it("has no @fusion runtime specifiers in built plugin-sdk artifact when present", () => {
const distPath = join(workspaceRoot, "packages", "cli", "dist", "plugin-sdk", "index.js");
if (!existsSync(distPath)) {
return;
}
const built = readFileSync(distPath, "utf-8");
expect(built.includes("@fusion/")).toBe(false);
});
it("has no @fusion specifiers in built plugin-sdk declaration artifact when present", () => {
const distPath = join(workspaceRoot, "packages", "cli", "dist", "plugin-sdk", "index.d.ts");
if (!existsSync(distPath)) {
return;
}
const built = readFileSync(distPath, "utf-8");
expect(built.includes("@fusion/")).toBe(false);
});
});