Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit f27aeaca7f99265a9463e1612a9e97f3fe4114f5
parent 0fd627c306a107f1bdbc620027ff517617282e60
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue, 29 Sep 2026 20:50:29 -0400

homepage: the e2e fixture carries a seventh, unlisted site (its own two channels, and the first site's first channel shared); a spec proves the summary, `/` and `/stats/` name it nowhere and count none of its own channels

buildFixtureSummary() is exactly buildFixtureSummary(FIXTURE_SITES, []): the
unlisted site's recordings are generated last, so every listed site's numbers
are unchanged and every other spec reads the six sites as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mhomepage/e2e/fixture-summary.ts | 44+++++++++++++++++++++++++++++++++++++-------
Ahomepage/e2e/unlisted-site.spec.ts | 46++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/homepage/e2e/fixture-summary.ts b/homepage/e2e/fixture-summary.ts @@ -26,7 +26,13 @@ import type { VideoStat } from "../../common/lib/stats"; // symlog axis reaches 10K while the Recent weeks stay linear and small; // • every site transcribes every day of the last 200, so every line is full // and every series has a non-zero start for Indexed; -// • uploads spread over 2019–2026, for the growth chart's months. +// • uploads spread over 2019–2026, for the growth chart's months; +// • a seventh site, UNLISTED (site.json `listed: false`, release 14 slice +// HS): its own two channels transcribe every day like the rest, and it +// also exposes the first site's first channel. The summary names it nowhere +// and counts its own channels in no total, so `buildFixtureSummary()` is +// exactly `buildFixtureSummary(FIXTURE_SITES, [])` — every number the specs +// read is the six listed sites'. export const FIXTURE_SUMMARY_NAME = ".e2e-summary.json"; @@ -42,7 +48,8 @@ export const FIXTURE_PALE_HEX = "#f4c2d7"; // site.json one (a named accent's site, the custom hex's and a site with no // accent have one — the last ends MID-WORD, "Fix" + "ture Three", as // "Jer" + "alyzer" does; the rest show their title plain); `daily` how many -// recordings each channel transcribes a day. +// recordings each channel transcribes a day; `listed` the site.json key +// (absent = listed). export type FixtureSite = { siteId: string; siteTitle: string; @@ -50,6 +57,7 @@ export type FixtureSite = { wordmarkLead?: string; channels: number; daily: number; + listed?: boolean; }; export const FIXTURE_SITES: readonly FixtureSite[] = [ @@ -61,6 +69,16 @@ export const FIXTURE_SITES: readonly FixtureSite[] = [ { siteId: "fixture-six", siteTitle: "Fixture Six", accent: "vermilion", channels: 2, daily: 3 }, ]; +// The unlisted site (an invented id, as every fixture's). Not in FIXTURE_SITES, +// which is the six the pages show. +export const FIXTURE_UNLISTED_SITE: FixtureSite = { + siteId: "fixture-unlisted", + siteTitle: "Fixture Unlisted", + channels: 2, + daily: 6, + listed: false, +}; + const DAY = 86_400_000; const SPIKE = { site: 0, start: Date.UTC(2026, 4, 11), days: 7, count: 12_000 }; const HISTORY_DAYS = 200; @@ -76,7 +94,10 @@ function uploadDate(n: number): string { return ymd(d.getTime()); } -export function buildFixtureSummary(fixtureSites: readonly FixtureSite[] = FIXTURE_SITES) { +export function buildFixtureSummary( + fixtureSites: readonly FixtureSite[] = FIXTURE_SITES, + unlistedSites: readonly FixtureSite[] = [FIXTURE_UNLISTED_SITE], +) { const stats: VideoStat[] = []; const channelSites: Record<string, string[]> = {}; const sites: Site[] = []; @@ -112,17 +133,20 @@ export function buildFixtureSummary(fixtureSites: readonly FixtureSite[] = FIXTU }); }; const today = Date.UTC(2026, 8, 15); - fixtureSites.forEach((s, i) => { + // A site's own channels, `<siteId>-ch<N>`, each transcribing every day of the + // last HISTORY_DAYS; `shared` are other sites' channels it also exposes. + const addSite = (s: FixtureSite, i: number, shared: string[] = []) => { const slugs = Array.from({ length: s.channels }, (_, c) => `${s.siteId}-ch${c + 1}`); - for (const slug of slugs) channelSites[slug] = [s.siteId]; + for (const slug of [...slugs, ...shared]) (channelSites[slug] ??= []).push(s.siteId); sites.push({ siteId: s.siteId, siteTitle: s.siteTitle, siteDescription: `${s.siteTitle}, an e2e fixture archive.`, siteUrl: `https://${s.siteId}.example`, - channels: slugs.map((slug) => ({ slug })), + channels: [...slugs, ...shared].map((slug) => ({ slug })), ...(s.accent ? { accent: s.accent } : {}), ...(s.wordmarkLead ? { wordmarkLead: s.wordmarkLead } : {}), + ...(s.listed === false ? { listed: false } : {}), } as unknown as Site); slugs.forEach((slug, c) => { const name = `${s.siteTitle} Channel ${c + 1}`; @@ -133,13 +157,19 @@ export function buildFixtureSummary(fixtureSites: readonly FixtureSite[] = FIXTU for (let k = 0; k < count; k++) record(i, slug, name, day); } }); - }); + }; + fixtureSites.forEach((s, i) => addSite(s, i)); // The megaspike: one week of bulk transcription on the first site's first // channel. const spikeSite = fixtureSites[SPIKE.site]; for (let k = 0; k < SPIKE.count; k++) { record(SPIKE.site, `${spikeSite.siteId}-ch1`, `${spikeSite.siteTitle} Channel 1`, SPIKE.start + (k % SPIKE.days) * DAY); } + // The unlisted sites LAST, so every listed site's recordings (their numbers, + // dates and states) are the same with them or without. + unlistedSites.forEach((s, j) => + addSite(s, fixtureSites.length + j, [`${spikeSite.siteId}-ch1`]), + ); return buildHomepageSummary(stats, channelSites, sites, FIXTURE_NOW); } diff --git a/homepage/e2e/unlisted-site.spec.ts b/homepage/e2e/unlisted-site.spec.ts @@ -0,0 +1,46 @@ +import fs from "node:fs"; +import path from "node:path"; +import { test, expect } from "@playwright/test"; +import { + FIXTURE_SITES, + FIXTURE_SUMMARY_NAME, + FIXTURE_UNLISTED_SITE, + buildFixtureSummary, +} from "./fixture-summary"; + +// Release 14 slice HS: a site with site.json `listed: false` builds and +// deploys, and the homepage lists it nowhere — not a card, not a chart series, +// not a recent item — and counts the channels only it exposes in no total. +// +// The fixture summary (fixture-summary.ts) is built over six listed sites and +// one unlisted one, which has two channels of its own and shares the first +// site's first channel. + +const NEEDLES = [FIXTURE_UNLISTED_SITE.siteId, FIXTURE_UNLISTED_SITE.siteTitle]; + +test("the summary the server reads is the one built without the unlisted site", () => { + const onDisk = JSON.parse( + fs.readFileSync(path.resolve(process.cwd(), "e2e", FIXTURE_SUMMARY_NAME), "utf8"), + ); + // Every array and every total: what the six listed sites alone produce. + expect(onDisk).toEqual(JSON.parse(JSON.stringify(buildFixtureSummary(FIXTURE_SITES, [])))); + const text = JSON.stringify(onDisk); + for (const needle of NEEDLES) expect(text).not.toContain(needle); + expect(onDisk.sites.map((s: { siteId: string }) => s.siteId)).toEqual( + FIXTURE_SITES.map((s) => s.siteId), + ); +}); + +for (const route of ["/", "/stats/"]) { + test(`${route} names no unlisted site, in its HTML or on the page`, async ({ page }) => { + const res = await page.goto(route); + expect(res?.ok()).toBe(true); + const served = (await res?.text()) ?? ""; + // The listed sites are there, so the check below is not vacuous. + for (const s of FIXTURE_SITES) expect(served).toContain(s.siteId); + for (const needle of NEEDLES) { + expect(served).not.toContain(needle); + expect(await page.content()).not.toContain(needle); + } + }); +}