Files
iptvnator/tools/release/build-release-notes.test.mjs
T
4gray 02b966895d docs(release): backfill curated 0.23.0 release notes, fix the notes CLI -- trap (#1263)
Backfills the 0.23.0 release notes the .changes/ pipeline missed: it landed
after most of the release was already merged, leaving 4 notes for 79 commits.

Adds 22 curated notes (26 total: 14 features, 11 fixes, 1 perf). Curated
rather than exhaustive — the GitHub release body renders these above
GitHub's own list of every merged PR, so related PRs are folded into one
note per user-facing story: shared player controls (8 PRs), embedded MPV
frame-copy (4), manual EPG mapping (3), plus five more pairs. Tooling-only
scopes get no note. No screenshot slugs: none of the five manifest shots
depicts a 0.23 headline feature, and the capture run asserts TMDB
enrichment stays disabled.

Two tooling fixes found while writing them:

- parseArgs now ignores a bare `--`. npm needs it to forward arguments past
  the script name; pnpm hands it to the script verbatim, so
  `pnpm run release:notes:github -- --version 0.24.0` died on the very
  separator typed to make forwarding work. Unknown flags and missing values
  still fail as before. Covered by new subprocess CLI tests wired into the
  release-tools target.
- .changes/README.md claimed every non-consume mode was a safe dry run;
  --format changelog and --format blog write their target file.

No app or lib code, no version bump, no --consume, no CHANGELOG.md or
website changes — those stay owned by release-cut.
2026-07-27 01:36:11 +02:00

120 lines
3.6 KiB
JavaScript

/**
* CLI-level tests for build-release-notes.mjs.
*
* The script parses its arguments and runs on import, so these drive the real
* entry point in a subprocess rather than importing parseArgs directly.
*/
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { after, describe, it } from 'node:test';
const workspaceRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../..'
);
const CLI = path.join(workspaceRoot, 'tools/release/build-release-notes.mjs');
const tempDirs = [];
/** A notes directory holding one valid note, so runs have something to render. */
function makeNotesDir() {
const directory = mkdtempSync(path.join(tmpdir(), 'release-cli-'));
tempDirs.push(directory);
writeFileSync(
path.join(directory, 'playback-example.md'),
'---\ntype: feature\narea: playback\n---\n\nAn example note.\n',
'utf8'
);
return directory;
}
/**
* @returns {{ status: number, stdout: string, stderr: string }}
*/
function runCli(args) {
try {
const stdout = execFileSync(process.execPath, [CLI, ...args], {
cwd: workspaceRoot,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return { status: 0, stdout, stderr: '' };
} catch (error) {
return {
status: error.status ?? 1,
stdout: error.stdout ?? '',
stderr: error.stderr ?? '',
};
}
}
after(() => {
for (const directory of tempDirs) {
rmSync(directory, { recursive: true, force: true });
}
});
describe('build-release-notes CLI arguments', () => {
it('validates a notes directory', () => {
const result = runCli(['--validate', '--dir', makeNotesDir()]);
assert.equal(result.status, 0);
assert.match(result.stdout, /1 release note\(s\) valid\./);
});
it('ignores a bare `--` separator', () => {
// pnpm forwards `--` to the script instead of consuming it, so the
// npm-style `pnpm run release:notes:validate -- --dir x` reaches us
// with the separator still attached.
const result = runCli(['--validate', '--', '--dir', makeNotesDir()]);
assert.equal(result.status, 0);
assert.match(result.stdout, /1 release note\(s\) valid\./);
});
it('ignores a leading `--` separator', () => {
const result = runCli(['--', '--validate', '--dir', makeNotesDir()]);
assert.equal(result.status, 0);
assert.match(result.stdout, /1 release note\(s\) valid\./);
});
it('renders the github body with a version passed after `--`', () => {
const result = runCli([
'--format',
'github',
'--',
'--version',
'0.24.0',
'--dir',
makeNotesDir(),
]);
assert.equal(result.status, 0);
assert.match(result.stdout, /## Features/);
assert.match(result.stdout, /\*\*playback\*\* — An example note\./);
});
it('still rejects a genuinely unknown argument', () => {
const result = runCli(['--validate', '--nope']);
assert.equal(result.status, 1);
assert.match(result.stderr, /unknown argument: --nope/);
});
it('still rejects a flag whose value is missing', () => {
const result = runCli(['--format']);
assert.equal(result.status, 1);
assert.match(result.stderr, /--format requires a value/);
});
});