From 49db96c0d8023b417a5e97abc7e47899c241c88e Mon Sep 17 00:00:00 2001 From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 Date: Thu, 18 Jun 2026 19:50:01 -0400 Subject: [PATCH] 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 Signed-off-by: Will Pfleger --- desktop/tests/e2e/channels.spec.ts | 10 ++++++- desktop/tests/e2e/relay-reconnect.spec.ts | 35 ++++++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/desktop/tests/e2e/channels.spec.ts b/desktop/tests/e2e/channels.spec.ts index 3437b0f18..64d36f547 100644 --- a/desktop/tests/e2e/channels.spec.ts +++ b/desktop/tests/e2e/channels.spec.ts @@ -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( diff --git a/desktop/tests/e2e/relay-reconnect.spec.ts b/desktop/tests/e2e/relay-reconnect.spec.ts index 890940f3a..beb050112 100644 --- a/desktop/tests/e2e/relay-reconnect.spec.ts +++ b/desktop/tests/e2e/relay-reconnect.spec.ts @@ -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); });