import { describe, expect, test } from "bun:test"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { ContentRegistry } from "../core/content.ts"; import { resolveConfig } from "../core/config.ts"; import { markdownLoader, parseFrontmatter, renderMarkdown, } from "../views/markdown.ts"; /** * Tests of the markdown loader and of the content collections. */ describe("markdown", () => { test("parses the frontmatter", () => { const { frontmatter, body } = parseFrontmatter( "---\ntitle: Hello\ndraft: false\ncount: 3\ntags:\n - a\n - b\n---\n\nBody", ); expect(frontmatter.title).toBe("Hello"); expect(frontmatter.draft).toBe(false); expect(frontmatter.count).toBe(3); expect(frontmatter.tags).toEqual(["a", "b"]); expect(body.trim()).toBe("Body"); }); test("renders the inline markup", () => { expect(renderMarkdown("**bold** and *italic*")).toContain( "bold", ); expect(renderMarkdown("`code`")).toContain("code"); expect(renderMarkdown("[link](/x)")).toContain('link'); }); test("renders the blocks", () => { const html = renderMarkdown( "# Title\n\nA paragraph.\n\n- one\n- two\n\n> quoted\n\n```js\nconst a = 1;\n```", ); expect(html).toContain("

Title

"); expect(html).toContain("

A paragraph.

"); expect(html).toContain("
  • one
  • "); expect(html).toContain("
    "); expect(html).toContain('
    ');
    	});
    });
    
    describe("content collections", () => {
    	test("loads the entries of a collection", async () => {
    		const dir = await mkdtemp(join(tmpdir(), "muddle-content-"));
    		await writeFile(
    			join(dir, "first.md"),
    			"---\ntitle: First\n---\n\nHello **world**",
    			"utf8",
    		);
    		await writeFile(
    			join(dir, "second.md"),
    			"---\ntitle: Second\n---\n\nSecond note",
    			"utf8",
    		);
    
    		const registry = new ContentRegistry(
    			resolveConfig({
    				root: dir,
    				collections: [
    					{
    						name: "notes",
    						dir: ".",
    						loader: markdownLoader(),
    						schema: { required: ["title"] },
    					},
    				],
    			}),
    		);
    		await registry.load();
    
    		const entries = registry.entries("notes");
    		expect(entries).toHaveLength(2);
    		expect(entries[0]?.id).toBe("first");
    		expect(entries[0]?.frontmatter.title).toBe("First");
    		expect(entries[0]?.body).toContain("world");
    		expect(registry.get("notes", "second")?.frontmatter.title).toBe("Second");
    
    		await rm(dir, { recursive: true, force: true });
    	});
    
    	test("rejects an invalid frontmatter", async () => {
    		const dir = await mkdtemp(join(tmpdir(), "muddle-content-"));
    		await writeFile(
    			join(dir, "broken.md"),
    			"---\ndraft: true\n---\n\nBody",
    			"utf8",
    		);
    
    		const registry = new ContentRegistry(
    			resolveConfig({
    				root: dir,
    				collections: [
    					{
    						name: "notes",
    						dir: ".",
    						loader: markdownLoader(),
    						schema: { required: ["title"] },
    					},
    				],
    			}),
    		);
    
    		await expect(registry.load()).rejects.toThrow("title is required");
    
    		await rm(dir, { recursive: true, force: true });
    	});
    });