Skip to content
Source
Sign inSign up
Open History to see the last commit

generate-release-notes.test.ts

107 lines4.0 KB

1import { assert, it } from "@effect/vitest";
2​
3import * as Option from "effect/Option";
4​
5import {
6 countContributors,
7 formatReleaseSummary,
8 isReleaseNoteCommit,
9 readReleaseHighlights,
10 type PullRequest,
11} from "./generate-release-notes.ts";
12​
13const pullRequest = (author: PullRequest["author"]): PullRequest => ({
14 body: "",
15 additions: 1,
16 deletions: 1,
17 changedFiles: 1,
18 author,
19});
20​
21it("counts each person once and leaves out bots", () => {
22 const commits = [
23 { subject: "fix(web): one (#1)", authorName: "Ada Lovelace" },
24 { subject: "fix(web): two (#2)", authorName: "Ada Lovelace" },
25 { subject: "chore: bump (#3)", authorName: "dependabot[bot]" },
26 { subject: "fix: direct push by a PR author", authorName: "Ada Lovelace" },
27 { subject: "fix: direct push by someone else", authorName: "Grace Hopper" },
28 { subject: "chore: direct push by a bot", authorName: "t3-code[bot]" },
29 { subject: "fix(web): four (#4)", authorName: "Linus T" },
30 { subject: "fix: direct push by a PR author with no profile name", authorName: "Linus T" },
31 { subject: "fix(web): five, whose PR did not resolve (#5)", authorName: "Ken T" },
32 ];
33 const pullRequests = new Map([
34 [1, pullRequest({ __typename: "User", login: "ada", name: "Ada Lovelace" })],
35 [2, pullRequest({ __typename: "User", login: "ada", name: "Ada Lovelace" })],
36 [3, pullRequest({ __typename: "Bot", login: "dependabot" })],
37 [4, pullRequest({ __typename: "User", login: "linus", name: null })],
38 ]);
39​
40 assert.equal(countContributors(commits, pullRequests), 4);
41});
42​
43it("leaves mobile-only scopes out and keeps shared ones", () => {
44 const subjects = {
45 "fix(mobile): a": false,
46 "feat(android): b": false,
47 "fix(ios)!: c": false,
48 "fix(web,mobile): d": true,
49 "feat(mobile,web): f": true,
50 "fix(server): e": true,
51 };
52 for (const [subject, kept] of Object.entries(subjects)) {
53 assert.equal(isReleaseNoteCommit({ subject, authorName: "" }), kept, subject);
54 }
55});
56​
57it("reads highlights only from a completed JSON response", () => {
58 const output = [
59 { type: "message", content: [{ type: "output_text", text: ' {"highlights":[]} ' }] },
60 ];
61 assert.deepStrictEqual(readReleaseHighlights({ status: "completed", output }), Option.some([]));
62 assert.deepStrictEqual(readReleaseHighlights({ status: "incomplete", output }), Option.none());
63});
64​
65it("counts fixes and performance changes once, outside the selected highlights", () => {
66 const commits = [
67 "feat: schedule a task once",
68 "fix: use the selected time",
69 "perf(server): read less history",
70 "fix(mobile): restore the chat position",
71 "docs: update instructions",
72 ].map((subject) => ({ subject, authorName: "" }));
73 const selected = [
74 { label: "One-time tasks", detail: "Schedule a task for a specific time.", commits: [1, 2] },
75 ] as const;
76 assert.deepStrictEqual(
77 formatReleaseSummary(commits, selected),
78 Option.some(
79 "- **One-time tasks.** Schedule a task for a specific time.\n\n### Plus 2 more bug fixes and performance improvements.",
80 ),
81 );
82 assert.deepStrictEqual(
83 formatReleaseSummary(commits.slice(0, 2), selected),
84 Option.some("- **One-time tasks.** Schedule a task for a specific time."),
85 );
86 assert.deepStrictEqual(formatReleaseSummary(commits.slice(2), selected), Option.none());
87 assert.deepStrictEqual(
88 formatReleaseSummary(commits, []),
89 Option.some("**3 bug fixes and performance improvements.**"),
90 );
91 assert.deepStrictEqual(formatReleaseSummary(commits.slice(-1), []), Option.some(""));
92});
93​
94it("counts fixes beyond the seven displayed highlights in the footer", () => {
95 const commits = Array.from({ length: 8 }, (_, i) => ({
96 subject: `fix: change ${i + 1}`,
97 authorName: "",
98 }));
99 const highlights = commits.map((_, i) => ({
100 label: `Fix ${i + 1}`,
101 detail: "A fix.",
102 commits: [i + 1] as const,
103 }));
104 const summary = Option.getOrThrow(formatReleaseSummary(commits, highlights));
105 assert.lengthOf(summary.match(/^- /gm) ?? [], 7);
106 assert.include(summary, "Plus 1 more bug fix.");
107});