| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { CALENDAR_WEEKS, buildCalendar, dayLabel, lastDay, levelOf, totalLabel } from "./contribution-calendar.ts"; |
| 5 | |
| 6 | test("the calendar is 53 weeks of seven days, ending with today's week", () => { |
| 7 | // 2026-10-08 is a Thursday. |
| 8 | const calendar = buildCalendar([], "2026-10-08", "2025-10-09"); |
| 9 | assert.equal(calendar.weeks.length, CALENDAR_WEEKS); |
| 10 | for (const week of calendar.weeks) assert.equal(week.length, 7); |
| 11 | const last = calendar.weeks.at(-1)!; |
| 12 | assert.equal(last[4]?.date, "2026-10-08"); |
| 13 | // Friday and Saturday are still to come. |
| 14 | assert.equal(last[5], null); |
| 15 | assert.equal(last[6], null); |
| 16 | // Sunday at the top of every column. |
| 17 | assert.equal(new Date(`${last[0]!.date}T00:00:00Z`).getUTCDay(), 0); |
| 18 | // Days before the year starts are blank; the year has 365 days in it. |
| 19 | const shown = calendar.weeks.flat().filter((day) => day != null); |
| 20 | assert.equal(shown.length, 365); |
| 21 | assert.equal(shown[0]!.date, "2025-10-09"); |
| 22 | assert.equal(calendar.max, 0); |
| 23 | assert.ok(shown.every((day) => day.count === 0 && day.level === 0)); |
| 24 | }); |
| 25 | |
| 26 | test("each day is shaded in quarters of the busiest", () => { |
| 27 | assert.equal(levelOf(0, 10), 0); |
| 28 | assert.equal(levelOf(1, 10), 1); |
| 29 | assert.equal(levelOf(3, 10), 2); |
| 30 | assert.equal(levelOf(6, 10), 3); |
| 31 | assert.equal(levelOf(10, 10), 4); |
| 32 | assert.equal(levelOf(5, 0), 0); |
| 33 | const calendar = buildCalendar( |
| 34 | [ |
| 35 | { date: "2026-10-01", count: 8 }, |
| 36 | { date: "2026-10-02", count: 1 }, |
| 37 | // Outside the year: neither shown nor the busiest. |
| 38 | { date: "2024-01-01", count: 99 }, |
| 39 | ], |
| 40 | "2026-10-08", |
| 41 | "2025-10-09", |
| 42 | ); |
| 43 | assert.equal(calendar.max, 8); |
| 44 | const days = calendar.weeks.flat(); |
| 45 | assert.deepEqual(days.find((day) => day?.date === "2026-10-01"), { date: "2026-10-01", count: 8, commits: 0, level: 4 }); |
| 46 | assert.equal(days.find((day) => day?.date === "2026-10-02")?.level, 1); |
| 47 | }); |
| 48 | |
| 49 | test("months are named over the week they start in", () => { |
| 50 | const { months, weeks } = buildCalendar([], "2026-10-08", "2025-10-09"); |
| 51 | // The year starts partway through October, which still gets its name: |
| 52 | // November is three weeks on, room enough for both. |
| 53 | assert.deepEqual(months[0], { label: "Oct", week: 0 }); |
| 54 | assert.deepEqual( |
| 55 | months.map((month) => month.label), |
| 56 | ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct"], |
| 57 | ); |
| 58 | for (const { label, week } of months.slice(1)) { |
| 59 | assert.ok(weeks[week]!.some((day) => day?.date.endsWith("-01")), label); |
| 60 | } |
| 61 | // Increasing, and never two in one column. |
| 62 | for (let i = 1; i < months.length; i++) assert.ok(months[i]!.week > months[i - 1]!.week); |
| 63 | }); |
| 64 | |
| 65 | test("a month that starts too close to the first column takes its place", () => { |
| 66 | // From Tue 2025-10-21: October's column is the one before November's, |
| 67 | // too narrow for its name. |
| 68 | const squeezed = buildCalendar([], "2026-10-20", "2025-10-21"); |
| 69 | assert.deepEqual(squeezed.months[0], { label: "Nov", week: 1 }); |
| 70 | }); |
| 71 | |
| 72 | test("a year that starts early in a month keeps its first label", () => { |
| 73 | // From Jan 2: January has room before February. |
| 74 | const { months } = buildCalendar([], "2027-01-01", "2026-01-02"); |
| 75 | assert.equal(months[0]!.label, "Jan"); |
| 76 | assert.equal(months[1]!.label, "Feb"); |
| 77 | }); |
| 78 | |
| 79 | test("a bad date draws nothing", () => { |
| 80 | assert.deepEqual(buildCalendar([], "not a date"), { weeks: [], months: [], max: 0 }); |
| 81 | }); |
| 82 | |
| 83 | test("hints and the heading read as sentences", () => { |
| 84 | assert.equal(dayLabel(3, "2026-10-04"), "3 contributions on Oct 4, 2026"); |
| 85 | assert.equal(dayLabel(1, "2026-10-04"), "1 contribution on Oct 4, 2026"); |
| 86 | assert.equal(dayLabel(0, "2026-01-01"), "No contributions on Jan 1, 2026"); |
| 87 | assert.equal(dayLabel(5, "2026-10-04", 3), "5 contributions on Oct 4, 2026, 3 of them commits"); |
| 88 | assert.equal(dayLabel(2, "2026-10-04", 1), "2 contributions on Oct 4, 2026, 1 of them a commit"); |
| 89 | assert.equal(dayLabel(3, "2026-10-04", 3), "3 commits on Oct 4, 2026"); |
| 90 | assert.equal(dayLabel(1, "2026-10-04", 1), "1 commit on Oct 4, 2026"); |
| 91 | assert.equal(totalLabel(0), "0 contributions in the last year"); |
| 92 | assert.equal(totalLabel(1), "1 contribution in the last year"); |
| 93 | assert.equal(totalLabel(1204), "1,204 contributions in the last year"); |
| 94 | assert.equal(lastDay("2025-10-09"), "2026-10-08"); |
| 95 | assert.equal(lastDay("nope"), null); |
| 96 | }); |