commit d48144397a89c335a5c67bd673e53d1a0fc3226d
parent 330f3134069da858cc9a89762b2d6474ab82cbb9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 01:52:06 -0400
sites: the Homepage group stamps data-hydrated; its specs wait for it before a click or a keystroke, and the queued lane is asserted with no alert
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/editor/app/sites/components/HomepageBuildButtons.tsx b/editor/app/sites/components/HomepageBuildButtons.tsx
@@ -12,6 +12,7 @@ import {
buildHomepageAction,
deployHomepageAction,
} from "../lib/homepageDeployActions";
+import { useHydrated } from "../../operations/components/useOperationsStatus";
import { JobLane } from "./JobLane";
type Lane = {
@@ -53,6 +54,7 @@ export function HomepageBuildButtons({ project, builtAt }: Props) {
const [preview, setPreview] = useState("");
const [lane, setLane] = useState<Lane | null>(null);
const [run, setRun] = useState(0);
+ const hydrated = useHydrated();
const branch = preview.trim();
const problem = branch ? previewBranchProblem(branch) : null;
@@ -75,7 +77,15 @@ export function HomepageBuildButtons({ project, builtAt }: Props) {
}
return (
- <div role="group" aria-label="Homepage build" className="flex flex-col gap-3">
+ // data-hydrated: a TESTING AFFORDANCE, as on the operation lanes. A click
+ // that lands before React hydrates fires nothing at all, so a spec waits
+ // for this before it clicks Build homepage.
+ <div
+ role="group"
+ aria-label="Homepage build"
+ className="flex flex-col gap-3"
+ data-hydrated={hydrated ? "true" : undefined}
+ >
<div className="flex flex-wrap items-center gap-3">
<button
type="button"
diff --git a/editor/e2e/sites-homepage.spec.ts b/editor/e2e/sites-homepage.spec.ts
@@ -35,6 +35,15 @@ const deployAfter = (page: Page) => group(page).getByLabel("Deploy after build")
const previewInput = (page: Page) =>
group(page).getByRole("textbox", { name: "preview branch", exact: true });
+// A click or a keystroke that lands before React hydrates is lost without a
+// trace; the group stamps data-hydrated once its mount effect has run.
+async function openSites(page: Page) {
+ await page.goto("/sites");
+ await expect(group(page)).toHaveAttribute("data-hydrated", "true", {
+ timeout: 15_000,
+ });
+}
+
type Meta = {
id: string;
kind: string;
@@ -107,7 +116,7 @@ test("the Homepage section follows Hub, with the build and deploy controls and w
test("a bad preview name greys out every deploy and says why; a good one shows its alias", async ({
page,
}) => {
- await page.goto("/sites");
+ await openSites(page);
const ships = group(page).getByTestId("homepage-ships");
await previewInput(page).fill("main");
@@ -162,12 +171,13 @@ test("Build homepage starts a build-homepage job on the build queue (held there,
);
expect(hold.ok()).toBe(true);
- await page.goto("/sites");
+ await openSites(page);
await expect(deployAfter(page)).not.toBeChecked();
await buildButton(page).click();
- // The lane: queued behind the holder.
- await expect(group(page).getByText(/^Queued/)).toBeVisible();
+ // The lane: queued behind the holder — not refused, not running.
+ await expect(group(page).getByText(/^Queued/)).toBeVisible({ timeout: 15_000 });
+ await expect(group(page).getByRole("alert")).toHaveCount(0);
// One build-homepage job, on the build queue, queued — and nothing else.
await expect.poll(async () => (await metasOfKind("build-homepage")).length).toBe(1);