sitez

metadata.test.ts

No prose in this file.

import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { parse } from "@amitkaps/markz";
import { describe, expect, test } from "vite-plus/test";
import { SiteError } from "./errors.ts";
import { textMetadata, siteMetadata } from "./metadata.ts";

const file = "/site/text/page.md";

function problem(fn: () => unknown): string {
  try {
    fn();
  } catch (error) {
    if (error instanceof SiteError && error.file === file) return error.message;
    throw error;
  }
  return "no error";
}

const block = (lines: string) => parse(`---\n${lines}\n---\n\n# Heading\n`);

describe("textMetadata", () => {
  test("is the block and the URL, and fills in nothing from the text", () => {
    const doc = parse("# Hello _there_\n\nThe first `post`.\n\nMore.\n");
    expect(textMetadata(file, "/hello/", doc)).toEqual({ url: "/hello/" });
  });

  test("keeps what the block says, and every other key, date included", () => {
    const doc = parse(
      "---\ntitle: Short\ndescription: Brief.\ndate: 2026-09-01\ndraft: true\ntags: [a, b]\n---\n\n# Long title\n\nText.\n",
    );
    expect(textMetadata(file, "/x/", doc)).toEqual({
      url: "/x/",
      title: "Short",
      description: "Brief.",
      date: "2026-09-01",
      draft: true,
      tags: ["a", "b"],
    });
  });

  test("passes summary through as the site's own key", () => {
    expect(textMetadata(file, "/x/", block("summary: Brief."))).toEqual({
      url: "/x/",
      summary: "Brief.",
    });
  });
});

describe("the keys Sitez reads", () => {
  test.each([
    ["draft: yes", 'draft is "yes": write draft: true or draft: false.'],
    ["draft: 1", "draft is 1: write draft: true or draft: false."],
    [
      "title: 1.10",
      'title is "1.10": `1.10` reads as a number in YAML: quote it, or write it as `1.1`.',
    ],
    ["title: 2026", "title is 2026: write it as text."],
    ["title:", "title is null: leave the line out, or give it a value."],
    ["description: [a, b]", 'description is ["a","b"]: write it as text.'],
    [
      "url: /about/",
      "url is \"/about/\": a page's URL is its file's path: move the file instead, and remove url.",
    ],
    [
      "redirects: 3",
      "redirects is 3: write the URLs that used to reach this page, such as redirects: [/old-name/].",
    ],
  ])("fail when wrong in a page: %s", (line, message) => {
    expect(problem(() => textMetadata(file, "/x/", block(line)))).toBe(message);
  });

  test("pass when right, and leave other keys alone", () => {
    const page = textMetadata(
      file,
      "/x/",
      block("title: T\ndescription: S\ndate: Sept 1\ndraft: false\ntags: [a]\norder: 3\nimage:"),
    );
    expect(page).toMatchObject({ draft: false, date: "Sept 1", order: 3, image: null });
  });
});

describe("siteMetadata", () => {
  function site(lines: string) {
    const root = mkdtempSync(join(tmpdir(), "sitez-"));
    writeFileSync(join(root, "site.md"), `---\n${lines}\n---\n\nNotes.\n`);
    try {
      return siteMetadata(root);
    } catch (error) {
      if (error instanceof SiteError)
        return `${error.file.slice(root.length + 1)}: ${error.message}`;
      throw error;
    }
  }

  test("is the block, and only the block, with no defaults", () => {
    expect(site("name: Notes\nurl: https://notes.example.com\nlang: hi\nauthor: Me")).toEqual({
      name: "Notes",
      url: "https://notes.example.com",
      lang: "hi",
      author: "Me",
    });
    expect(site("name: 42")).toEqual({ name: 42 });
  });

  test.each([
    [
      "url: notes.example.com",
      'site.md: url is "notes.example.com": write the full address, starting https://.',
    ],
    [
      "repo: amitkaps/notes",
      'site.md: repo is "amitkaps/notes": write the full address, starting https://.',
    ],
  ])("fails on %s", (line, message) => {
    expect(site(line)).toBe(message);
  });
});