import { test, expect } from "@playwright/test"; import type { Page } from "@playwright/test"; import { resetData, writeSite } from "./helpers"; // Where publishing lives (release 18): the family page has the Publish panel — // a row per site, the hub and the homepage, Publish now and Build all stale — // and a site's Publish tab has its own Build & deploy. The panel's stages run // in publish.spec; this pins the layout and the wiring decided before a job. const row = (page: Page, siteId: string) => page.getByRole("group", { name: `Publish ${siteId}` }); test.beforeEach(async () => { await resetData("empty"); }); test("publishing splits: the family page releases and publishes, the site page builds & deploys", async ({ page, }) => { await writeSite("testsite", { cloudflareProject: "proj" }); await page.goto("/sites"); const release = page.getByRole("heading", { name: "Release notes" }); const publish = page.getByRole("heading", { name: "Publish", exact: true }); await expect(release).toBeVisible(); await expect(publish).toBeVisible(); // Order: the release sits above the Publish panel. expect((await release.boundingBox())!.y).toBeLessThan( (await publish.boundingBox())!.y, ); // "Build all sites" and the batch panel are gone. await expect(page.getByRole("heading", { name: "Build all sites" })).toHaveCount(0); await expect(page.getByRole("group", { name: "Build specific sites" })).toHaveCount(0); await expect( page.getByRole("heading", { name: "Build & deploy", exact: true }), ).toHaveCount(0); await page.goto("/sites/testsite/publish"); await expect( page.getByRole("heading", { name: "Build & deploy", exact: true }), ).toBeVisible(); await expect( page.getByRole("heading", { name: "Publish", exact: true }), ).toHaveCount(0); await expect( page.getByRole("heading", { name: "Release notes" }), ).toHaveCount(0); }); test("Build & deploy is enabled only when the active site has a Cloudflare project", async ({ page, }) => { await writeSite("with-proj", { cloudflareProject: "proj" }); await writeSite("no-proj", {}); // With a Cloudflare project → enabled. await page.goto("/sites/with-proj/publish"); await expect( page.getByRole("button", { name: "Build & deploy", exact: true }), ).toBeEnabled(); // Without one → disabled, with an explanatory notice. await page.goto("/sites/no-proj/publish"); await expect( page.getByRole("button", { name: "Build & deploy", exact: true }), ).toBeDisabled(); await expect( page.getByText(/has no Cloudflare Pages project configured/i), ).toBeVisible(); }); test("/sites has no build-mode toggle: the Publish panel has Publish now and Build all stale", async ({ page, }) => { await writeSite("testsite", { cloudflareProject: "proj" }); await page.goto("/sites"); await expect(page.getByRole("group", { name: "Build mode" })).toHaveCount(0); await expect(page.getByRole("button", { name: "Publish now", exact: true })).toBeVisible(); await expect(page.getByRole("button", { name: "Build all stale", exact: true })).toBeVisible(); // The plan Publish now would run is shown before it is pressed: with no // index stamp yet, the index update comes first. await expect(page.getByTestId("publish-plan")).toContainText("update-index _index"); }); test("a row per site: its chips, and the deploy buttons only where it can deploy", async ({ page, }) => { await writeSite("alpha", { siteTitle: "Alpha", cloudflareProject: "a" }); await writeSite("beta", { siteTitle: "Beta" }); await writeSite("mine", { siteTitle: "Mine", audience: "private" }); await page.goto("/sites"); const alpha = row(page, "alpha"); await expect(alpha.getByRole("button", { name: "Build", exact: true })).toBeVisible(); await expect(alpha.getByRole("button", { name: "Deploy preview", exact: true })).toBeVisible(); await expect(alpha.getByRole("button", { name: "Deploy production", exact: true })).toBeVisible(); // No index yet: every row says so on its index chip, and its build waits for it. await expect(alpha.locator('[data-chip="index"]')).toContainText("no index yet"); await expect(alpha.locator('[data-chip="built"]')).toContainText("update the index first"); await expect(alpha.locator('[data-chip="deployed"]')).toBeVisible(); await expect(alpha.locator('[data-chip="live"]')).toBeVisible(); // No project: built, never deployed — and it says why. const beta = row(page, "beta"); await expect(beta.getByRole("button", { name: "Build", exact: true })).toBeVisible(); await expect(beta.getByRole("button", { name: "Deploy production", exact: true })).toHaveCount(0); await expect(beta.getByText(/No Cloudflare Pages project/)).toBeVisible(); // Private: never deployed. const mine = row(page, "mine"); await expect(mine.getByRole("button", { name: "Deploy production", exact: true })).toHaveCount(0); await expect(mine.getByText(/is private \(audience: private\)/)).toBeVisible(); // A preview name is judged as you type, with the server's sentence. await alpha.getByRole("textbox", { name: "preview branch", exact: true }).fill("main"); await expect(alpha.getByRole("status", { name: "preview problem" })).toContainText(/production branch/); await expect(alpha.getByRole("button", { name: "Deploy preview", exact: true })).toBeDisabled(); });