import { describe, it, expect } from "vitest"; import type { PlopTypes } from "@turbo/gen"; import generator from "./config.js"; describe("core-package generator", () => { it("is registered with realtime and events in choices list", () => { const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plopMock = { setHelper: () => {}, setGenerator: (name: string, def: PlopTypes.PlopGeneratorConfig) => captured.push({ name, def }), } as unknown as PlopTypes.NodePlopAPI; generator(plopMock); const corePkg = captured.find((c) => c.name === "core-package"); expect(corePkg).toBeDefined(); const prompts = corePkg!.def.prompts as Array<{ name: string; choices: unknown[] }>; expect(prompts[0]!.name).toBe("name"); expect(prompts[0]!.choices).toContain("realtime"); expect(prompts[0]!.choices).toContain("events"); expect(prompts[0]!.choices).toContain("trpc"); expect(prompts[0]!.choices).toContain("ui"); }); }); describe("core-package realtime", () => { it("emits actions covering package files, transpilePackages, and ESLint rules", () => { // Capture the actions returned by the realtime entry. const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plop = { setHelper: () => {}, setGenerator: (n: string, d: unknown) => captured.push({ name: n, def: d as PlopTypes.PlopGeneratorConfig }), } as unknown as PlopTypes.NodePlopAPI; generator(plop); const corePkg = captured.find((c) => c.name === "core-package")!.def; const actions = (corePkg.actions as (a: { name: string }) => PlopTypes.ActionType[])( { name: "realtime" }, ); // Expectations: at least one assertNotPresent guard, multiple `add` actions, and a transpilePackages action. expect(actions.length).toBeGreaterThan(20); // 28 files + extras }); }); describe("core-package events", () => { it("emits actions covering package files, transpilePackages, and ESLint rule splice", () => { const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plop = { setHelper: () => {}, setGenerator: (n: string, d: unknown) => captured.push({ name: n, def: d as PlopTypes.PlopGeneratorConfig }), } as unknown as PlopTypes.NodePlopAPI; generator(plop); const corePkg = captured.find((c) => c.name === "core-package")!.def; const actions = (corePkg.actions as (a: { name: string }) => PlopTypes.ActionType[])( { name: "events" }, ); // 1 guard + 15 template files + transpilePackages + ESLint splice + printNextSteps = 19 expect(actions.length).toBeGreaterThanOrEqual(18); }); }); describe("core-ui-component generator", () => { it("is registered with tier and name prompts", () => { const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plopMock = { setHelper: () => {}, setGenerator: (name: string, def: PlopTypes.PlopGeneratorConfig) => captured.push({ name, def }), } as unknown as PlopTypes.NodePlopAPI; generator(plopMock); const entry = captured.find((c) => c.name === "core-ui-component"); expect(entry).toBeDefined(); const prompts = entry!.def.prompts as Array<{ name: string; choices?: unknown[] }>; expect(prompts.map((p) => p.name)).toEqual(["tier", "name"]); expect(prompts[0]!.choices).toEqual(["atom", "molecule", "organism"]); }); it("for each tier, emits 4 add actions, 1 modify, plus guards and print", () => { const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plopMock = { setHelper: () => {}, setGenerator: (name: string, def: PlopTypes.PlopGeneratorConfig) => captured.push({ name, def }), } as unknown as PlopTypes.NodePlopAPI; generator(plopMock); const corePkg = captured.find((c) => c.name === "core-ui-component")!.def; for (const tier of ["atom", "molecule", "organism"] as const) { const actions = (corePkg.actions as (a: { tier: string; name: string }) => PlopTypes.ActionType[])( { tier, name: "Spinner" }, ); const tierPlural = `${tier}s`; // 4 `add` actions, one per emitted file const adds = actions.filter( (a): a is PlopTypes.AddActionConfig => typeof a === "object" && "type" in a && (a as { type: string }).type === "add", ); expect(adds).toHaveLength(4); const addPaths = adds.map((a) => a.path); expect(addPaths).toContain( `packages/core-ui/src/${tierPlural}/{{kebabCase name}}/{{kebabCase name}}.tsx`, ); expect(addPaths).toContain( `packages/core-ui/src/${tierPlural}/{{kebabCase name}}/{{kebabCase name}}.stories.tsx`, ); expect(addPaths).toContain( `packages/core-ui/src/${tierPlural}/{{kebabCase name}}/{{kebabCase name}}.test.tsx`, ); expect(addPaths).toContain( `packages/core-ui/src/${tierPlural}/{{kebabCase name}}/index.ts`, ); // 1 `modify` action targeting the tier barrel const modifies = actions.filter( (a): a is PlopTypes.ModifyActionConfig => typeof a === "object" && "type" in a && (a as { type: string }).type === "modify", ); expect(modifies).toHaveLength(1); expect(modifies[0]!.path).toBe(`packages/core-ui/src/${tierPlural}/index.ts`); expect(String(modifies[0]!.pattern)).toContain(``); // 3 function actions (2 guards + 1 print) const fns = actions.filter((a) => typeof a === "function"); expect(fns).toHaveLength(3); } }); it("PascalCase validator rejects bad names", () => { const captured: Array<{ name: string; def: PlopTypes.PlopGeneratorConfig }> = []; const plopMock = { setHelper: () => {}, setGenerator: (name: string, def: PlopTypes.PlopGeneratorConfig) => captured.push({ name, def }), } as unknown as PlopTypes.NodePlopAPI; generator(plopMock); const corePkg = captured.find((c) => c.name === "core-ui-component")!.def; const nameValidate = (corePkg.prompts as Array<{ name: string; validate?: (i: string) => string | true }>) .find((p) => p.name === "name")!.validate!; expect(nameValidate("")).toBe("Required"); expect(nameValidate("spinner")).toContain("PascalCase"); expect(nameValidate("123Foo")).toContain("PascalCase"); expect(nameValidate("Foo-Bar")).toContain("PascalCase"); expect(nameValidate("Spinner")).toBe(true); expect(nameValidate("IconButton")).toBe(true); }); });