import { describe, expect, test } from "vitest"; import { findImageRefs, planImages, rewriteBody } from "./images"; describe("findImageRefs", () => { test("detects embeds, markdown, html; ignores external and site-absolute", () => { const body = [ "![[shot.png]]", "![cap](pics/local.jpeg)", 'd', "![remote](https://x.com/a.png)", "![done](/assets/img/already.png)", ].join("\n"); const refs = findImageRefs(body); expect(refs.map((r) => r.linktext)).toEqual(["shot.png", "pics/local.jpeg", "diagram.svg"]); expect(refs.map((r) => r.kind)).toEqual(["embed", "markdown", "html"]); expect(refs[1].alt).toBe("cap"); }); test("embed alias becomes alt text", () => { expect(findImageRefs("![[a.png|My alt]]")[0].alt).toBe("My alt"); }); }); describe("planImages flat-slug", () => { test("single image drops the numeric suffix", () => { const body = "![[only.png]]"; const refs = findImageRefs(body); const { rewrittenBody, plan } = planImages(refs, { slug: "my-post", strategy: "flat-slug", imagesDir: "assets/img", body }); expect(plan).toEqual([{ linktext: "only.png", repoPath: "assets/img/my-post.png", siteUrl: "/assets/img/my-post.png" }]); expect(rewrittenBody).toBe("![](/assets/img/my-post.png)"); }); test("multiple images get -1, -2 suffixes and body is rewritten", () => { const body = "![[a.png]]\n![alt](b.jpeg)"; const refs = findImageRefs(body); const { rewrittenBody, plan } = planImages(refs, { slug: "post", strategy: "flat-slug", imagesDir: "assets/img", body }); expect(plan.map((p) => p.repoPath)).toEqual(["assets/img/post-1.png", "assets/img/post-2.jpeg"]); expect(rewrittenBody).toBe("![](/assets/img/post-1.png)\n![alt](/assets/img/post-2.jpeg)"); }); }); describe("planImages per-post-folder", () => { test("keeps original basename under a slug folder", () => { const body = "![[sub/dir/Photo.PNG|cap]]"; const refs = findImageRefs(body); const { plan, rewrittenBody } = planImages(refs, { slug: "post", strategy: "per-post-folder", imagesDir: "assets/img", body }); expect(plan[0].repoPath).toBe("assets/img/post/Photo.PNG"); expect(rewrittenBody).toBe("![cap](/assets/img/post/Photo.PNG)"); }); test("basename collisions are de-duped with -1", () => { const body = "![[x/p.png]]\n![[y/p.png]]"; const refs = findImageRefs(body); const { plan } = planImages(refs, { slug: "post", strategy: "per-post-folder", imagesDir: "assets/img", body }); expect(plan.map((p) => p.repoPath)).toEqual(["assets/img/post/p.png", "assets/img/post/p-1.png"]); }); }); describe("rewriteBody", () => { test("rewrites all ref types to markdown image syntax", () => { const body = '![[shot.png]]\n![cap](pics/local.jpeg)\nd'; const refs = findImageRefs(body); const plan = [ { linktext: "shot.png", repoPath: "assets/img/post-1.png", siteUrl: "/assets/img/post-1.png" }, { linktext: "pics/local.jpeg", repoPath: "assets/img/post-2.jpeg", siteUrl: "/assets/img/post-2.jpeg" }, { linktext: "diagram.svg", repoPath: "assets/img/post-3.svg", siteUrl: "/assets/img/post-3.svg" }, ]; const result = rewriteBody(body, refs, plan); expect(result).toBe( "![](/assets/img/post-1.png)\n![cap](/assets/img/post-2.jpeg)\n![d](/assets/img/post-3.svg)" ); }); test("alt text containing $ patterns is preserved verbatim (no replacement-pattern corruption)", () => { const slug = "my-post"; const body = "![a $& b $$ c](local.png)"; const refs = findImageRefs(body); const { rewrittenBody } = planImages(refs, { slug, strategy: "flat-slug", imagesDir: "assets/img", body }); expect(rewrittenBody).toBe("![a $& b $$ c](/assets/img/my-post.png)"); }); });