fix(changelog): make every surface split a release the same way

The app, the website and the release scripts each parse CHANGELOG.md with
their own code, and only the app skipped fenced code blocks and read CRLF.
A `###` or a release heading inside a fenced example in a highlight would
have ended the highlights early on the website and in the GitHub release
body, or started a phantom release, while the app showed it correctly.

The website and script parsers now follow the app's rules, and one case
file, scripts/test-support/changelog-structure.json, is run by all three
suites so a rule cannot change in one place only.

The release-notes script tests were not part of any CI job; they now run
with the other script contract tests.
This commit is contained in:
Mohamed Boudra
2026-11-10 10:04:33 +01:00
parent eda939b092
commit ccdca942e9
8 changed files with 361 additions and 16 deletions
+41
View File
@@ -0,0 +1,41 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { parseChangelogEntries, splitChangelogHighlights } from "./changelog-utils.mjs";
// The cases the app, the website and the release scripts must all split the same way.
const structureCases = JSON.parse(
readFileSync(new URL("./test-support/changelog-structure.json", import.meta.url), "utf8"),
);
for (const lineEnding of ["\n", "\r\n"]) {
test(`splits the shared cases with ${JSON.stringify(lineEnding)} line endings`, () => {
const entries = parseChangelogEntries(structureCases.document.join(lineEnding));
assert.deepEqual(
entries.map((entry) => {
const { highlightLines, sectionLines } = splitChangelogHighlights(entry.bodyLines);
return {
version: entry.version,
date: entry.date,
highlights: highlightLines,
sections: sectionLines,
};
}),
structureCases.releases.map((release) => ({
version: release.version,
date: release.date,
highlights: release.highlights,
sections: release.sections,
})),
);
});
}
test("finds an entry for every release heading line in the repository's CHANGELOG.md", () => {
for (const file of ["CHANGELOG.md", "CHANGELOG.zh-CN.md"]) {
const changelog = readFileSync(new URL(`../${file}`, import.meta.url), "utf8");
const headingLines = changelog.split("\n").filter((line) => line.startsWith("## "));
assert.equal(parseChangelogEntries(changelog).length, headingLines.length, file);
}
});