import { test, expect, type Page } from "@playwright/test"; // The /source/ page and the files it points at (common/publish/source.ts). // Like downloads.spec.ts, correct in BOTH states: the mirror, the raw tree and // the tarball are gitignored build artefacts, so a fresh checkout has none and // the page must say so rather than offer a clone that fails. The release gate // runs `archilyzer source publish` in the checkout first, so the published // branch is the one exercised there. // // This suite runs against `next dev`, which serves public/ from disk but does // NOT serve a directory's index.html at `//` (Pages does), and ignores // _headers — so indexes are requested by name and content types are left to // the preview deploy's checks (and to app/lib/headers.test.ts). // // E2E_EXPECT_SOURCE=1 (declared in playwright.config.ts) makes the empty state // a FAILURE of the three data tests, so a gate that published first cannot // pass with a publish that silently produced nothing, or a loader that always // says "empty". const EXPECT_SOURCE = process.env.E2E_EXPECT_SOURCE === "1"; async function published(page: Page): Promise { await page.goto("/source/"); const has = (await page.getByTestId("source-clone").count()) > 0; if (EXPECT_SOURCE) { expect(has, "E2E_EXPECT_SOURCE=1, and /source/ shows its empty state: run `archilyzer source publish` first").toBe(true); } return has; } test("says what the source is, and that nothing here takes a push", async ({ page }) => { await page.goto("/source/"); await expect(page.getByRole("heading", { level: 1 })).toContainText("Source"); await expect(page.getByText(/There is no GitHub, by choice\./)).toBeVisible(); await expect(page.getByText(/Nothing here takes a push or a pull request\./)).toBeVisible(); await expect(page.getByRole("heading", { name: "What is mirrored" })).toBeVisible(); await expect(page.getByRole("heading", { name: "License" })).toBeVisible(); }); test("with a manifest: the clone command, and one tarball the page, snapshot.json and manifest.json agree on — else the empty state", async ({ page }) => { if (!(await published(page))) { await expect(page.getByTestId("source-empty")).toContainText("No source published in this build."); await expect(page.getByTestId("source-tree-link")).toHaveCount(0); await expect(page.getByTestId("source-tarball-link")).toHaveCount(0); return; } await expect(page.getByTestId("source-clone")).toHaveText( "git clone https://archilyzer.pages.dev/source/archilyzer.git", ); const manifest = await (await page.request.get("/source/manifest.json")).json(); const snapshot = await (await page.request.get("/downloads/snapshot.json")).json(); await expect(page.getByTestId("source-mirror-head")).toHaveText(manifest.mirrorHead); const href = await page.getByTestId("source-tarball-link").getAttribute("href"); expect(href).toBe("/downloads/archilyzer-source.tar.gz"); const tarball = await page.request.get(href!); expect(tarball.status()).toBe(200); expect((await tarball.body()).length).toBe(manifest.tarball.bytes); const onPage = (await page.getByTestId("source-tarball-sha").textContent())?.trim(); expect(onPage).toMatch(/^[0-9a-f]{64}$/); expect(onPage).toBe(manifest.tarball.sha256); expect(onPage).toBe(snapshot.sha256); expect(snapshot.commit).toBe(manifest.mirrorHead); }); test("the mirror is a dumb-HTTP git repository: HEAD, info/refs, objects/info/packs", async ({ page }) => { if (!(await published(page))) { await expect(page.getByTestId("source-empty")).toBeVisible(); return; } const manifest = await (await page.request.get("/source/manifest.json")).json(); const head = await page.request.get("/source/archilyzer.git/HEAD"); expect(head.status()).toBe(200); expect(await head.text()).toBe("ref: refs/heads/main\n"); const refs = await page.request.get("/source/archilyzer.git/info/refs"); expect(await refs.text()).toContain(`${manifest.mirrorHead}\trefs/heads/main`); const packs = await (await page.request.get("/source/archilyzer.git/objects/info/packs")).text(); const first = /^P (pack-[0-9a-f]+\.pack)$/m.exec(packs); expect(first, packs).not.toBeNull(); const pack = await page.request.head(`/source/archilyzer.git/objects/pack/${first![1]}`); expect(pack.status()).toBe(200); }); test("the raw tree: an index per directory, encoded hrefs that resolve, brackets included", async ({ page }) => { if (!(await published(page))) { await expect(page.getByTestId("source-empty")).toBeVisible(); return; } const hrefs = (html: string) => [...html.matchAll(//g)].map((m) => m[1]); const root = await page.request.get("/source/tree/index.html"); expect(root.status()).toBe(200); const rootHrefs = hrefs(await root.text()); const dir = rootHrefs.find((h) => h === "common/"); expect(dir, rootHrefs.join(" ")).toBeTruthy(); expect((await page.request.get(`/source/tree/${dir}index.html`)).status()).toBe(200); const file = rootHrefs.find((h) => h === "README.md"); expect(file).toBeTruthy(); const readme = await page.request.get(`/source/tree/${file}`); expect(readme.status()).toBe(200); expect(await readme.text()).toContain("Archilyzer"); // A variable font's name carries brackets: encoded in the href, and served. const fonts = await page.request.get("/source/tree/umtool/report-to-video/fonts/index.html"); expect(fonts.status()).toBe(200); const bracketed = hrefs(await fonts.text()).find((h) => h.includes("%5B")); expect(bracketed).toBeTruthy(); const font = await page.request.head(`/source/tree/umtool/report-to-video/fonts/${bracketed}`); expect(font.status()).toBe(200); }); // The history pages (release 15 slice SG): stagit's rendering, rewritten by // the publish. Run when the checkout's publish has one (a machine with // stagit); without, the page must show no History block. test("the history pages: the log, a commit, the Files index into the raw tree; the site's line, its theme by stored choice and by the OS; the feed untouched", async ({ page, browser, baseURL }) => { if (!(await published(page))) { await expect(page.getByTestId("source-history")).toHaveCount(0); return; } const manifest = await (await page.request.get("/source/manifest.json")).json(); if (!manifest.history) { await expect(page.getByTestId("source-history")).toHaveCount(0); return; } await expect(page.getByTestId("source-history-head")).toHaveText(manifest.history.head.slice(0, 12)); const headHref = await page.getByTestId("source-history-head").getAttribute("href"); expect(headHref).toBe(`/source/git/commit/${manifest.history.head}.html`); expect((await page.request.get(headHref!)).status()).toBe(200); const shown = (await page.getByTestId("source-history-commits").textContent())!.replace(/\D/g, ""); expect(shown).toBe(String(manifest.history.commits)); // The log lists exactly the commits with a page (all of them under the cap). const listed = await (await page.request.get("/source/git/log.html")).text(); expect(new Set([...listed.matchAll(//g)].map((m) => m[1])).size).toBe(manifest.history.commits); // The log, with the one line the publish adds, linking back here, and the // homepage's base (dark) for a reader who has stored none. await page.getByTestId("source-history-log").click(); await expect(page).toHaveURL(/\/source\/git\/log\.html$/); const back = page.getByRole("link", { name: "Archilyzer · Source" }); await expect(back).toHaveAttribute("href", "/source/"); await expect(page.locator("html")).toHaveAttribute("data-base", "dark"); await expect(page.locator("html")).toHaveAttribute("data-theme-ready", "1"); const bg = () => page.evaluate(() => getComputedStyle(document.body).backgroundColor); expect(await bg()).toBe("rgb(12, 10, 8)"); // The stored choice is the homepage's (one origin, one key). await page.evaluate(() => localStorage.setItem("ytdlp-tb:base", "light")); await page.reload(); await expect(page.locator("html")).toHaveAttribute("data-base", "light"); expect(await bg()).toBe("rgb(243, 246, 247)"); // The newest commit's page resolves from the log, and its diff's file links // go to the raw tree (no per-file pages are published). const first = page.locator("#log a[href^='commit/']").first(); await expect(first).toHaveAttribute("href", `commit/${manifest.history.head}.html`); const commit = await page.request.get(`/source/git/commit/${manifest.history.head}.html`); expect(commit.status()).toBe(200); const commitHtml = await commit.text(); expect(commitHtml).toContain('Archilyzer · Source'); expect(commitHtml).not.toMatch(/href="(?:\.\.\/)*file\//); // Files is an index into the tree: its first link is a raw file, served. await page.goto("/source/git/files.html"); const href = await page.locator("#files a").first().getAttribute("href"); expect(href).toMatch(/^\.\.\/tree\//); const target = new URL(href!, `${baseURL}/source/git/files.html`).pathname; expect((await page.request.get(target)).status()).toBe(200); expect((await page.request.get("/source/git/refs.html")).status()).toBe(200); // The feeds are not pages: nothing is injected into them. const atom = await (await page.request.get("/source/git/atom.xml")).text(); expect(atom).toContain(" getComputedStyle(document.body).backgroundColor), `no JS, a ${scheme} OS`).toBe(rgb); await ctx.close(); } }); test("the Downloads page points at the mirror for the history", async ({ page }) => { await page.goto("/downloads/"); await expect(page.getByRole("link", { name: "read-only git mirror" })).toHaveAttribute("href", "/source/"); });