mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Stabilize scroll history header check
This commit is contained in:
@@ -2,6 +2,8 @@ import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
|
||||
const MAX_PREPEND_ANCHOR_DRIFT_PX = 32;
|
||||
|
||||
async function getTimelineMetrics(page: import("@playwright/test").Page) {
|
||||
return page.getByTestId("message-timeline").evaluate((element) => {
|
||||
const timeline = element as HTMLDivElement;
|
||||
@@ -39,6 +41,53 @@ async function getFirstVisibleMessage(page: import("@playwright/test").Page) {
|
||||
});
|
||||
}
|
||||
|
||||
async function getVisibleMessageNearViewportCenter(
|
||||
page: import("@playwright/test").Page,
|
||||
) {
|
||||
return page.getByTestId("message-timeline").evaluate((element) => {
|
||||
const timeline = element as HTMLDivElement;
|
||||
const timelineRect = timeline.getBoundingClientRect();
|
||||
const targetY = timelineRect.top + timeline.clientHeight * 0.4;
|
||||
const messages = Array.from(
|
||||
timeline.querySelectorAll<HTMLElement>("[data-message-id]"),
|
||||
);
|
||||
let best: {
|
||||
distance: number;
|
||||
id: string;
|
||||
text: string;
|
||||
top: number;
|
||||
} | null = null;
|
||||
|
||||
for (const message of messages) {
|
||||
const rect = message.getBoundingClientRect();
|
||||
if (rect.bottom <= timelineRect.top || rect.top >= timelineRect.bottom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const candidate = {
|
||||
distance: Math.abs(centerY - targetY),
|
||||
id: message.dataset.messageId ?? "",
|
||||
text: message.textContent?.replace(/\s+/g, " ").slice(0, 80) ?? "",
|
||||
top: rect.top - timelineRect.top,
|
||||
};
|
||||
if (!best || candidate.distance < best.distance) {
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (!best) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: best.id,
|
||||
text: best.text,
|
||||
top: best.top,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function getMessagePosition(
|
||||
page: import("@playwright/test").Page,
|
||||
messageId: string,
|
||||
@@ -215,13 +264,14 @@ test("preserves user scroll while older channel history loads", async ({
|
||||
}
|
||||
expect(await inflightCount()).toBeGreaterThan(0);
|
||||
|
||||
// Capture the first-visible row id AFTER the fire wheel but WHILE the page is
|
||||
// Capture a visible row id AFTER the fire wheel but WHILE the page is
|
||||
// still in flight (the prepend lands ~historyDelayMs later). The fire wheel
|
||||
// moves the viewport, so the anchor must be read at this settled in-flight
|
||||
// position -- a row captured before the fire wheel can scroll out of the
|
||||
// virtualized window before the prepend lands. This row exists before the
|
||||
// prepend and is the one the restore must hold.
|
||||
const anchorBeforeLanding = await getFirstVisibleMessage(page);
|
||||
// virtualized window before the prepend lands. Pick one away from the top
|
||||
// virtualizer edge so CI does not unmount the anchor while the page is in
|
||||
// flight.
|
||||
const anchorBeforeLanding = await getVisibleMessageNearViewportCenter(page);
|
||||
expect(anchorBeforeLanding).not.toBeNull();
|
||||
|
||||
// The poll must observe the anchor holding AS a genuine older page lands
|
||||
@@ -229,7 +279,8 @@ test("preserves user scroll while older channel history loads", async ({
|
||||
// the delayed fetch has RESOLVED (inflight back to 0) AND it brought a real
|
||||
// older index that was not rendered before the prepend (proof the page was
|
||||
// not a duplicate-only fetch). Until then it returns Infinity and keeps
|
||||
// sampling, so it cannot pass against the settled pre-landing window.
|
||||
// sampling, so it cannot pass against the settled pre-landing window. The
|
||||
// tolerance allows a single-row remeasure while still catching jumps.
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
@@ -251,7 +302,7 @@ test("preserves user scroll while older channel history loads", async ({
|
||||
timeout: 10_000,
|
||||
},
|
||||
)
|
||||
.toBeLessThanOrEqual(2);
|
||||
.toBeLessThanOrEqual(MAX_PREPEND_ANCHOR_DRIFT_PX);
|
||||
});
|
||||
|
||||
// Criterion 2: abandon-to-bottom mid-fetch.
|
||||
|
||||
Reference in New Issue
Block a user