diff --git a/test/digest-email.test.ts b/test/digest-email.test.ts new file mode 100644 index 000000000..c3e75721b --- /dev/null +++ b/test/digest-email.test.ts @@ -0,0 +1,219 @@ +import { describe, it, expect } from "vitest"; +import { buildDigestHtml, buildDigestText } from "../src/lib/digest-email"; + +const BASE_DATA = { + githubLogin: "testuser", + unsubscribeUrl: "https://example.com/unsubscribe?token=abc", + weekLabel: "Week of 1 June 2026", + metrics: null, +}; + +describe("digest-email", () => { + describe("buildDigestHtml", () => { + it("includes the githubLogin in the greeting", () => { + const html = buildDigestHtml({ ...BASE_DATA, githubLogin: "Alice" }); + expect(html).toContain("Hey Alice"); + }); + + it("includes the weekLabel in the title and header", () => { + const html = buildDigestHtml({ ...BASE_DATA, weekLabel: "Week of 15 June 2026" }); + expect(html).toContain("Week of 15 June 2026"); + }); + + it("includes the unsubscribeUrl in the footer", () => { + const url = "https://example.com/unsubscribe?token=xyz"; + const html = buildDigestHtml({ ...BASE_DATA, unsubscribeUrl: url }); + expect(html).toContain(url); + }); + + it("renders fallback message when metrics is null", () => { + const html = buildDigestHtml({ ...BASE_DATA, metrics: null }); + expect(html).toContain("Metrics are loading"); + }); + + it("renders streak section when metrics present with streak", () => { + const html = buildDigestHtml({ + ...BASE_DATA, + metrics: { + streak: { current: 5, longest: 10, lastCommitDate: null }, + weeklyCommits: 20, + prsThisWeek: 3, + weeklyActiveDays: 4, + topLanguages: [], + topRepos: [], + }, + }); + expect(html).toContain("Current Streak"); + expect(html).toContain("5 days"); + }); + + it("renders weekly activity section with correct commit count", () => { + const html = buildDigestHtml({ + ...BASE_DATA, + metrics: { + streak: { current: 0, longest: 0, lastCommitDate: null }, + weeklyCommits: 42, + prsThisWeek: 2, + weeklyActiveDays: 3, + topLanguages: [], + topRepos: [], + }, + }); + expect(html).toContain("42"); + expect(html).toContain("PRs merged"); + // weeklyActiveDays is rendered as "3" followed by span with "/7" + expect(html).toContain("3"); + expect(html).toContain("/7"); + }); + + it("renders top languages section when languages are present", () => { + const html = buildDigestHtml({ + ...BASE_DATA, + metrics: { + streak: { current: 0, longest: 0, lastCommitDate: null }, + weeklyCommits: 10, + prsThisWeek: 1, + weeklyActiveDays: 2, + topLanguages: [{ name: "TypeScript", percentage: 80.5 }], + topRepos: [], + }, + }); + expect(html).toContain("Top languages"); + expect(html).toContain("TypeScript"); + }); + + it("renders top repos section when repos are present", () => { + const html = buildDigestHtml({ + ...BASE_DATA, + metrics: { + streak: { current: 0, longest: 0, lastCommitDate: null }, + weeklyCommits: 10, + prsThisWeek: 1, + weeklyActiveDays: 2, + topLanguages: [], + topRepos: [{ name: "my-project", commits: 5, url: "https://github.com/test/my-project" }], + }, + }); + expect(html).toContain("Most active repositories"); + expect(html).toContain("my-project"); + }); + + it("escapes HTML in githubLogin", () => { + const html = buildDigestHtml({ ...BASE_DATA, githubLogin: "" }); + expect(html).not.toContain("