Skip to content

Harden ai-assistant scrolling tests against a scroll-settle race#5401

Open
habdelra wants to merge 1 commit into
mainfrom
worktree-fix-scrolling-stays-at-bottom-flake
Open

Harden ai-assistant scrolling tests against a scroll-settle race#5401
habdelra wants to merge 1 commit into
mainfrom
worktree-fix-scrolling-stays-at-bottom-flake

Conversation

@habdelra

@habdelra habdelra commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

The Integration | ai-assistant-panel | scrolling suite intermittently fails on scrolling stays at the bottom if a message is streaming in with AI assistant is scrolled to bottom expected true, got false (failing CI job).

Root cause

The "open a room" assertions read the conversation's scroll position synchronously, immediately after the room renders. The panel auto-scrolls to the newest message when the last message registers its scroller, and re-scrolls whenever that message's subtree mutates (see the per-message MutationObserver in ai-assistant/message). Avatars, card pills, and markdown that finish rendering after the test runloop has otherwise settled shift the layout and briefly move the scroll position before the component re-scrolls to correct it. A single synchronous read races that correction and occasionally samples a position past the bottom threshold. The failing run shows this directly — the avatar set-background-image helper module 404s and retries at +100ms, i.e. late layout work landing after settled().

Only the first assertion in the test can fail this way: simulateRemoteMessage delivers events via setTimeout(0) and Glimmer batches renders, so the two assertions that follow a simulateRemoteMessage with no await read stale DOM (still at the bottom) and pass trivially.

Fix

Replace the point-in-time scroll reads in the "open a room" assertions with a waitUntil poll that tolerates the late re-scroll. The predicate is unchanged, so an already-settled panel resolves immediately; only the racing case now waits for the component's own correction to land. On timeout the helper reports the exact geometry (scrollHeight / clientHeight / scrollTop / distanceFromBottom / bottomThreshold) so a future failure is diagnosable instead of a bare expected true.

The same race exists in the sibling "open a room" checks (scroll-to-last, scroll-to-first-unread, and the unread-indicator click, which previously used a fixed 2s sleep), so they move to the same helper. The two checks that verify enqueuing a streaming event doesn't synchronously jank the scroll stay synchronous by design.

Verification

Test-only change; eslint and ember-tsc are clean for the file. The underlying flake is timing- and CI-load-dependent and doesn't reproduce reliably locally, so the diagnostics are included as insurance: if the poll ever times out, the next failure will print the exact scroll geometry.

The "open a room" scroll assertions in the ai-assistant scrolling suite
read the conversation's scroll position synchronously immediately after
the room renders. The panel auto-scrolls to the newest message when the
last message registers its scroller, and re-scrolls whenever that
message's subtree mutates — so avatars, card pills, and markdown that
finish rendering after the test runloop has otherwise settled shift the
layout and briefly move the scroll position before the component corrects
it. A single synchronous read races that correction and intermittently
observes distanceFromBottom past the bottom threshold.

Replace those point-in-time reads with a waitUntil poll that tolerates
the late re-scroll, and report the exact scroll geometry (scrollHeight /
clientHeight / scrollTop / distanceFromBottom) on timeout so a future
failure is diagnosable instead of a bare "expected true". The two checks
that verify enqueuing a streaming event does not synchronously jank the
scroll stay synchronous by design.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files      1 suites   2h 36m 2s ⏱️
3 376 tests 3 361 ✅ 15 💤 0 ❌
3 395 runs  3 380 ✅ 15 💤 0 ❌

Results for commit 70e06a6.

Realm Server Test Results

    1 files      1 suites   9m 7s ⏱️
1 674 tests 1 674 ✅ 0 💤 0 ❌
1 753 runs  1 753 ✅ 0 💤 0 ❌

Results for commit 70e06a6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant