import { test, describe } from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import os from "node:os"; import { registerFeatureInReleasePlease } from "./release-please-utils.ts"; function setupRepo() { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "rp-utils-")); fs.writeFileSync( path.join(tmp, ".release-please-manifest.json"), JSON.stringify( { ".": "0.1.0", "packages/auth": "0.1.0", "packages/blog": "0.1.0", }, null, 2, ) + "\n", ); fs.writeFileSync( path.join(tmp, "release-please-config.json"), JSON.stringify( { "release-type": "node", "include-component-in-tag": true, packages: { ".": { "package-name": "template-vertical", component: "template", "changelog-path": "CHANGELOG.md", }, "packages/auth": { "package-name": "@repo/auth", component: "auth", "changelog-path": "CHANGELOG.md", }, }, }, null, 2, ) + "\n", ); return tmp; } describe("registerFeatureInReleasePlease", () => { test("adds a new package to both files", () => { const tmp = setupRepo(); try { const { manifestChanged, configChanged } = registerFeatureInReleasePlease( tmp, "comments", ); assert.equal(manifestChanged, true); assert.equal(configChanged, true); const manifest = JSON.parse( fs.readFileSync( path.join(tmp, ".release-please-manifest.json"), "utf8", ), ); assert.equal(manifest["packages/comments"], "0.1.0"); const config = JSON.parse( fs.readFileSync(path.join(tmp, "release-please-config.json"), "utf8"), ); assert.deepEqual(config.packages["packages/comments"], { "package-name": "@repo/comments", component: "comments", "changelog-path": "CHANGELOG.md", }); } finally { fs.rmSync(tmp, { recursive: true, force: true }); } }); test("is idempotent — re-running on an already-tracked feature is a no-op", () => { const tmp = setupRepo(); try { const first = registerFeatureInReleasePlease(tmp, "comments"); assert.equal(first.manifestChanged, true); const second = registerFeatureInReleasePlease(tmp, "comments"); assert.equal(second.manifestChanged, false); assert.equal(second.configChanged, false); } finally { fs.rmSync(tmp, { recursive: true, force: true }); } }); test("keeps root ('.') entry first and sorts the rest alphabetically", () => { const tmp = setupRepo(); try { registerFeatureInReleasePlease(tmp, "alphabetically-first-comments"); const manifestText = fs.readFileSync( path.join(tmp, ".release-please-manifest.json"), "utf8", ); const keys = Object.keys(JSON.parse(manifestText)); assert.equal(keys[0], ".", "root should always be first"); // The rest are alphabetical const rest = keys.slice(1); const sorted = [...rest].sort(); assert.deepEqual(rest, sorted); const configText = fs.readFileSync( path.join(tmp, "release-please-config.json"), "utf8", ); const packageKeys = Object.keys(JSON.parse(configText).packages); assert.equal(packageKeys[0], "."); assert.deepEqual(packageKeys.slice(1), [...packageKeys.slice(1)].sort()); } finally { fs.rmSync(tmp, { recursive: true, force: true }); } }); test("throws if .release-please-manifest.json is missing", () => { const tmp = setupRepo(); try { fs.unlinkSync(path.join(tmp, ".release-please-manifest.json")); assert.throws( () => registerFeatureInReleasePlease(tmp, "x"), /\.release-please-manifest\.json is missing/, ); } finally { fs.rmSync(tmp, { recursive: true, force: true }); } }); test("throws if release-please-config.json is missing", () => { const tmp = setupRepo(); try { fs.unlinkSync(path.join(tmp, "release-please-config.json")); assert.throws( () => registerFeatureInReleasePlease(tmp, "x"), /release-please-config\.json is missing/, ); } finally { fs.rmSync(tmp, { recursive: true, force: true }); } }); });