Files
2026-07-13 13:39:12 +08:00

111 lines
4.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const mod = await import("../../scripts/release/gen-contributors.mjs");
const {
extractVersionSection,
parseContributors,
renderContributors,
injectContributors,
NOISE_HANDLES,
} = mod;
const FIXTURE = `# Changelog
## [Unreleased]
---
## [3.9.0] — 2026-08-01
### ✨ New Features
- **feat(a):** thing one. ([#100](https://github.com/x/y/pull/100) — thanks @alice)
- **feat(b):** uses \`@toon-format/toon\` and \`@dnd-kit\`. ([#101](https://github.com/x/y/pull/101) — thanks @bob)
### 🔧 Bug Fixes
- **fix(c):** direct commit fix. (thanks @carol)
- **fix(d):** extracted. Extracted from [#102](https://github.com/x/y/pull/102) by [@dave](https://github.com/dave).
### 📝 Maintenance
- **refactor(rollup):** god-file split ([#200](https://github.com/x/y/pull/200), [#201](https://github.com/x/y/pull/201) — thanks @erin); editorconfig ([#202](https://github.com/x/y/pull/202) — thanks @frank). — thanks @diegosouzapw
---
## [3.8.99] — 2026-07-31
### 🔧 Bug Fixes
- **fix(z):** other version, must not leak. ([#999](https://github.com/x/y/pull/999) — thanks @zoe)
---
`;
test("extractVersionSection returns only the target version body (not the next section)", () => {
const sec = extractVersionSection(FIXTURE, "3.9.0");
assert.ok(sec.includes("thing one"), "includes 3.9.0 content");
assert.ok(!sec.includes("must not leak"), "excludes 3.8.99 content");
assert.ok(!sec.includes("#999"), "does not bleed into next version");
});
test("parseContributors credits per parenthetical group, not a flat scan", () => {
const agg = parseContributors(extractVersionSection(FIXTURE, "3.9.0"));
// rollup: erin gets 200+201, frank gets 202 — NOT both getting all three
assert.deepEqual(
[...agg.get("erin")].sort((a, b) => a - b),
[200, 201]
);
assert.deepEqual([...agg.get("frank")], [202]);
// simple bullets
assert.deepEqual([...agg.get("alice")], [100]);
// direct-commit credit with no PR ref
assert.ok(agg.has("carol") && agg.get("carol").size === 0);
// "Extracted from #N by @X"
assert.deepEqual([...agg.get("dave")], [102]);
});
test("noise handles and the maintainer are excluded from the contributor map", () => {
const agg = parseContributors(extractVersionSection(FIXTURE, "3.9.0"));
assert.ok(!agg.has("toon-format"), "package scope is not a contributor");
assert.ok(!agg.has("dnd-kit"), "package scope is not a contributor");
assert.ok(!agg.has("diegosouzapw"), "maintainer is rendered separately, not in the map");
assert.ok(NOISE_HANDLES.has("toon-format"));
});
test("renderContributors emits an alphabetical table with maintainer last", () => {
const agg = parseContributors(extractVersionSection(FIXTURE, "3.9.0"));
const table = renderContributors("3.9.0", agg);
assert.ok(table.startsWith("### 🙌 Contributors"));
const rows = table.split("\n").filter((l) => l.startsWith("| [@"));
const handles = rows.map((r) => r.match(/@([A-Za-z0-9_-]+)/)[1]);
assert.equal(handles[handles.length - 1], "diegosouzapw", "maintainer is last");
const external = handles.slice(0, -1);
assert.deepEqual(
external,
[...external].sort((a, b) => a.localeCompare(b)),
"external sorted"
);
assert.ok(table.includes("| [@carol](https://github.com/carol) | direct commit / report |"));
});
test("injectContributors inserts before the closing --- and is idempotent", () => {
const once = injectContributors(
FIXTURE,
"3.9.0",
renderContributors("3.9.0", parseContributors(extractVersionSection(FIXTURE, "3.9.0")))
);
assert.ok(once.includes("### 🙌 Contributors"), "section injected");
// 3.8.99 untouched
assert.ok(once.includes("must not leak"));
// idempotent: injecting again does not duplicate
const twice = injectContributors(
once,
"3.9.0",
renderContributors("3.9.0", parseContributors(extractVersionSection(once, "3.9.0")))
);
const count = (twice.match(/### 🙌 Contributors/g) || []).length;
assert.equal(count, 1, "no duplicate Contributors section on re-run");
});