openclaw/apps/web/lib/workspace-links.test.ts
2026-02-16 01:01:12 -08:00

229 lines
7.9 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
buildEntryLink,
buildFileLink,
parseWorkspaceLink,
isWorkspaceLink,
isInternalLink,
isEntryLink,
} from "./workspace-links";
// ─── buildEntryLink ────────────────────────────────────────────────
describe("buildEntryLink", () => {
it("builds a basic entry link", () => {
expect(buildEntryLink("leads", "abc123")).toBe("/workspace?entry=leads:abc123");
});
it("encodes special characters in object name", () => {
const result = buildEntryLink("my objects", "id1");
expect(result).toContain("my%20objects");
expect(result).toContain("id1");
});
it("encodes special characters in entry ID", () => {
const result = buildEntryLink("leads", "id/with/slashes");
expect(result).toContain("id%2Fwith%2Fslashes");
});
it("handles empty object name", () => {
const result = buildEntryLink("", "id1");
expect(result).toBe("/workspace?entry=:id1");
});
it("handles unicode characters", () => {
const result = buildEntryLink("対象", "エントリ");
expect(result).toContain("/workspace?entry=");
// Should decode back correctly
const url = new URL(result, "http://localhost");
expect(url.searchParams.get("entry")).toBe("対象:エントリ");
});
});
// ─── buildFileLink ────────────────────────────────────────────────
describe("buildFileLink", () => {
it("builds a basic file link", () => {
expect(buildFileLink("knowledge/doc.md")).toBe("/workspace?path=knowledge%2Fdoc.md");
});
it("builds link for nested path", () => {
const result = buildFileLink("a/b/c/d.txt");
const url = new URL(result, "http://localhost");
expect(url.searchParams.get("path")).toBe("a/b/c/d.txt");
});
it("handles spaces in path", () => {
const result = buildFileLink("my docs/file name.md");
const url = new URL(result, "http://localhost");
expect(url.searchParams.get("path")).toBe("my docs/file name.md");
});
it("handles special characters", () => {
const result = buildFileLink("notes & ideas/doc (1).md");
const url = new URL(result, "http://localhost");
expect(url.searchParams.get("path")).toBe("notes & ideas/doc (1).md");
});
it("handles empty path", () => {
expect(buildFileLink("")).toBe("/workspace?path=");
});
});
// ─── parseWorkspaceLink ───────────────────────────────────────────
describe("parseWorkspaceLink", () => {
it("parses file link from path param", () => {
const result = parseWorkspaceLink("/workspace?path=knowledge/doc.md");
expect(result).toEqual({ kind: "file", path: "knowledge/doc.md" });
});
it("parses entry link from entry param", () => {
const result = parseWorkspaceLink("/workspace?entry=leads:abc123");
expect(result).toEqual({ kind: "entry", objectName: "leads", entryId: "abc123" });
});
it("parses entry link from full URL", () => {
const result = parseWorkspaceLink("http://localhost:3100/workspace?entry=deals:xyz");
expect(result).toEqual({ kind: "entry", objectName: "deals", entryId: "xyz" });
});
it("parses file link from full URL", () => {
const result = parseWorkspaceLink("http://localhost:3100/workspace?path=readme.md");
expect(result).toEqual({ kind: "file", path: "readme.md" });
});
it("parses legacy @entry/ format", () => {
const result = parseWorkspaceLink("@entry/leads/abc123");
expect(result).toEqual({ kind: "entry", objectName: "leads", entryId: "abc123" });
});
it("returns null for invalid URL", () => {
expect(parseWorkspaceLink("not a url ://bad")).toBeNull();
});
it("returns null when no params present", () => {
expect(parseWorkspaceLink("/workspace")).toBeNull();
});
it("returns null for hash-only link", () => {
expect(parseWorkspaceLink("/workspace#section")).toBeNull();
});
it("returns null for entry param without colon", () => {
expect(parseWorkspaceLink("/workspace?entry=nocolon")).toBeNull();
});
it("handles deeply nested file path", () => {
const result = parseWorkspaceLink("/workspace?path=a/b/c/d/e/f.txt");
expect(result).toEqual({ kind: "file", path: "a/b/c/d/e/f.txt" });
});
it("handles encoded characters in path", () => {
const result = parseWorkspaceLink("/workspace?path=my%20docs%2Ffile.md");
expect(result).toEqual({ kind: "file", path: "my docs/file.md" });
});
it("returns null for non-workspace URL", () => {
expect(parseWorkspaceLink("https://google.com")).toBeNull();
});
it("entry param takes priority over path param", () => {
const result = parseWorkspaceLink("/workspace?entry=obj:id&path=file.md");
expect(result).toEqual({ kind: "entry", objectName: "obj", entryId: "id" });
});
it("handles entry with colon in ID", () => {
const result = parseWorkspaceLink("/workspace?entry=obj:id:with:colons");
expect(result).toEqual({ kind: "entry", objectName: "obj", entryId: "id:with:colons" });
});
it("returns null for legacy @entry with no slash after object name", () => {
expect(parseWorkspaceLink("@entry/objectonly")).toBeNull();
});
});
// ─── isWorkspaceLink ──────────────────────────────────────────────
describe("isWorkspaceLink", () => {
it("returns true for /workspace?path=...", () => {
expect(isWorkspaceLink("/workspace?path=doc.md")).toBe(true);
});
it("returns true for /workspace#...", () => {
expect(isWorkspaceLink("/workspace#section")).toBe(true);
});
it("returns true for /workspace alone", () => {
expect(isWorkspaceLink("/workspace")).toBe(true);
});
it("returns true for @entry/ format", () => {
expect(isWorkspaceLink("@entry/leads/abc")).toBe(true);
});
it("returns false for external URL", () => {
expect(isWorkspaceLink("https://example.com")).toBe(false);
});
it("returns false for random path", () => {
expect(isWorkspaceLink("/other-page")).toBe(false);
});
it("returns false for empty string", () => {
expect(isWorkspaceLink("")).toBe(false);
});
});
// ─── isInternalLink ───────────────────────────────────────────────
describe("isInternalLink", () => {
it("returns false for http:// URLs", () => {
expect(isInternalLink("http://example.com")).toBe(false);
});
it("returns false for https:// URLs", () => {
expect(isInternalLink("https://example.com")).toBe(false);
});
it("returns false for mailto: links", () => {
expect(isInternalLink("mailto:user@example.com")).toBe(false);
});
it("returns true for relative paths", () => {
expect(isInternalLink("/workspace?path=doc.md")).toBe(true);
});
it("returns true for @entry/ links", () => {
expect(isInternalLink("@entry/leads/123")).toBe(true);
});
it("returns true for plain text", () => {
expect(isInternalLink("some-page")).toBe(true);
});
});
// ─── isEntryLink ──────────────────────────────────────────────────
describe("isEntryLink", () => {
it("returns true for new format entry link", () => {
expect(isEntryLink("/workspace?entry=leads:abc")).toBe(true);
});
it("returns true for legacy @entry/ format", () => {
expect(isEntryLink("@entry/leads/abc")).toBe(true);
});
it("returns false for file workspace link", () => {
expect(isEntryLink("/workspace?path=doc.md")).toBe(false);
});
it("returns false for external URL", () => {
expect(isEntryLink("https://example.com")).toBe(false);
});
it("returns false for plain /workspace", () => {
expect(isEntryLink("/workspace")).toBe(false);
});
});