From d37d752310b4313da7d568251c483fbecd81c412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Sun, 10 May 2026 22:53:23 +0800 Subject: [PATCH] Stabilize chat scroll tests for release gate The release push gate exposed that the MessageList scroll tests fired synthetic user scrolls before the component's programmatic auto-scroll guard had reset. Waiting for the requestAnimationFrame reset keeps the tests aligned with the runtime behavior where users cannot scroll in the same frame as mount auto-scroll. Constraint: Pre-push quality gate must pass before publishing the release tag Rejected: Bypass pre-push verification | would publish without the repo's required PR quality evidence Confidence: high Scope-risk: narrow Tested: cd desktop && bun run test -- src/components/chat/MessageList.test.tsx --run Tested: bun run check:desktop Tested: bun run check:coverage Not-tested: Full bun run quality:pr after this commit; it will run again during push --- desktop/src/components/chat/MessageList.test.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/desktop/src/components/chat/MessageList.test.tsx b/desktop/src/components/chat/MessageList.test.tsx index 0b1ee1a0..36a1611b 100644 --- a/desktop/src/components/chat/MessageList.test.tsx +++ b/desktop/src/components/chat/MessageList.test.tsx @@ -11,6 +11,14 @@ import type { PerSessionState } from '../../stores/chatStore' const ACTIVE_TAB = 'active-tab' +async function waitForProgrammaticScrollReset() { + await act(async () => { + await new Promise((resolve) => { + requestAnimationFrame(() => resolve()) + }) + }) +} + function makeSessionState(overrides: Partial = {}): PerSessionState { return { messages: [], @@ -459,6 +467,7 @@ describe('MessageList nested tool calls', () => { }) scrollIntoView.mockClear() + await waitForProgrammaticScrollReset() fireEvent.scroll(scroller) act(() => { @@ -517,6 +526,7 @@ describe('MessageList nested tool calls', () => { }) scrollIntoView.mockClear() + await waitForProgrammaticScrollReset() fireEvent.scroll(scroller) act(() => { @@ -581,6 +591,7 @@ describe('MessageList nested tool calls', () => { }, }) + await waitForProgrammaticScrollReset() fireEvent.scroll(scroller) expect(screen.getByRole('button', { name: 'Latest' })).toBeTruthy() @@ -592,6 +603,7 @@ describe('MessageList nested tool calls', () => { }) scrollTop = 760 + await waitForProgrammaticScrollReset() fireEvent.scroll(scroller) act(() => { @@ -697,6 +709,7 @@ describe('MessageList nested tool calls', () => { }) scrollIntoView.mockClear() + await waitForProgrammaticScrollReset() fireEvent.scroll(scroller) fireEvent.click(screen.getByRole('button', { name: 'Latest' }))