import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { test } from "node:test"; import assert from "node:assert/strict"; import { renderChannelMarkdown, renderSiteMarkdown } from "./fileSchemaDocs"; import { CHANNEL_CONFIG_KEYS, parseChannelConfig } from "./channelConfig"; import { SITE_KEYS, parseSite, siteToDisk } from "./siteSchema"; // SITE.md and CHANNEL.md are GENERATED from the file schemas // (common/bin/file-schemas-docs.ts). This is what keeps them generated: a hand // edit to either file, or a schema change without a regenerate, fails here. const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); for (const [name, render] of [ ["SITE.md", renderSiteMarkdown], ["CHANNEL.md", renderChannelMarkdown], ] as const) { test(`${name} is what the schema generates`, () => { const committed = readFileSync(path.join(REPO, name), "utf8"); assert.equal( committed, render(), `${name} is stale: run pnpm --filter yt-dlp-transcript-common exec tsx bin/file-schemas-docs.ts`, ); }); } test("every key has a row", () => { const site = renderSiteMarkdown(); for (const key of SITE_KEYS) assert.ok(site.includes(`| [\`${key}\`]`), key); const channel = renderChannelMarkdown(); for (const key of CHANNEL_CONFIG_KEYS) assert.ok(channel.includes(`| \`${key}\` |`), key); }); test("CHANNEL.md's smallest channel is a channel", () => { const m = /`(\{ "handling"[^`]*\})`/.exec(renderChannelMarkdown()); assert.ok(m, "the one-line example is present"); assert.deepEqual(parseChannelConfig(JSON.parse(m![1])), { handling: "youtube", url: "https://www.youtube.com/@example", }); }); test("SITE.md names exactly the keys a save always writes", () => { const always = Object.keys(siteToDisk(parseSite("x", {}))); const m = /A save ALWAYS writes ([^;]*);/.exec(renderSiteMarkdown()); assert.ok(m, "the always-written sentence is present"); const named = [...m![1].matchAll(/`([a-zA-Z]+)`/g)].map((x) => x[1]); assert.deepEqual(named, always); }); test("CHANNEL.md renders each nested table under one heading", () => { const md = renderChannelMarkdown(); for (const key of ["downloadFilter", "audioCheck"]) { const headings = md.split("\n").filter((l) => l.startsWith("#") && l.includes(`\`${key}\``)); assert.equal(headings.length, 1, key); } });