import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const workflows = ["product-launch-video", "faceless-explainer", "pr-to-video"];
const css = ``;
for (const skill of workflows) {
const scripts = new URL("../../" + skill + "/scripts/", import.meta.url);
const { brandFontFaces } = await import(new URL("captions.mjs", scripts));
test(skill + " stages every captured subset and captions preserve the same coverage", () => {
const project = mkdtempSync(join(tmpdir(), "frame-subsets-"));
try {
mkdirSync(join(project, "capture/extracted"), { recursive: true });
mkdirSync(join(project, "capture/assets/fonts"), { recursive: true });
writeFileSync(
join(project, "capture/extracted/tokens.json"),
JSON.stringify({ fonts: [{ family: "Manrope" }] }),
);
writeFileSync(join(project, "capture/extracted/page.html"), css);
writeFileSync(join(project, "capture/assets/fonts/aaa.woff2"), "cyrillic bytes");
writeFileSync(join(project, "capture/assets/fonts/zzz.woff2"), "latin bytes");
for (let pass = 0; pass < 2; pass++) {
execFileSync(
process.execPath,
[
fileURLToPath(new URL("build-frame.mjs", scripts)),
"--preset",
"capsule",
"--hyperframes",
project,
],
{ stdio: "pipe" },
);
const frame = readFileSync(join(project, "frame.md"), "utf8");
const captions = brandFontFaces(join(project, "frame.md"), project);
for (const output of [frame, captions]) {
assert.match(output, /unicode-range:\s*U\+0400-045F/);
assert.match(output, /unicode-range:\s*U\+0000-00FF/);
assert.equal((output.match(/font-weight:\s*200 800/g) ?? []).length, 2);
assert.equal((output.match(/@font-face/g) ?? []).length, 2);
for (const match of output.matchAll(/url\(["']?(assets\/fonts\/[^"')]+)["']?\)/g)) {
assert.ok(
["cyrillic bytes", "latin bytes"].includes(
readFileSync(join(project, match[1]), "utf8"),
),
);
}
}
assert.equal(readdirSync(join(project, "assets/fonts")).length, 2);
}
} finally {
rmSync(project, { recursive: true, force: true });
}
});
}
test("captured font helper copies stay equal to the owner", () => {
const owner = readFileSync(new URL("./lib/captured-fonts.mjs", import.meta.url), "utf8");
for (const skill of workflows.slice(1)) {
assert.equal(
readFileSync(
new URL("../../" + skill + "/scripts/lib/captured-fonts.mjs", import.meta.url),
"utf8",
),
owner,
);
}
});
test("captured rules stage only available local sources for requested families", async () => {
const { stageCapturedFonts } = await import("./lib/captured-fonts.mjs");
const project = mkdtempSync(join(tmpdir(), "captured-font-sources-"));
try {
mkdirSync(join(project, "capture/extracted"), { recursive: true });
mkdirSync(join(project, "capture/assets/fonts"), { recursive: true });
writeFileSync(join(project, "capture/assets/fonts/local.woff2"), "first");
writeFileSync(
join(project, "capture/extracted/page.html"),
``,
);
const first = stageCapturedFonts(project, ["Manrope"]);
assert.equal(first.faces.length, 1);
assert.deepEqual([...first.families], ["manrope"]);
assert.doesNotMatch(first.faces[0], /Other|https:|missing|local\(/);
assert.match(first.faces[0], /unicode-range: U\+0000-00FF/);
const [file] = first.files;
assert.equal(readFileSync(join(project, file), "utf8"), "first");
writeFileSync(join(project, "capture/assets/fonts/local.woff2"), "refreshed");
stageCapturedFonts(project, ["Manrope"]);
assert.equal(readFileSync(join(project, file), "utf8"), "refreshed");
} finally {
rmSync(project, { recursive: true, force: true });
}
});
for (const skill of workflows) {
test(
skill + " keeps captured family ownership when downloads are missing or names overlap",
async () => {
const { brandFontFaces } = await import(
new URL("../../" + skill + "/scripts/captions.mjs", import.meta.url)
);
const project = mkdtempSync(join(tmpdir(), "font-ownership-"));
try {
mkdirSync(join(project, "capture/extracted"), { recursive: true });
mkdirSync(join(project, "capture/assets/fonts"), { recursive: true });
writeFileSync(
join(project, "frame.md"),
'typography:\n display: { fontFamily: "TT Norms Pro Mono", weight: 400 }\n body: { fontFamily: "TT Norms Pro", weight: 400 }\n',
);
writeFileSync(
join(project, "capture/assets/fonts/TT_Norms_Pro_Mono_Regular.woff2"),
"mono",
);
writeFileSync(
join(project, "capture/extracted/page.html"),
``,
);
const mixed = brandFontFaces(join(project, "frame.md"), project);
assert.equal((mixed.match(/@font-face/g) ?? []).length, 1);
assert.doesNotMatch(mixed, /font-family: 'TT Norms Pro';/);
writeFileSync(
join(project, "capture/extracted/page.html"),
``,
);
assert.equal(brandFontFaces(join(project, "frame.md"), project), "");
} finally {
rmSync(project, { recursive: true, force: true });
}
},
);
}