import { describe, expect, it, vi } from "vite-plus/test"; import { resolveExternalWebLinkHost, showExternalLinkContextMenu } from "./externalLinkContextMenu"; function createHarness(selection: "open-in-preview" | "open-external" | "copy-link" | null) { const showContextMenu = vi.fn().mockResolvedValue(selection); const openInPreview = vi.fn().mockResolvedValue(undefined); const openExternal = vi.fn().mockResolvedValue(undefined); const copyLink = vi.fn().mockResolvedValue(undefined); const reportFailure = vi.fn(); return { showContextMenu, openInPreview, openExternal, copyLink, reportFailure, }; } describe("external chat link context menu", () => { it("offers both open actions and Copy Link", async () => { const harness = createHarness(null); await showExternalLinkContextMenu({ href: "https://example.com/docs?topic=menus#copy", position: { x: 12, y: 24 }, ...harness, }); expect(harness.showContextMenu).toHaveBeenCalledWith( [ { id: "open-in-preview", label: "Open in integrated browser" }, { id: "open-external", label: "Open in system browser" }, { id: "copy-link", label: "Copy Link" }, ], { x: 12, y: 24 }, ); expect(harness.openInPreview).not.toHaveBeenCalled(); expect(harness.openExternal).not.toHaveBeenCalled(); expect(harness.copyLink).not.toHaveBeenCalled(); }); it("copies the exact destination without opening it", async () => { const harness = createHarness("copy-link"); const href = "https://example.com/docs?topic=menus#copy"; await showExternalLinkContextMenu({ href, position: { x: 1, y: 2 }, ...harness }); expect(harness.copyLink).toHaveBeenCalledWith(href); expect(harness.openInPreview).not.toHaveBeenCalled(); expect(harness.openExternal).not.toHaveBeenCalled(); }); it.each([ ["open-in-preview" as const, "openInPreview" as const], ["open-external" as const, "openExternal" as const], ])("preserves the %s action", async (selection, expectedCallback) => { const harness = createHarness(selection); const href = "https://example.com/docs"; await showExternalLinkContextMenu({ href, position: { x: 1, y: 2 }, ...harness }); expect(harness[expectedCallback]).toHaveBeenCalledWith(href); expect(harness.copyLink).not.toHaveBeenCalled(); }); it("reports the selected action when it fails", async () => { const harness = createHarness("copy-link"); const cause = new Error("clipboard denied"); harness.copyLink.mockRejectedValue(cause); await showExternalLinkContextMenu({ href: "https://example.com/docs", position: { x: 1, y: 2 }, ...harness, }); expect(harness.reportFailure).toHaveBeenCalledWith("copy-link", cause); }); it("reports the menu operation when the native menu cannot be shown", async () => { const harness = createHarness(null); const cause = new Error("menu unavailable"); harness.showContextMenu.mockRejectedValue(cause); await showExternalLinkContextMenu({ href: "https://example.com/docs", position: { x: 1, y: 2 }, ...harness, }); expect(harness.reportFailure).toHaveBeenCalledWith("show-link-context-menu", cause); expect(harness.openInPreview).not.toHaveBeenCalled(); expect(harness.openExternal).not.toHaveBeenCalled(); expect(harness.copyLink).not.toHaveBeenCalled(); }); it.each([ ["open-in-preview" as const, "openInPreview" as const, "open-link-in-preview"], ["open-external" as const, "openExternal" as const, "open-link-external"], ])("reports a failed %s action", async (selection, callback, operation) => { const harness = createHarness(selection); const cause = new Error("open failed"); harness[callback].mockRejectedValue(cause); await showExternalLinkContextMenu({ href: "https://example.com/docs", position: { x: 1, y: 2 }, ...harness, }); expect(harness.reportFailure).toHaveBeenCalledWith(operation, cause); }); it.each([ ["https://example.com", "example.com"], ["http://localhost:3000/path", "localhost"], ["#details", null], ["mailto:hello@example.com", null], ["file:///tmp/example.txt", null], ["javascript:void(0)", null], ["not a URL", null], [undefined, null], ])("resolves the external web-link host for %s as %s", (href, expected) => { expect(resolveExternalWebLinkHost(href)).toBe(expected); }); });