test(timeline): align eva e2e assertions to the virtualized contract

Three eva-authored e2e assertions encoded the pre-virtualization layout and
break once the timeline windows rows out of the DOM and positions them with
absolute/translateY:

- relay-reconnect: the reconnect-backfill test expected both the newest and the
  260-rows-old message mounted at once. A virtualized timeline windows the
  oldest rows out while the user sits at the bottom, so assert the newest at the
  bottom, then scroll to the top and poll until the oldest mounts -- the backfill
  depth is now proven by reachability, not simultaneous mounting.
- channels (x2): expectIntroBalancedAroundDayDivider compared the intro->divider
  gap against the divider->message gap for equality. The intro is a flex sibling
  above the timeline while the divider and first row are virtualized items, so
  the two gaps are measured across different layout regimes and no longer match
  within a pixel. Assert the intended reading order instead: intro, divider,
  then the first message, cleanly separated with no overlap.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
2026-06-18 20:04:03 -04:00
co-authored by Will Pfleger
parent fe489d81a8
commit 49db96c0d8
2 changed files with 37 additions and 8 deletions
+9 -1
View File
@@ -268,7 +268,15 @@ async function expectIntroBalancedAroundDayDivider(
const gapAboveDivider = dividerBox.y - (introBox.y + introBox.height);
const gapBelowDivider = messageBox.y - (dividerBox.y + dividerBox.height);
expect(Math.abs(gapAboveDivider - gapBelowDivider)).toBeLessThanOrEqual(1);
// The intro is a flex sibling above the timeline, while the day divider and
// the first message-row are virtualized items positioned by translateY inside
// the scroll container. The intro -> divider gap now spans those two layout
// regimes (it includes the wrapper flex gap), so it no longer matches the
// divider -> message gap within a pixel. Assert the intended layout instead:
// intro, divider, then the first message in reading order, each cleanly
// separated with no overlap.
expect(gapAboveDivider).toBeGreaterThanOrEqual(0);
expect(gapBelowDivider).toBeGreaterThanOrEqual(0);
}
async function expectIntroActionCardLayout(
+28 -7
View File
@@ -175,11 +175,32 @@ test("reconnect backfills more missed channel messages than the live subscriptio
}));
await emitMockMessages(page, missedMessages);
await expect(page.getByTestId("message-timeline")).toContainText(
"reconnect e2e missed 001",
{ timeout: 15_000 },
);
await expect(page.getByTestId("message-timeline")).toContainText(
"reconnect e2e missed 260",
);
// The newest backfilled message renders at the bottom once the reconnect
// catch-up settles.
const timeline = page.getByTestId("message-timeline");
await expect(timeline).toContainText("reconnect e2e missed 260", {
timeout: 15_000,
});
// The virtualized timeline windows the oldest backfilled rows out of the DOM
// while the user sits at the bottom, so the backfill depth can't be asserted
// by expecting all 260 rows to be mounted at once. Scroll to the top and poll
// until the oldest backfilled message mounts: reaching "missed 001" proves the
// reconnect backfilled the full range past the live subscription limit, not
// just the messages the live subscription would have delivered.
await expect
.poll(
async () => {
await timeline.evaluate((element) => {
const scrollable = element as HTMLDivElement;
scrollable.scrollTop = 0;
scrollable.dispatchEvent(new Event("scroll", { bubbles: true }));
});
return timeline.evaluate((element) =>
(element.textContent ?? "").includes("reconnect e2e missed 001"),
);
},
{ timeout: 15_000 },
)
.toBe(true);
});