import { renderHook } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import type { MenuTargetId } from "@/domain"; import type { PluginRuntimeRegistry, WhenContext } from "@/plugins/runtime"; import { usePluginMenus } from "./usePluginMenus"; const NO_CONTEXT: WhenContext = { projectOpen: false, gitRepository: false, agentSelected: false, terminalFocused: false, layoutCellFocused: false, }; function throwingRegistry(overrides: Record): PluginRuntimeRegistry { return { runCommand: vi.fn(async () => {}), topLevelMenus: vi.fn(() => []), menuItems: vi.fn(() => []), ...overrides, } as unknown as PluginRuntimeRegistry; } describe("usePluginMenus", () => { it("falls back to no plugin top-level menus when plugin menu resolution throws", () => { const consoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {}); try { const registry = throwingRegistry({ topLevelMenus: vi.fn(() => { throw new Error("broken top-level contribution"); }), }); const { result } = renderHook(() => usePluginMenus(registry, NO_CONTEXT)); expect(result.current.topLevelMenus).toEqual([]); expect(consoleWarn).toHaveBeenCalledWith( "[plugins] menu contribution rejected", expect.objectContaining({ scope: "top-level-menus", reason: "broken top-level contribution", }), ); } finally { consoleWarn.mockRestore(); } }); it("falls back to no plugin items for a native menu when item resolution throws", () => { const consoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {}); try { const registry = throwingRegistry({ menuItems: vi.fn(() => { throw new Error("broken item contribution"); }), }); const { result } = renderHook(() => usePluginMenus(registry, NO_CONTEXT)); const items = result.current.itemsFor("panels" as MenuTargetId); expect(items).toEqual([]); expect(consoleWarn).toHaveBeenCalledWith( "[plugins] menu contribution rejected", expect.objectContaining({ scope: "items", targetMenuId: "panels", reason: "broken item contribution", }), ); } finally { consoleWarn.mockRestore(); } }); });