feat(FN-4050): add one-click install support for reports plugin

- Add reports plugin to bundled plugin resolver so it can be installed from packaged assets
- Expose reports plugin in dashboard plugin manager and server routes for one-click install flows
- Add CLI and dashboard test coverage for bundled install behavior and plugin route availability
- Update reports/plugin management/settings docs and include a changeset for @runfusion/fusion
This commit is contained in:
Fusion
2026-05-11 21:48:19 -07:00
committed by gsxdsm
parent 4404c6103e
commit 2963923571
11 changed files with 148 additions and 2 deletions

View File

@@ -123,6 +123,13 @@ const BUILTIN_PLUGINS: BuiltinPlugin[] = [
category: "integration",
path: "./plugins/fusion-plugin-dependency-graph",
},
{
id: "fusion-plugin-reports",
name: "Reports",
description: "View report history, compare runs side-by-side, and export standalone HTML summaries.",
category: "integration",
path: "./plugins/fusion-plugin-reports",
},
{
id: "fusion-plugin-whatsapp-chat",
name: "WhatsApp Chat",

View File

@@ -267,6 +267,7 @@ describe("PluginManager", () => {
expect(screen.getByText("OpenClaw Runtime")).toBeTruthy();
expect(screen.getByText("Droid Runtime")).toBeTruthy();
expect(screen.getByText("Dependency Graph")).toBeTruthy();
expect(screen.getByText("Reports")).toBeTruthy();
expect(screen.getByText("WhatsApp Chat")).toBeTruthy();
expect(screen.getByText("CLI Printing Press")).toBeTruthy();
expect(screen.getByText(/Pairs to WhatsApp Web \(multi-device\) with QR or pairing code/i)).toBeTruthy();
@@ -426,6 +427,26 @@ describe("PluginManager", () => {
});
});
it("installs reports from the built-in section", async () => {
render(<PluginManager addToast={addToast} />);
await waitFor(() => {
expect(fetchPlugins).toHaveBeenCalled();
});
const reportsLabel = await screen.findByText("Reports");
const reportsCard = reportsLabel.closest(".plugin-builtins-item");
expect(reportsCard).toBeTruthy();
const installButton = within(reportsCard as HTMLElement).getByRole("button", { name: /Install Reports/i });
await userEvent.click(installButton);
await waitFor(() => {
expect(installPlugin).toHaveBeenCalledWith({ path: "./plugins/fusion-plugin-reports" }, undefined);
expect(addToast).toHaveBeenCalledWith("Reports installed globally", "success");
});
});
it("installs CLI Printing Press from the built-in section", async () => {
render(<PluginManager addToast={addToast} />);
@@ -842,6 +863,37 @@ describe("PluginManager", () => {
expect(screen.getByTestId("plugin-manager-detail")).toBeTruthy();
});
it("shows Manage for installed reports in built-in section", async () => {
vi.mocked(fetchPlugins).mockResolvedValueOnce([
{
...mockPlugins[0],
id: "fusion-plugin-reports",
name: "Reports",
},
]);
render(<PluginManager addToast={addToast} />);
await waitFor(() => {
expect(screen.getAllByText("Reports").length).toBeGreaterThanOrEqual(2);
});
const reportsCard = screen.getAllByText("Reports")[1]?.closest(".plugin-builtins-item");
expect(reportsCard).toBeTruthy();
const manageButton = within(reportsCard as HTMLElement).getByRole("button", { name: /^Manage$/i });
expect(manageButton).not.toBeDisabled();
expect(within(reportsCard as HTMLElement).getAllByText("Installed").length).toBeGreaterThanOrEqual(1);
await userEvent.click(manageButton);
await waitFor(() => {
expect(fetchPluginSettings).toHaveBeenCalledWith("fusion-plugin-reports", undefined);
});
expect(screen.getByTestId("plugin-manager-detail")).toBeTruthy();
});
describe("SSE Live Updates", () => {
it("subscribes to plugin:lifecycle SSE events", async () => {
vi.mocked(fetchPlugins).mockResolvedValueOnce(mockPlugins);