import { describe, expect, test } from "bun:test" import { collectNewSessionDeepLinks, collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink, parseNewSessionDeepLink, } from "./deep-links" import { type Session } from "@opencode-ai/sdk/v2/client" import { displayName, effectiveWorkspaceOrder, errorMessage, hasProjectPermissions, latestRootSession, workspaceKey, } from "./helpers" const session = (input: Partial & Pick) => ({ title: "", version: "v2", parentID: undefined, messageCount: 0, permissions: { session: {}, share: {} }, time: { created: 0, updated: 0, archived: undefined }, ...input, }) as Session describe("layout deep links", () => { test("parses open-project deep links", () => { expect(parseDeepLink("opencode://open-project?directory=/tmp/demo")).toBe("/tmp/demo") }) test("ignores non-project deep links", () => { expect(parseDeepLink("opencode://other?directory=/tmp/demo")).toBeUndefined() expect(parseDeepLink("https://example.com")).toBeUndefined() }) test("ignores malformed deep links safely", () => { expect(() => parseDeepLink("opencode://open-project/%E0%A4%A%")).not.toThrow() expect(parseDeepLink("opencode://open-project/%E0%A4%A%")).toBeUndefined() }) test("parses links when URL.canParse is unavailable", () => { const original = Object.getOwnPropertyDescriptor(URL, "canParse") Object.defineProperty(URL, "canParse", { configurable: true, value: undefined }) try { expect(parseDeepLink("opencode://open-project?directory=/tmp/demo")).toBe("/tmp/demo") } finally { if (original) Object.defineProperty(URL, "canParse", original) if (!original) Reflect.deleteProperty(URL, "canParse") } }) test("ignores open-project deep links without directory", () => { expect(parseDeepLink("opencode://open-project")).toBeUndefined() expect(parseDeepLink("opencode://open-project?directory=")).toBeUndefined() }) test("collects only valid open-project directories", () => { const result = collectOpenProjectDeepLinks([ "opencode://open-project?directory=/a", "opencode://other?directory=/b", "opencode://open-project?directory=/c", ]) expect(result).toEqual(["/a", "/c"]) }) test("parses new-session deep links with optional prompt", () => { expect(parseNewSessionDeepLink("opencode://new-session?directory=/tmp/demo")).toEqual({ directory: "/tmp/demo" }) expect(parseNewSessionDeepLink("opencode://new-session?directory=/tmp/demo&prompt=hello%20world")).toEqual({ directory: "/tmp/demo", prompt: "hello world", }) }) test("ignores new-session deep links without directory", () => { expect(parseNewSessionDeepLink("opencode://new-session")).toBeUndefined() expect(parseNewSessionDeepLink("opencode://new-session?directory=")).toBeUndefined() }) test("collects only valid new-session deep links", () => { const result = collectNewSessionDeepLinks([ "opencode://new-session?directory=/a", "opencode://open-project?directory=/b", "opencode://new-session?directory=/c&prompt=ship%20it", ]) expect(result).toEqual([{ directory: "/a" }, { directory: "/c", prompt: "ship it" }]) }) test("drains global deep links once", () => { const target = { __OPENCODE__: { deepLinks: ["opencode://open-project?directory=/a"], }, } as unknown as Window & { __OPENCODE__?: { deepLinks?: string[] } } expect(drainPendingDeepLinks(target)).toEqual(["opencode://open-project?directory=/a"]) expect(drainPendingDeepLinks(target)).toEqual([]) }) }) describe("layout workspace helpers", () => { test("normalizes trailing slash in workspace key", () => { expect(workspaceKey("/tmp/demo///")).toBe("/tmp/demo") expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:/tmp/demo") }) test("preserves posix and drive roots in workspace key", () => { expect(workspaceKey("/")).toBe("/") expect(workspaceKey("///")).toBe("/") expect(workspaceKey("C:\\")).toBe("C:/") expect(workspaceKey("C://")).toBe("C:/") expect(workspaceKey("C:///")).toBe("C:/") }) test("keeps local first while preserving known order", () => { const result = effectiveWorkspaceOrder("/root", ["/root", "/b", "/c"], ["/root", "/c", "/a", "/b"]) expect(result).toEqual(["/root", "/c", "/b"]) }) test("finds the latest root session across workspaces", () => { const result = latestRootSession( [ { path: { directory: "/root" }, session: [session({ id: "root", directory: "/root", time: { created: 1, updated: 1, archived: undefined } })], }, { path: { directory: "/workspace" }, session: [ session({ id: "workspace", directory: "/workspace", time: { created: 2, updated: 2, archived: undefined }, }), ], }, ], 120_000, ) expect(result?.id).toBe("workspace") }) test("detects project permissions with a filter", () => { const result = hasProjectPermissions( { root: [{ id: "perm-root" }, { id: "perm-hidden" }], child: [{ id: "perm-child" }], }, (item) => item.id === "perm-child", ) expect(result).toBe(true) }) test("ignores project permissions filtered out", () => { const result = hasProjectPermissions( { root: [{ id: "perm-root" }], }, () => false, ) expect(result).toBe(false) }) test("ignores archived and child sessions when finding latest root session", () => { const result = latestRootSession( [ { path: { directory: "/workspace" }, session: [ session({ id: "archived", directory: "/workspace", time: { created: 10, updated: 10, archived: 10 }, }), session({ id: "child", directory: "/workspace", parentID: "parent", time: { created: 20, updated: 20, archived: undefined }, }), session({ id: "root", directory: "/workspace", time: { created: 30, updated: 30, archived: undefined }, }), ], }, ], 120_000, ) expect(result?.id).toBe("root") }) test("formats fallback project display name", () => { expect(displayName({ worktree: "/tmp/app" })).toBe("app") expect(displayName({ worktree: "/tmp/app", name: "My App" })).toBe("My App") }) test("extracts api error message and fallback", () => { expect(errorMessage({ data: { message: "boom" } }, "fallback")).toBe("boom") expect(errorMessage(new Error("broken"), "fallback")).toBe("broken") expect(errorMessage("unknown", "fallback")).toBe("fallback") }) })