Files
suaveplan/packages/ui/react/react-ui/scripts/build-registry.test.ts
2026-05-27 23:47:53 +01:00

138 lines
4.4 KiB
TypeScript

import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
build,
buildRegistryIndex,
buildRegistryItem,
parseArgs,
serialize,
validateIndex,
validateItem,
} from "./build-registry.js";
describe("build-registry", () => {
it(
"buildRegistryItem produces a shadcn-compatible item",
{ timeout: 5000 },
() => {
const item = buildRegistryItem("button");
expect(item.name).toBe("@suaveplan/button");
expect(item.type).toBe("registry:ui");
expect(item.files).toEqual([
{ path: "button.tsx", type: "registry:ui" },
]);
expect(validateItem(item)).toBe(true);
}
);
it(
"buildRegistryIndex includes every supplied name",
{ timeout: 5000 },
() => {
const idx = buildRegistryIndex(["button", "card"]);
expect(idx.items).toHaveLength(2);
expect(idx.items[0]?.name).toBe("@suaveplan/button");
expect(validateIndex(idx)).toBe(true);
}
);
it(
"parseArgs picks up --out and --components flags",
{ timeout: 5000 },
() => {
const parsed = parseArgs([
"--out",
"/tmp/out",
"--components",
"/tmp/src/components",
]);
expect(parsed.outDir).toBe("/tmp/out");
expect(parsed.componentsDir).toBe("/tmp/src/components");
}
);
it(
"serialize emits canonical JSON with trailing newline",
{ timeout: 5000 },
() => {
const out = serialize({ a: 1 });
expect(out).toBe('{\n "a": 1\n}\n');
}
);
it(
"build emits an empty index when components dir does not exist",
{ timeout: 10000 },
async () => {
const dir = await mkdtemp(join(tmpdir(), "registry-empty-"));
const componentsDir = join(dir, "src", "components");
const outDir = join(dir, "registry", "r");
const result = await build({ componentsDir, outDir });
expect(result.items).toEqual([]);
const idx = JSON.parse(await readFile(result.indexPath, "utf8"));
expect(idx.items).toEqual([]);
}
);
it(
"build emits one manifest per component when present",
{ timeout: 10000 },
async () => {
const dir = await mkdtemp(join(tmpdir(), "registry-some-"));
const componentsDir = join(dir, "src", "components");
const outDir = join(dir, "registry", "r");
await mkdir(join(componentsDir, "button"), { recursive: true });
await writeFile(
join(componentsDir, "button", "button.tsx"),
"export const Button = () => null;"
);
await mkdir(join(componentsDir, "card"), { recursive: true });
await writeFile(
join(componentsDir, "card", "index.ts"),
"export {};"
);
const result = await build({ componentsDir, outDir });
expect(result.items.sort()).toEqual(["button", "card"]);
const buttonItem = JSON.parse(
await readFile(join(outDir, "button.json"), "utf8")
);
expect(buttonItem.name).toBe("@suaveplan/button");
const idx = JSON.parse(await readFile(result.indexPath, "utf8"));
expect(idx.items).toHaveLength(2);
}
);
it(
"validateItem rejects malformed items",
{ timeout: 5000 },
() => {
expect(
validateItem({
$schema: "wrong",
name: "x",
type: "registry:ui",
dependencies: [],
registryDependencies: [],
files: [],
} as never)
).toBe(false);
}
);
it(
"validateIndex rejects malformed indexes",
{ timeout: 5000 },
() => {
expect(
validateIndex({
$schema: "wrong",
name: "x",
items: [],
} as never)
).toBe(false);
}
);
});