fix(todos): restore clean build and navigation coverage (#3321)

## Summary
- remove an unavailable jest-dom type from the Todo plugin production
TypeScript build
- update the dashboard navigation fixture for the plugin-owned Todo
destination and root test id

## Test plan
- `corepack pnpm --filter @fusion-plugin-examples/todos build`
- `corepack pnpm --filter @fusion-plugin-examples/todos test`
- `FUSION_DASHBOARD_DEEP=1 corepack pnpm --filter @fusion/dashboard exec
vitest run app/components/__tests__/navigation-history.test.tsx
--project dashboard-app-quality-components-a --silent=passed-only
--reporter=dot`
- `corepack pnpm check:changesets`

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

* **New Features**
  * Restored CLI packaging for the bundled Todo Lists plugin.
  * Made `AgentStore` available to bundled plugins at runtime.
* **Tests**
* Updated navigation coverage for Todo Lists dashboard views, overflow
placement, and ordering.
* Added coverage for opening and dismissing the Todo view through
browser history navigation.
  * Improved validation of runtime exports.
* **Chores**
  * Simplified test type configuration for the Todo Lists plugin.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Phil Larson
2026-08-03 12:14:31 -07:00
committed by GitHub
parent 4ff41a723c
commit 5d8d494230
5 changed files with 85 additions and 9 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Restore clean CLI packaging for the bundled Todo Lists plugin.
category: fix
dev: Re-exports AgentStore through the bundled plugin core runtime shim.

View File

@@ -1,11 +1,39 @@
import { describe, it, expect } from "vitest";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import ts from "typescript";
import { definePlugin, validatePluginManifest } from "@fusion/plugin-sdk";
import { applyPrepackTransform } from "../../scripts/prepare-publish-manifest.mjs";
const workspaceRoot = join(__dirname, "..", "..", "..", "..");
function executableModuleSpecifiers(source: string): string[] {
const sourceFile = ts.createSourceFile("artifact.js", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
const specifiers: string[] = [];
const visit = (node: ts.Node): void => {
if (
(ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
node.moduleSpecifier &&
ts.isStringLiteralLike(node.moduleSpecifier)
) {
specifiers.push(node.moduleSpecifier.text);
} else if (
ts.isCallExpression(node) &&
(node.expression.kind === ts.SyntaxKind.ImportKeyword ||
(ts.isIdentifier(node.expression) && node.expression.text === "require")) &&
node.arguments.length === 1 &&
ts.isStringLiteralLike(node.arguments[0])
) {
specifiers.push(node.arguments[0].text);
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
return specifiers;
}
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;
@@ -43,7 +71,7 @@ describe("plugin-sdk export surface", () => {
expect(tsupRaw).toContain("/^@fusion\\//");
});
it("uses a runtime-only core shim that bundles schema source and Quality supervision without core dist", () => {
it("uses a runtime-only core shim that bundles required plugin values 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");
@@ -51,6 +79,12 @@ describe("plugin-sdk export surface", () => {
expect(tsupRaw).toContain('"plugin-sdk-core-runtime-shim.mjs"');
expect(shimRaw).toContain('from "../../core/src/postgres/schema/index.js"');
/*
* FNXC:BundledPlugins 2026-08-03-18:39:
* Bundled plugins must receive runtime values through the CLI source shim so clean package builds never leave private `@fusion/core` imports unresolved.
*/
expect(shimRaw).toContain('from "../../core/src/agents/agent-store.js"');
expect(shimRaw).toContain("export { AgentStore, postgresSchema }");
expect(shimRaw).toContain("export function superviseSpawn");
expect(shimRaw).not.toContain("../../core/dist/");
});
@@ -61,7 +95,27 @@ describe("plugin-sdk export surface", () => {
return;
}
const built = readFileSync(distPath, "utf-8");
expect(built.includes("@fusion/")).toBe(false);
const fusionRuntimeSpecifiers = executableModuleSpecifiers(built).filter((specifier) =>
specifier.startsWith("@fusion/"),
);
expect(fusionRuntimeSpecifiers).toEqual([]);
});
it("distinguishes executable @fusion specifiers from documentation text", () => {
const source = [
'import value from "@fusion/static";',
'import /* comment */ ("@fusion/dynamic");',
'require(/* comment */ "@fusion/commonjs");',
'export {} from /* comment */ "@fusion/exported";',
'const docs = "Run pnpm --filter @fusion/core test";',
].join("\n");
expect(executableModuleSpecifiers(source)).toEqual([
"@fusion/static",
"@fusion/dynamic",
"@fusion/commonjs",
"@fusion/exported",
]);
});
it("has no @fusion specifiers in built plugin-sdk declaration artifact when present", () => {

View File

@@ -9,8 +9,13 @@ import { spawn } from "node:child_process";
* and bundles every runtime export without a private @fusion/core dependency.
*/
import * as postgresSchema from "../../core/src/postgres/schema/index.js";
import { AgentStore } from "../../core/src/agents/agent-store.js";
export { postgresSchema };
/*
* FNXC:BundledPlugins 2026-08-03-17:18:
* The bundled Todo plugin lists project agents through AgentStore. Re-export the source implementation from the runtime shim so clean CLI packaging does not leave a private @fusion/core runtime import unresolved.
*/
export { AgentStore, postgresSchema };
/*
* FNXC:BundledPlugins 2026-07-31-09:55:

View File

@@ -57,7 +57,18 @@ vi.mock("../../api", async (importOriginal) => {
fetchAgents: vi.fn(() => Promise.resolve([])),
fetchTaskDetail: vi.fn((id: string) => Promise.resolve({ id, title: `Task ${id}` })),
fetchUnreadCount: vi.fn(() => Promise.resolve({ unreadCount: 0 })),
fetchPluginDashboardViews: vi.fn(() => Promise.resolve([])),
// FNXC:TodoNavigation 2026-08-03-17:18: Todo Lists is plugin-owned; keep this fixture production-shaped so the overflow destination uses the runtime manifest contribution.
fetchPluginDashboardViews: vi.fn(() => Promise.resolve([{
pluginId: "fusion-plugin-todos",
view: {
viewId: "todos",
label: "Todos",
componentPath: "./dashboard-view",
icon: "CheckSquare",
placement: "overflow",
order: 70,
},
}])),
fetchExecutorStats: vi.fn(() => Promise.resolve({
globalPause: false,
enginePaused: false,
@@ -554,16 +565,16 @@ describe("Navigation history integration", () => {
const pushCallsBefore = (window.history.pushState as any).mock.calls.length;
fireEvent.click(screen.getByTestId("view-toggle-overflow-trigger"));
fireEvent.click(screen.getByTestId("view-overflow-todos"));
fireEvent.click(await screen.findByTestId("view-overflow-plugin-fusion-plugin-todos-todos"));
await waitFor(() => {
expect(screen.getByTestId("todo-view")).toBeTruthy();
expect(screen.getByTestId("todo-view-root")).toBeTruthy();
});
expect((window.history.pushState as any).mock.calls.length).toBeGreaterThan(pushCallsBefore);
dispatchPopState({ navIndex: 0 });
await waitFor(() => {
expect(screen.queryByTestId("todo-view")).toBeNull();
expect(screen.queryByTestId("todo-view-root")).toBeNull();
expect(screen.getByTestId("board-view")).toBeTruthy();
});
});

View File

@@ -11,8 +11,7 @@
},
"types": [
"node",
"vitest/globals",
"@testing-library/jest-dom"
"vitest/globals"
]
},
"include": [