test(desktop): await thread scroll anchor (#3174)

## Why
The focus/split E2E test could capture the thread root before its
programmatic middle-thread scroll had settled, then incorrectly report a
scroll-restoration failure.

## What
- Poll until the requested middle-thread scroll position is applied
- Require the captured anchor to intersect the thread viewport and
differ from the root
- Preserve the existing focus-to-split-to-focus viewport assertions

## Risk Assessment
Low — test-only synchronization change with no production behavior
changes.

## References
- Original failure:
https://github.com/block/buzz/actions/runs/30231271427/job/89870533541
- Buzz thread:
buzz://message?channel=12dd513d-45fd-48ff-80ac-8596d2fcc9d3&id=87ce6024b4bf74bfac2fa75d9f7bbbcc8f8fe2df460afe534152c495929f51ba
- Reproduced confidence: 20 consecutive targeted passes, full spec pass,
`just desktop-ci`, and `just ci`

Generated with Codex

Signed-off-by: npub1x4hk035p3p9q39a3fcrd2fe30lpkrhr5dwe0cqzzjphxyyh8m0gsq4vqap <356f67c681884a0897b14e06d527317fc361dc746bb2fc0042906e6212e7dbd1@buzz.block.builderlab.xyz>
Co-authored-by: npub1x4hk035p3p9q39a3fcrd2fe30lpkrhr5dwe0cqzzjphxyyh8m0gsq4vqap <356f67c681884a0897b14e06d527317fc361dc746bb2fc0042906e6212e7dbd1@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
This commit is contained in:
Cameron Hotchkies
2026-08-06 08:04:39 -07:00
committed by GitHub
parent 480c41ebf1
commit 9213090f60

View File

@@ -30,17 +30,37 @@ async function seedLongThread(page: import("@playwright/test").Page) {
});
}
async function topVisibleMessageId(
async function scrollToMiddleVisibleMessage(
body: import("@playwright/test").Locator,
threadRootId: string,
): Promise<string> {
return body.evaluate((element) => {
const top = element.getBoundingClientRect().top;
const row = Array.from(
element.querySelectorAll<HTMLElement>("[data-message-id]"),
).find((candidate) => candidate.getBoundingClientRect().bottom > top);
if (!row?.dataset.messageId) throw new Error("No visible thread anchor");
return row.dataset.messageId;
});
let anchorId: string | null = null;
await expect
.poll(async () => {
anchorId = await body.evaluate((element) => {
const maxScrollTop = element.scrollHeight - element.clientHeight;
if (maxScrollTop <= 0) return null;
const targetScrollTop = Math.floor(maxScrollTop * 0.4);
element.scrollTop = targetScrollTop;
element.dispatchEvent(new Event("scroll", { bubbles: true }));
if (Math.abs(element.scrollTop - targetScrollTop) > 1) return null;
const bounds = element.getBoundingClientRect();
const row = Array.from(
element.querySelectorAll<HTMLElement>("[data-message-id]"),
).find((candidate) => {
const rect = candidate.getBoundingClientRect();
return rect.bottom > bounds.top && rect.top < bounds.bottom;
});
return row?.dataset.messageId ?? null;
});
return anchorId !== null && anchorId !== threadRootId;
})
.toBe(true);
if (!anchorId) throw new Error("No visible middle-thread anchor");
return anchorId;
}
/**
@@ -181,11 +201,7 @@ test("focus and split preserve reading context and interaction ownership", async
.toBe(true);
await expect(channel).toHaveAttribute("inert", "");
await body.evaluate((element) => {
element.scrollTop = element.scrollHeight * 0.4;
element.dispatchEvent(new Event("scroll", { bubbles: true }));
});
const anchorId = await topVisibleMessageId(body);
const anchorId = await scrollToMiddleVisibleMessage(body, rootId);
const focusModeToggle = page.getByRole("button", {
name: "Show thread beside channel",