Add client plugin contribution registry

This commit is contained in:
Federico Jaramillo Martinez
2026-05-08 15:27:20 +02:00
parent b0eac4157e
commit 87d0aa3b21
16 changed files with 535 additions and 290 deletions
+79
View File
@@ -0,0 +1,79 @@
import { describe, expect, it, vi } from "vitest";
import { initialAppState, type AppState } from "../appState";
import { corePlugin } from "./core";
import { PluginRegistry } from "./registry";
import type { PluginRuntimeContext } from "./types";
function createContext(statePatch: Partial<AppState> = {}) {
const calls: string[] = [];
const context: PluginRuntimeContext = {
state: { ...initialAppState(), ...statePatch },
openActionPalette: vi.fn(() => { calls.push("openActionPalette"); }),
focusPrompt: vi.fn(() => { calls.push("focusPrompt"); }),
addProject: vi.fn(() => { calls.push("addProject"); }),
selectMainView: vi.fn((view: AppState["mainView"]) => { calls.push(`selectMainView:${view}`); }),
selectWorkspaceTool: vi.fn((tool: AppState["workspaceTool"]) => { calls.push(`selectWorkspaceTool:${tool}`); }),
refreshFiles: vi.fn(() => { calls.push("refreshFiles"); }),
refreshGit: vi.fn(() => { calls.push("refreshGit"); }),
startSession: vi.fn(() => { calls.push("startSession"); }),
archiveSession: vi.fn(() => { calls.push("archiveSession"); }),
stopActiveWork: vi.fn(() => { calls.push("stopActiveWork"); }),
};
return { context, calls };
}
describe("PluginRegistry", () => {
it("namespaces contribution ids with the owning plugin id", () => {
const registry = new PluginRegistry();
registry.register(corePlugin);
expect(registry.getActions(createContext().context).some((action) => action.id === "core:actions.show")).toBe(true);
expect(registry.getWorkspacePanels().map((panel) => panel.id)).toEqual(["core:workspace.files", "core:workspace.git"]);
});
it("rejects duplicate ids within the same namespace", () => {
const registry = new PluginRegistry();
expect(() => {
registry.register({
id: "example",
name: "Example",
activate: () => ({
actions: [
{ id: "duplicate", title: "One", run: () => undefined },
{ id: "duplicate", title: "Two", run: () => undefined },
],
}),
});
}).toThrow("Duplicate contribution id: example:duplicate");
});
it("evaluates core action enablement against runtime state", () => {
const registry = new PluginRegistry();
registry.register(corePlugin);
const inactive = registry.getActions(createContext().context);
const active = registry.getActions(createContext({ selectedWorkspace: testWorkspace() }).context);
expect(inactive.find((action) => action.id === "core:view.files")?.enabled).toBe(false);
expect(active.find((action) => action.id === "core:view.files")?.enabled).toBe(true);
});
it("routes refresh current to the active core workspace panel", () => {
const registry = new PluginRegistry();
registry.register(corePlugin);
const { context, calls } = createContext({
selectedWorkspace: testWorkspace(),
workspaceTool: "core:workspace.git",
});
const action = registry.getActions(context).find((candidate) => candidate.id === "core:workspace.refresh-current");
if (action !== undefined) void action.run();
expect(calls).toEqual(["refreshGit"]);
});
});
function testWorkspace(): AppState["selectedWorkspace"] {
return { id: "w1", projectId: "p1", path: "/tmp/project", label: "main", isMain: true, isGitWorktree: false };
}