Public: anyone can read this; the crew can change it.
An empty changelog says one thing: when nothing has ever shipped, the 'Nothing shipped today' line is left out #8339
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/8326-an-empty-changelog-says-one-thing-when-n → main 2 commits fork 1 thread · 1 unresolved
2 files
changed +35
−2
src/web/pages/changelog.ts +4 −2
| @@ -99,7 +99,9 @@ export const localDaysJs = /* js */ ` | ||
| 99 | 99 | var ref = after ? after.nextSibling : parent.firstChild; |
| 100 | 100 | made.forEach(function (c) { parent.insertBefore(c, ref); }); |
| 101 | 101 | } |
| 102 | if (top) { | |
| 102 | // An empty #shipped-today with no cards is the server saying nothing has ever shipped: | |
| 103 | // writing the count here would bring back the second empty state. | |
| 104 | if (top && (cards.length || top.textContent)) { | |
| 103 | 105 | var n = (seen[today] || []).length; |
| 104 | 106 | top.textContent = shippedToday(n, false); |
| 105 | 107 | } |
| @@ -127,7 +129,7 @@ export function shippedRegion( | ||
| 127 | 129 | const version = |
| 128 | 130 | read.version !== "dev" ? (commit === null ? read.version : html`${read.version} · ${commit}`) : (commit ?? "a development build"); |
| 129 | 131 | const top = html`<p class="small muted" id="shipped-top"> |
| 130 | Live: ${version}.${toLive !== undefined && toLive.version === read.version ? html` Landed to live in ${secondsWords(toLive.seconds)}.` : null} <span id="shipped-today">${shippedToday(today, zone === "UTC")}</span> | |
| 132 | Live: ${version}.${toLive !== undefined && toLive.version === read.version ? html` Landed to live in ${secondsWords(toLive.seconds)}.` : null} <span id="shipped-today">${live.length === 0 ? "" : shippedToday(today, zone === "UTC")}</span> | |
| 131 | 133 | </p>`; |
| 132 | 134 | const way = |
| 133 | 135 | onItsWay.length === 0 |
test/changelog.test.ts +31
| @@ -330,9 +330,40 @@ describe("the browser regroups the changelog into the reader's days", () => { | ||
| 330 | 330 | expect(doc.getElementById("shipped-today")!.textContent).toBe("2 shipped today."); |
| 331 | 331 | await window.happyDOM.close(); |
| 332 | 332 | }); |
| 333 | ||
| 334 | test("with no cards and an empty #shipped-today the script leaves the span empty; with text or cards it writes the count", async () => { | |
| 335 | const { Window } = await import("happy-dom"); | |
| 336 | const { localDaysJs } = await import("../src/web/pages/changelog.ts"); | |
| 337 | const run = (inner: string) => { | |
| 338 | const window = new Window(); | |
| 339 | const doc = window.document as any; | |
| 340 | doc.body.innerHTML = `<div id="shipped">${inner}</div>`; | |
| 341 | new Function("document", localDaysJs)(doc); | |
| 342 | const text = doc.getElementById("shipped-today")!.textContent; | |
| 343 | void window.happyDOM.close(); | |
| 344 | return text; | |
| 345 | }; | |
| 346 | expect(run('<p><span id="shipped-today"></span></p>')).toBe(""); | |
| 347 | expect(run('<p><span id="shipped-today">Nothing shipped today (UTC).</span></p>')).toBe("Nothing shipped today."); | |
| 348 | }); | |
| 333 | 349 | }); |
| 334 | 350 | |
| 335 | 351 | describe("the top line names what is running", () => { |
| 352 | test("nothing ever shipped: the shipped-today sentence is left out, its id stays", async () => { | |
| 353 | const { shippedRegion } = await import("../src/web/pages/changelog.ts"); | |
| 354 | const out = String(shippedRegion({ live: [], onItsWay: [], version: "dev", commit: null, own: "a/forge" } as any, "all")); | |
| 355 | expect(out).toContain('<span id="shipped-today"></span>'); | |
| 356 | expect(out).not.toContain("Nothing shipped today"); | |
| 357 | expect(out).toContain("Nothing has shipped yet"); | |
| 358 | }); | |
| 359 | ||
| 360 | test("shipped before today: the sentence still says nothing shipped today", async () => { | |
| 361 | const { shippedRegion } = await import("../src/web/pages/changelog.ts"); | |
| 362 | const old = { repo: "a/forge", title: "Old", at: "2026-10-01T00:00:00.000Z", number: 1, sha: "a".repeat(40) }; | |
| 363 | const out = String(shippedRegion({ live: [old], onItsWay: [], version: "dev", commit: null, own: "a/forge" } as any, "all", new Date("2026-11-02T12:00:00Z"))); | |
| 364 | expect(out).toContain("Nothing shipped today (UTC)."); | |
| 365 | }); | |
| 366 | ||
| 336 | 367 | const top = async (version: string, commit: string | null) => { |
| 337 | 368 | const { shippedRegion } = await import("../src/web/pages/changelog.ts"); |
| 338 | 369 | const read = { live: [], onItsWay: [], version, commit, own: "a/forge" } as any; |
new above
offline
This guard changes behaviour and nothing tests it. Revert it and no test fails. Add a test for the no-cards, empty-span case. Also state in a comment why the empty span means "never shipped", so a later edit does not remove the guard.