import { describe, expect, it, test } from "vitest"; import { applyOpenClawManifestInstallCommonFields, getFrontmatterString, normalizeStringList, parseFrontmatterBool, parseOpenClawManifestInstallBase, resolveOpenClawManifestBlock, resolveOpenClawManifestInstall, resolveOpenClawManifestOs, resolveOpenClawManifestRequires, } from "./frontmatter.js"; describe("shared/frontmatter", () => { test("normalizeStringList handles strings, arrays, and non-list values", () => { expect(normalizeStringList("a, b,,c")).toEqual(["a", "b", "c"]); expect(normalizeStringList([" a ", "", "b", 42])).toEqual(["a", "b", "42"]); expect(normalizeStringList(null)).toEqual([]); }); test("getFrontmatterString extracts strings only", () => { expect(getFrontmatterString({ a: "b" }, "a")).toBe("b"); expect(getFrontmatterString({ a: 1 }, "a")).toBeUndefined(); }); test("parseFrontmatterBool respects explicit values and fallback", () => { expect(parseFrontmatterBool("true", false)).toBe(true); expect(parseFrontmatterBool("false", true)).toBe(false); expect(parseFrontmatterBool(undefined, true)).toBe(true); expect(parseFrontmatterBool("maybe", false)).toBe(false); }); test("resolveOpenClawManifestBlock reads current manifest keys and custom metadata fields", () => { expect( resolveOpenClawManifestBlock({ frontmatter: { metadata: "{ openclaw: { foo: 1, bar: 'baz' } }", }, }), ).toEqual({ foo: 1, bar: "baz" }); expect( resolveOpenClawManifestBlock({ frontmatter: { pluginMeta: "{ openclaw: { foo: 2 } }", }, key: "pluginMeta", }), ).toEqual({ foo: 2 }); }); test("resolveOpenClawManifestBlock returns undefined for invalid input", () => { expect(resolveOpenClawManifestBlock({ frontmatter: {} })).toBeUndefined(); expect( resolveOpenClawManifestBlock({ frontmatter: { metadata: "not-json5" } }), ).toBeUndefined(); expect(resolveOpenClawManifestBlock({ frontmatter: { metadata: "123" } })).toBeUndefined(); expect(resolveOpenClawManifestBlock({ frontmatter: { metadata: "[]" } })).toBeUndefined(); expect( resolveOpenClawManifestBlock({ frontmatter: { metadata: "{ nope: { a: 1 } }" } }), ).toBeUndefined(); }); it("normalizes manifest requirement and os lists", () => { expect( resolveOpenClawManifestRequires({ requires: { bins: "bun, node", anyBins: [" ffmpeg ", ""], env: ["OPENCLAW_TOKEN", " OPENCLAW_URL "], config: null, }, }), ).toEqual({ bins: ["bun", "node"], anyBins: ["ffmpeg"], env: ["OPENCLAW_TOKEN", "OPENCLAW_URL"], config: [], }); expect(resolveOpenClawManifestRequires({})).toBeUndefined(); expect(resolveOpenClawManifestOs({ os: [" darwin ", "linux", ""] })).toEqual([ "darwin", "linux", ]); }); it("parses and applies install common fields", () => { const parsed = parseOpenClawManifestInstallBase( { type: " Brew ", id: "brew.git", label: "Git", bins: [" git ", "git"], }, ["brew", "npm"], ); expect(parsed).toEqual({ raw: { type: " Brew ", id: "brew.git", label: "Git", bins: [" git ", "git"], }, kind: "brew", id: "brew.git", label: "Git", bins: ["git", "git"], }); expect(parseOpenClawManifestInstallBase({ kind: "bad" }, ["brew"])).toBeUndefined(); expect( applyOpenClawManifestInstallCommonFields<{ extra: boolean; id?: string; label?: string; bins?: string[]; }>({ extra: true }, parsed!), ).toEqual({ extra: true, id: "brew.git", label: "Git", bins: ["git", "git"], }); }); it("prefers explicit kind, ignores invalid common fields, and leaves missing ones untouched", () => { const parsed = parseOpenClawManifestInstallBase( { kind: " npm ", type: "brew", id: 42, label: null, bins: [" ", ""], }, ["brew", "npm"], ); expect(parsed).toEqual({ raw: { kind: " npm ", type: "brew", id: 42, label: null, bins: [" ", ""], }, kind: "npm", }); expect( applyOpenClawManifestInstallCommonFields( { id: "keep", label: "Keep", bins: ["bun"] }, parsed!, ), ).toEqual({ id: "keep", label: "Keep", bins: ["bun"], }); }); it("maps install entries through the parser and filters rejected specs", () => { expect( resolveOpenClawManifestInstall( { install: [{ id: "keep" }, { id: "drop" }, "bad"], }, (entry) => { if ( typeof entry === "object" && entry !== null && (entry as { id?: string }).id === "keep" ) { return { id: "keep" }; } return undefined; }, ), ).toEqual([{ id: "keep" }]); }); });