fix(ticket116): isoler crash plugin hello-plugin + erreur explicite UI
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
* acceptance criteria: "ordre déterministe, disabledReason, plugin disabled
|
||||
* absent, command handler appelé").
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { PluginContributionDto } from "@/domain";
|
||||
import { PluginRuntimeRegistry, type LoadedPlugin, type WhenContext } from "@/plugins/runtime";
|
||||
@ -19,11 +19,15 @@ const NO_CONTEXT: WhenContext = {
|
||||
};
|
||||
|
||||
function stubPlugin(pluginId: string, displayName: string, contributes: PluginContributionDto): LoadedPlugin {
|
||||
const declaredCommandIds = contributes.menuItems.flatMap((item) => {
|
||||
if (item === null || typeof item !== "object") return [];
|
||||
return typeof item.command === "string" ? [item.command] : [];
|
||||
});
|
||||
return {
|
||||
pluginId,
|
||||
displayName,
|
||||
contributes,
|
||||
commands: new PluginCommandRegistry(pluginId, new Set(contributes.menuItems.map((i) => i.command))),
|
||||
commands: new PluginCommandRegistry(pluginId, new Set(declaredCommandIds)),
|
||||
layouts: new PluginLayoutRegistry(pluginId, new Set(contributes.layouts.map((l) => l.type))),
|
||||
menu: new PluginMenuRegistry(pluginId),
|
||||
dispose: async () => {},
|
||||
@ -67,6 +71,7 @@ describe("resolveTopLevelMenus", () => {
|
||||
...empty(),
|
||||
menus: [
|
||||
{ id: "bad.menu", label: "Good", topLevel: true },
|
||||
null,
|
||||
{ id: "bad.empty", label: "", topLevel: true },
|
||||
{ id: undefined, label: "No id", topLevel: true },
|
||||
{ id: "bad.no-label", label: undefined, topLevel: true },
|
||||
@ -167,6 +172,7 @@ describe("resolveMenuItems", () => {
|
||||
label: "Say Hello",
|
||||
command: "hello-plugin.sayHello",
|
||||
},
|
||||
null,
|
||||
{
|
||||
id: "hello-plugin.broken.item",
|
||||
targetMenuId: "hello-plugin.menu",
|
||||
@ -199,4 +205,29 @@ describe("resolveMenuItems", () => {
|
||||
await registry.runCommand("dev.acme", "dev.acme.a.cmd");
|
||||
expect(ran).toBe(true);
|
||||
});
|
||||
|
||||
it("confines a throwing command handler to the plugin command dispatch", async () => {
|
||||
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
try {
|
||||
const registry = new PluginRuntimeRegistry();
|
||||
const plugin = stubPlugin("dev.acme", "Acme", {
|
||||
...empty(),
|
||||
menuItems: [
|
||||
{ id: "dev.acme.a", targetMenuId: "panels", label: "Open A", command: "dev.acme.a.cmd" },
|
||||
],
|
||||
});
|
||||
plugin.commands.register("dev.acme.a.cmd", () => {
|
||||
throw new Error("boom");
|
||||
});
|
||||
registry.add(plugin);
|
||||
|
||||
await expect(registry.runCommand("dev.acme", "dev.acme.a.cmd")).resolves.toBeUndefined();
|
||||
expect(consoleError).toHaveBeenCalledWith(
|
||||
'[plugin:dev.acme] command "dev.acme.a.cmd" failed',
|
||||
expect.any(Error),
|
||||
);
|
||||
} finally {
|
||||
consoleError.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user