The core-package e2e tests cover each generator in isolation. None exercised generators running in sequence — so the feature template shipping without an integrations/cms/index.ts barrel went unnoticed until `gen job` failed against a scaffolded feature. This test scaffolds a feature, then runs `gen job` against it. `gen job` throws at its `<gen:job-tasks>` anchor assertion if the barrel is missing, so the composition gap now fails loudly.
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import { describe, it, expect, onTestFinished } from "vitest";
|
|
import { mkdtempSync, rmSync, cpSync, existsSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { execSync } from "node:child_process";
|
|
import { join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
// Repo root is 2 levels up from turbo/generators/__tests__
|
|
const REPO_ROOT = resolve(
|
|
fileURLToPath(import.meta.url),
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"..",
|
|
);
|
|
|
|
/**
|
|
* Generator-composition e2e: scaffold a feature, then run `gen job` against
|
|
* the freshly-scaffolded feature.
|
|
*
|
|
* The `core-package` e2e tests verify each generator's output in isolation
|
|
* (byte-identical snapshots). This one verifies generators run *in sequence*:
|
|
* `gen job` asserts the consumer feature's `integrations/cms/index.ts` carries
|
|
* a `<gen:job-tasks>` anchor, so this test fails loudly if the `feature`
|
|
* template ever stops emitting that barrel — the regression that shipped once.
|
|
*/
|
|
describe("e2e: feature generator composition", () => {
|
|
it(
|
|
"scaffolds a feature that `gen job` can extend",
|
|
{ timeout: 180_000 },
|
|
() => {
|
|
const tmp = mkdtempSync(join(tmpdir(), "e2e-feature-"));
|
|
// Remove the temp clone when the test finishes so sequential runs
|
|
// don't accumulate and fill disk.
|
|
onTestFinished(() => rmSync(tmp, { recursive: true, force: true }));
|
|
cpSync(REPO_ROOT, tmp, {
|
|
recursive: true,
|
|
filter: (src) =>
|
|
!src.includes("node_modules") &&
|
|
!src.includes(".turbo") &&
|
|
!src.includes(".pnpm-store"),
|
|
});
|
|
|
|
execSync(`cd ${tmp} && pnpm install --frozen-lockfile=false`, {
|
|
stdio: "ignore",
|
|
});
|
|
execSync(
|
|
`cd ${tmp} && pnpm turbo gen feature --args demo-feature Widget widgets`,
|
|
{ stdio: "ignore" },
|
|
);
|
|
// Regression guard: `gen job` throws at its anchor assertion if the
|
|
// feature template did not emit src/integrations/cms/index.ts.
|
|
execSync(
|
|
`cd ${tmp} && pnpm turbo gen job --args demo-feature send-demo-email void`,
|
|
{ stdio: "ignore" },
|
|
);
|
|
|
|
const featureRoot = join(tmp, "packages", "demo-feature");
|
|
|
|
const cmsIndex = join(featureRoot, "src/integrations/cms/index.ts");
|
|
expect(existsSync(cmsIndex)).toBe(true);
|
|
expect(readFileSync(cmsIndex, "utf8")).toContain(
|
|
'export { sendDemoEmailTask } from "./jobs/send-demo-email.task"',
|
|
);
|
|
|
|
expect(
|
|
existsSync(join(featureRoot, "src/jobs/send-demo-email.job.ts")),
|
|
).toBe(true);
|
|
|
|
const pkgJson = JSON.parse(
|
|
readFileSync(join(featureRoot, "package.json"), "utf8"),
|
|
) as { exports: Record<string, string> };
|
|
expect(pkgJson.exports["./cms"]).toBe("./src/integrations/cms/index.ts");
|
|
},
|
|
);
|
|
});
|