This repository has been archived on 2026-08-23. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
pi-web/src/client/src/plugins/registry.test.ts
T

104 lines
4.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { Workspace } from "../api";
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"); }),
configureAuth: vi.fn(() => { calls.push("configureAuth"); }),
logoutAuth: vi.fn(() => { calls.push("logoutAuth"); }),
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", "core:workspace.terminal"]);
});
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"]);
});
it("collects workspace label items in contribution order", () => {
const registry = new PluginRegistry();
const workspace = testWorkspace();
registry.register({
id: "example",
name: "Example",
activate: () => ({
workspaceLabelContributions: [
{ id: "last", order: 20, items: () => ({ type: "text", text: "last" }) },
{ id: "hidden", order: 5, visible: () => false, items: () => ({ type: "text", text: "hidden" }) },
{ id: "first", order: 10, items: () => [{ type: "link", text: "web", href: "http://localhost:5173" }] },
],
}),
});
expect(registry.getWorkspaceLabelItems(initialAppState(), workspace)).toEqual([
{ type: "link", text: "web", href: "http://localhost:5173" },
{ type: "text", text: "last" },
]);
});
});
function testWorkspace(): Workspace {
return { id: "w1", projectId: "p1", path: "/tmp/project", label: "main", isMain: true, isGitRepo: true, isGitWorktree: false };
}