import { test, expect } from "@playwright/test"; import type { Page } from "@playwright/test"; import { resetData, writeSite } from "./helpers"; // The preview control on a site's Publish tab. // // NOTHING HERE CLICKS DEPLOY. Every external binary the suite touches has a // fake in e2e/fixtures/bin — wrangler's is fake-wrangler.mjs since release 18 // (WRANGLER_BIN in playwright.config.ts; it deploys nothing and writes its argv // beside the bundle), and the deploy stage itself is publish.spec's. What is // pinned here is everything decided // BEFORE the job: that the two deploys are told apart by their labels, that the // branch input refuses exactly what the server refuses and with the same // sentence, and that the alias is shown while you type — it is a function of // the project and the branch, so it is knowable without deploying anything. // The Deploy controls live under the "Individual steps" disclosure. async function openIndividualSteps(page: Page, siteId: string) { await page.goto(`/sites/${siteId}/publish`); await page.getByText("Individual steps").click(); } const preview = (page: Page) => page.getByRole("region", { name: "Deploy preview" }); // EXACT, not the default substring match. getByLabel("preview branch") once // matched the problem message too, whose aria-label began with the same two // words — the ambiguity was the component's, and it is fixed there; this keeps // the spec from being able to re-acquire it silently. const branchInput = (page: Page) => preview(page).getByRole("textbox", { name: "preview branch", exact: true }); test.beforeEach(async () => { await resetData("empty"); }); test("the production deploy says production, and a preview sits beside it", async ({ page, }) => { await writeSite("testsite", { cloudflareProject: "proj" }); await openIndividualSteps(page, "testsite"); // "Deploy" alone is the button somebody clicks by reflex when they meant the // safe one, which is the whole reason for the extra word. await expect( page.getByRole("button", { name: "Deploy to production", exact: true }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Deploy", exact: true }), ).toHaveCount(0); const panel = preview(page); await expect(panel.getByRole("button", { name: "Deploy preview" })).toBeVisible(); // Defaulted, so the common case is one click. await expect(branchInput(page)).toHaveValue("preview"); }); test("the alias is shown before the deploy, and follows the branch name", async ({ page, }) => { await writeSite("testsite", { cloudflareProject: "proj" }); await openIndividualSteps(page, "testsite"); const panel = preview(page); const link = panel.getByRole("link", { name: "https://preview.proj.pages.dev", }); await expect(link).toBeVisible(); await expect(link).toHaveAttribute("href", "https://preview.proj.pages.dev"); const input = branchInput(page); await input.fill("tags-exclude"); await expect( panel.getByRole("link", { name: "https://tags-exclude.proj.pages.dev" }), ).toBeVisible(); }); test("main disables the preview button and says why", async ({ page }) => { await writeSite("testsite", { cloudflareProject: "proj" }); await openIndividualSteps(page, "testsite"); const panel = preview(page); const button = panel.getByRole("button", { name: "Deploy preview" }); await expect(button).toBeEnabled(); await branchInput(page).fill("main"); await expect(button).toBeDisabled(); await expect(panel.getByText(/is the production branch/)).toBeVisible(); // No alias while the name is refused — a link to a hostname that will not // exist is worse than none. await expect(panel.getByRole("link")).toHaveCount(0); // Uppercase is refused for its shape rather than lowercased into production. await branchInput(page).fill("Main"); await expect(button).toBeDisabled(); await expect( panel.getByText(/not a valid preview branch name/), ).toBeVisible(); // And a good name re-arms it. await branchInput(page).fill("rc-1"); await expect(button).toBeEnabled(); await expect( panel.getByRole("link", { name: "https://rc-1.proj.pages.dev" }), ).toBeVisible(); }); test("a site with no Cloudflare project says so instead of linking a hostname", async ({ page, }) => { await writeSite("noproj", {}); await openIndividualSteps(page, "noproj"); const panel = preview(page); await expect( panel.getByText(/Set a Cloudflare Pages project for this site/), ).toBeVisible(); await expect(panel.getByRole("link")).toHaveCount(0); });