Files
gacha-event-tracker/test/changelog.test.ts
T
Lucas WintherandClaude Opus 5.5 a74bbd3efe feat(changelog): remember the newest change the reader has seen
First-run setup marks a new reader up to date, since changes mean nothing
before you've used the app. An absent marker therefore means an install
from before the changelog, which is what lets the launch be announced to
existing readers once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:24:05 +02:00

85 lines
3.2 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { existsSync } from "node:fs";
import {
compareUrl,
ENTRIES,
EntrySchema,
launchWindow,
newestId,
NOTES,
NoteSchema,
unseen,
type Entry,
} from "../src/shared/changelog.ts";
/**
* The changelog is reader-facing copy tied to git. These pin its shape — the
* refs' existence is `scripts/check-changelog.ts`'s job, since CI is shallow.
*/
describe("the changelog's content", () => {
test("every entry and note parses", () => {
for (const e of ENTRIES) expect(EntrySchema.safeParse(e).success).toBe(true);
for (const n of NOTES) expect(NoteSchema.safeParse(n).success).toBe(true);
});
test("ids are unique, and both lists are newest first", () => {
expect(new Set(ENTRIES.map((e) => e.id)).size).toBe(ENTRIES.length);
expect(new Set(NOTES.map((n) => n.id)).size).toBe(NOTES.length);
const sorted = (xs: { date: string }[]) =>
xs.every((x, i) => i === 0 || xs[i - 1]!.date >= x.date);
expect(sorted(ENTRIES)).toBe(true);
expect(sorted(NOTES)).toBe(true);
});
test("every entry's note exists, and every note's specs exist on disk", () => {
const ids = new Set(NOTES.map((n) => n.id));
for (const e of ENTRIES) if (e.note !== undefined) expect(ids).toContain(e.note);
for (const n of NOTES) for (const p of n.specs ?? []) expect(existsSync(p)).toBe(true);
});
});
describe("what a reader has not seen", () => {
const e = (id: string, date: string): Entry => ({
id, date, kind: "feature", title: id, ref: "abcdef1",
});
const list = [e("c", "2026-10-13"), e("b", "2026-10-05"), e("a", "2026-09-01")];
test("everything newer than the one they dismissed", () => {
expect(unseen(list, "b", "2026-10-13").map((x) => x.id)).toEqual(["c"]);
expect(unseen(list, "c", "2026-10-13")).toEqual([]);
});
test("never recorded: the last two weeks, once", () => {
expect(unseen(list, undefined, "2026-10-13").map((x) => x.id)).toEqual(["c", "b"]);
});
test("a recorded id that is gone falls back to the launch window, not the whole history", () => {
expect(unseen(list, "renamed", "2026-10-13").map((x) => x.id)).toEqual(["c", "b"]);
});
test("helpers", () => {
expect(launchWindow(list, "2026-10-13", 3).map((x) => x.id)).toEqual(["c"]);
expect(newestId(list)).toBe("c");
expect(compareUrl("https://r", "a1b2c3d", "e4f5a6b")).toBe("https://r/compare/a1b2c3d...e4f5a6b");
});
});
describe("the seen marker in prefs", () => {
test("restore keeps a string and drops anything else", async () => {
const { restorePrefsValue } = await import("../src/client/state/usePrefs.ts");
expect(restorePrefsValue({ changelogSeen: "check-in-goals" }).changelogSeen).toBe("check-in-goals");
expect(restorePrefsValue({ changelogSeen: 7 }).changelogSeen).toBeUndefined();
expect(restorePrefsValue({}).changelogSeen).toBeUndefined();
});
test("first-run setup marks everything seen", async () => {
const { firstRunPrefs } = await import("../src/client/state/usePrefs.ts");
expect(firstRunPrefs(["genshin", "hsr"], ["hsr"], "soon")).toEqual({
onboarded: true,
hiddenGames: ["genshin"],
view: "soon",
changelogSeen: newestId(ENTRIES),
});
});
});