sbuffkin_hexmaker/tests/utils.test.ts

185 lines
6.2 KiB
TypeScript
Raw Permalink Normal View History

2026-04-04 15:51:53 +00:00
import { describe, it, mock } from "node:test";
import expect from "expect";
import { TFile } from "obsidian";
import { normalizeFolder, makeTableTemplate, getIconUrl } from "../src/utils";
import type HexmakerPlugin from "../src/HexmakerPlugin";
// ── normalizeFolder ───────────────────────────────────────────────────────────
describe("normalizeFolder", () => {
it("returns empty string for empty input", () => {
expect(normalizeFolder("")).toBe("");
});
it("strips a leading slash", () => {
expect(normalizeFolder("/tables")).toBe("tables");
});
it("strips a trailing slash", () => {
expect(normalizeFolder("tables/")).toBe("tables");
});
it("strips both leading and trailing slashes", () => {
expect(normalizeFolder("/tables/")).toBe("tables");
});
it("strips multiple leading and trailing slashes", () => {
expect(normalizeFolder("///tables///")).toBe("tables");
});
it("leaves interior slashes intact", () => {
expect(normalizeFolder("world/tables")).toBe("world/tables");
});
it("returns empty string for a string that is only slashes", () => {
expect(normalizeFolder("///")).toBe("");
});
it("does not modify a clean path", () => {
expect(normalizeFolder("tables/terrain")).toBe("tables/terrain");
});
});
// ── makeTableTemplate ─────────────────────────────────────────────────────────
describe("makeTableTemplate", () => {
it("includes the dice value in frontmatter", () => {
const t = makeTableTemplate(6);
expect(t).toContain("dice: 6");
});
it("produces valid YAML frontmatter block", () => {
const t = makeTableTemplate(4);
expect(t).toMatch(/^---\n/);
expect(t).toContain("\n---\n");
});
it("generates a single blank example row", () => {
const t = makeTableTemplate(6);
expect(t).toContain("| | 1 |");
});
it("includes the Result/Weight table header", () => {
const t = makeTableTemplate(6);
expect(t).toContain("| Result | Weight |");
expect(t).toContain("|--------|--------|");
});
it("dice: 0 still produces valid frontmatter", () => {
const t = makeTableTemplate(0);
expect(t).toContain("dice: 0");
});
it("includes extra frontmatter fields when provided", () => {
const t = makeTableTemplate(6, { terrain: "forest", category: "monsters" });
expect(t).toContain("terrain: forest");
expect(t).toContain("category: monsters");
});
it("serialises boolean extra frontmatter values", () => {
const t = makeTableTemplate(6, { "roll-filter": false });
expect(t).toContain("roll-filter: false");
});
it("serialises number extra frontmatter values", () => {
const t = makeTableTemplate(6, { level: 3 });
expect(t).toContain("level: 3");
});
it("includes preamble between frontmatter and table", () => {
const t = makeTableTemplate(6, undefined, "[🎲 Open](obsidian://roll)");
expect(t).toContain("[🎲 Open](obsidian://roll)");
const preambleIdx = t.indexOf("[🎲 Open]");
const tableIdx = t.indexOf("| Result |");
expect(preambleIdx).toBeLessThan(tableIdx);
});
});
// ── getIconUrl ────────────────────────────────────────────────────────────────
/** Build a minimal plugin stub for getIconUrl. */
function makePluginForIcon(
vaultIcons: string[],
iconsFolder: string,
manifestDir: string,
): { plugin: HexmakerPlugin; getLastPath: () => string } {
let lastPath = "";
const vaultIconSet = new Set(vaultIcons);
const plugin = {
vaultIconsSet: vaultIconSet,
settings: { iconsFolder },
manifest: { dir: manifestDir },
app: {
vault: {
adapter: {
getResourcePath: mock.fn((p: string) => { lastPath = p; return `resource://${p}`; }),
},
getAbstractFileByPath: mock.fn((path: string) => {
const filename = path.split("/").pop() ?? "";
if (vaultIconSet.has(filename)) {
const f = Object.create(TFile.prototype) as TFile;
f.path = path;
return f;
}
return null;
}),
getResourcePath: mock.fn((f: TFile) => { lastPath = f.path; return `resource://${f.path}`; }),
},
},
} as unknown as HexmakerPlugin;
return { plugin, getLastPath: () => lastPath };
}
describe("getIconUrl", () => {
it("uses plugin icons dir when icon is not in vaultIconsSet", () => {
const { plugin, getLastPath } = makePluginForIcon(
[],
"custom",
"plugins/duckmage-plugin",
);
getIconUrl(plugin, "tower.png");
expect(getLastPath()).toBe("plugins/duckmage-plugin/icons/tower.png");
});
it("uses vault iconsFolder when icon is in vaultIconsSet", () => {
const { plugin, getLastPath } = makePluginForIcon(
["village.png"],
"custom-icons",
"plugins/duckmage-plugin",
);
getIconUrl(plugin, "village.png");
expect(getLastPath()).toBe("custom-icons/village.png");
});
it("uses plugin icons dir for icons not in vaultIconsSet even when others are", () => {
const { plugin, getLastPath } = makePluginForIcon(
["village.png"],
"custom-icons",
"plugins/duckmage-plugin",
);
getIconUrl(plugin, "castle.png");
expect(getLastPath()).toBe("plugins/duckmage-plugin/icons/castle.png");
});
it("normalises iconsFolder by stripping leading/trailing slashes", () => {
const { plugin, getLastPath } = makePluginForIcon(
["icon.png"],
"/my-icons/",
"plugins/duckmage-plugin",
);
getIconUrl(plugin, "icon.png");
expect(getLastPath()).toBe("my-icons/icon.png");
});
it("returns a string (the resource path)", () => {
const { plugin } = makePluginForIcon(
[],
"icons",
"plugins/duckmage-plugin",
);
const result = getIconUrl(plugin, "ruins.png");
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
});