fix(ingest): keep an unannounced end out of the event blurb

The open-end vocabulary is written down twice and the two copies have to agree.
`parseOpenRange` decides whether a row is datable; `RANGE_PREFIX` strips the
range off the front of the cell so `proseAfterDates` can recover the blurb
behind it. A word the first knows and the second does not still yields the
event — with the leftover end tacked onto the summary a reader actually sees.

That is what happened to all eight Endfield rows the previous commit recovered:
`- TBA Sign-in to get extra pulls for Typhoeus!`. Adding `tba` beside `tbd`
fixes it, and a comment now says why the lists travel together, since the next
one of these will be found the same way — by reading generated fixture output.

Pins the 1.5 page beside the 1.4 one rather than replacing it. Fixtures are
permanent and these are different table shapes, not different data: 1.4 is a
single dated row, 1.5 is nine rows of open ends. The group name gains the
fixture path now that one adapter has two.

Nine rows, eight events. `Ridgeline Flows of Autumn Sign-In` reads
`Period: TBA` with no start at all, so it stays undatable and dropped.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-23 01:07:21 +02:00
co-authored by Claude Opus 5
parent 843e27b4f1
commit 0307d24b92
4 changed files with 2000 additions and 4 deletions
+34 -1
View File
@@ -31,6 +31,10 @@ const CASES: Array<{ adapter: Adapter; fixture: string }> = [
{ adapter: adapter("wuwa-game8-events"), fixture: "fixtures/wuwa/game8-events-2026-08-14" },
{ adapter: adapter("zzz-game8-events"), fixture: "fixtures/zzz/game8-events-2026-08-14" },
{ adapter: adapter("endfield-game8-events"), fixture: "fixtures/endfield/game8-events-2026-08-14" },
// The same page a version later. 1.4's upcoming table held one dated row;
// 1.5's holds nine, every one of them ending "- TBA". Both are pinned because
// they are different shapes, not different data.
{ adapter: adapter("endfield-game8-events"), fixture: "fixtures/endfield/game8-events-2026-08-23" },
{ adapter: adapter("endfield-wikigg-events"), fixture: "fixtures/endfield/wikigg-events-2026-08-14" },
{ adapter: adapter("p5x-game8-events"), fixture: "fixtures/p5x/game8-events-2026-08-17" },
{ adapter: adapter("arknights-akwiki-events"), fixture: "fixtures/arknights/akwiki-events-2026-08-17" },
@@ -59,7 +63,7 @@ async function runAdapter(adapter: Adapter, fixture: string) {
});
}
describe.each(CASES)("$adapter.id", ({ adapter, fixture }) => {
describe.each(CASES)("$adapter.id $fixture", ({ adapter, fixture }) => {
test("matches the checked-in expected output", async () => {
const events = await runAdapter(adapter, fixture);
const expected = await Bun.file(`${fixture}.expected.json`).json();
@@ -175,6 +179,35 @@ describe("nte fixture specifics", () => {
});
});
describe("endfield fixture specifics", () => {
const fixture = "fixtures/endfield/game8-events-2026-08-23";
test("reads an upcoming version whose ends are unannounced", async () => {
// Game8 published the whole 1.5 schedule as "Period: 09/02/2026 - TBA".
// Nine rows; the ninth reads "Period: TBA" with no start at all, so it is
// undatable and dropped rather than guessed.
const events = await runAdapter(adapter("endfield-game8-events"), fixture);
expect(events).toHaveLength(8);
const signIn = events.find((e) => e.title === "Fletched Irontip Sign-In");
expect(signIn?.startsAt).toBe("2026-09-02T00:00:00.000Z");
expect(signIn?.endsAt).toBeNull();
expect(events.map((e) => e.title)).not.toContain(
"Ridgeline Flows of Autumn Sign-In",
);
});
test("keeps the unannounced end out of the summary", async () => {
// The range is structure, not prose. Left in, "- TBA" reaches the reader as
// the opening words of every blurb on the page.
const events = await runAdapter(adapter("endfield-game8-events"), fixture);
expect(
events.find((e) => e.title === "Fletched Irontip Sign-In")?.summary,
).toBe("Sign-in to get extra pulls for Typhoeus!");
for (const e of events) expect(e.summary ?? "").not.toMatch(/\bTBA\b/i);
});
});
describe("inferType", () => {
const cases: Array<[string, EventType]> = [
["Overflowing Abundance Rerun", "rerun"],