commit 67ace2d330967dd2ec7b6ec34df37ee3a39db1a4
parent 189bb8a3ed05cbfb446d29a0ac3a6d9e146d4757
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 16:18:11 -0400
homepage: marketing e2e pins the chart, Official instances and the build date against the summary on disk
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 65 insertions(+), 32 deletions(-)
diff --git a/homepage/e2e/marketing.spec.ts b/homepage/e2e/marketing.spec.ts
@@ -1,10 +1,12 @@
+import fs from "node:fs";
+import path from "node:path";
import { test, expect } from "@playwright/test";
// The home page's job is to say what Archilyzer is and offer the download.
-// Everything asserted here is data-INDEPENDENT by construction: no count, no
-// site name, no headline number. The numeric bands are gated on corpus data, so
-// pinning one would make this suite fail on a source-only build — which is
-// exactly the build this page has to work on.
+// Nothing asserted here names a count, a site or a headline number. The numeric
+// parts are gated on corpus data, so the specs that touch them read the summary
+// on disk and branch on it — a source-only build is the build this page has to
+// work on.
test.beforeEach(async ({ page }) => {
await page.goto("/");
@@ -23,7 +25,7 @@ test("says what it is and offers the source", async ({ page }) => {
});
// The hub is a separate app on its own Pages project, linked from the hero
-// when homepage.json names its URL. Data-dependent like the rail below, so the
+// when homepage.json names its URL. Data-dependent like the numbers below, so the
// assertion is conditional the same way: absent is a legal state (a
// source-only build has no hub), but PRESENT means an absolute link off-site —
// never a relative path into this site, which has no search.
@@ -34,19 +36,6 @@ test("the hub link, when there is one, leaves for the hub", async ({ page }) =>
await expect(hub).toHaveAttribute("href", /^https?:\/\/[^/]/);
});
-test("the trust block states what it isn't", async ({ page }) => {
- // The cheapest, highest-value block on the page — every claim in it is
- // simply true, and each one pre-empts a wrong assumption.
- for (const claim of [
- "Not a hosted service",
- "No public repository",
- "You supply the tools",
- "Transcription is slow",
- ]) {
- await expect(page.getByText(claim, { exact: true })).toBeVisible();
- }
-});
-
test("every nav destination resolves", async ({ page }) => {
// The nav must never point at a 404, including on a build with no corpus
// data — which is why /stats/ always exists and degrades in place.
@@ -61,22 +50,66 @@ test("every nav destination resolves", async ({ page }) => {
}
});
-test("the rail never invents a recording", async ({ page }) => {
- // Absent data must produce an empty state, not filler. An archiving tool
- // that fabricates transcript rows on its own marketing page has undercut
- // itself before a visitor has read a sentence.
- const rail = page.getByText("Recent acquisitions");
- const empty = page.getByText(/shipped without corpus data/i);
- const hasRail = await rail.isVisible().catch(() => false);
- if (!hasRail) {
- await expect(empty).toBeVisible();
+// The numeric parts of the page are gated on the build-time summary, so each
+// assertion reads the summary on disk and expects exactly what it implies: the
+// part present with data, absent without. A source-only build (no summary) is
+// a legal state and must render none of them rather than zeroes.
+function readSummary(): {
+ monthly?: unknown[];
+ sites?: unknown[];
+ official?: { sites: number };
+ generatedAt?: string;
+} | null {
+ try {
+ const file = path.resolve(
+ test.info().project.testDir,
+ "..",
+ "public",
+ "homepage-summary.json",
+ );
+ return JSON.parse(fs.readFileSync(file, "utf8"));
+ } catch {
+ return null;
+ }
+}
+
+test("the growth chart renders from the summary, or not at all", async ({
+ page,
+}) => {
+ const summary = readSummary();
+ const chart = page.getByRole("img", { name: /transcripts by the month/i });
+ if (!summary?.monthly?.length || !summary.sites?.length) {
+ await expect(chart).toHaveCount(0);
+ return;
+ }
+ await expect(chart).toBeVisible();
+ await expect(
+ page.getByText(/all official instances\.$/i),
+ ).toBeVisible();
+});
+
+test("official instances link out, and the numbers carry their build date", async ({
+ page,
+}) => {
+ const summary = readSummary();
+ const heading = page.getByRole("heading", { name: "Official instances" });
+ if (!summary?.sites?.length) {
+ await expect(heading).toHaveCount(0);
return;
}
- // With data: every row carries a real date in the log's own format.
- const times = page.locator("time[datetime]");
- await expect(times.first()).toBeVisible();
- for (const text of await times.allTextContents()) {
- expect(text.trim()).toMatch(/^\d{4}-\d{2}-\d{2}$/);
+ await expect(heading).toBeVisible();
+ const section = page.locator("section").filter({ has: heading });
+ const links = section.getByRole("link");
+ expect(await links.count()).toBeGreaterThanOrEqual(1);
+ for (const href of await links.evaluateAll((els) =>
+ els.map((e) => e.getAttribute("href") ?? ""),
+ )) {
+ expect(href).toMatch(/^https?:\/\/[^/]/);
+ }
+ if (summary.official && summary.official.sites > 0) {
+ const time = page.locator("time[datetime]");
+ await expect(time).toHaveCount(1);
+ expect(await time.getAttribute("datetime")).toMatch(/^\d{4}-\d{2}-\d{2}$/);
}
});