From 5d1a2c10f8a493e2329f3f9bee51dfb94ccd28af Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 4 Nov 2026 22:35:23 +0700 Subject: [PATCH] test(app): wheel the thinking group into view before opening it (#8791) The "reasoning follows Expand thinking (false)" spec clicked a tool-call group above the viewport. Playwright's scroll-into-view is programmatic, and the web timeline only leaves sticky-bottom on user input (wheel, key, touch, scrollbar), so it snapped back to the bottom while the click was in flight and the group never opened. Wheel the group into view first, reusing the helper the timeline pagination specs already use for prompts. --- .../app/e2e/browser/turn-collapse.spec.ts | 2 ++ .../support/helpers/timeline-pagination.ts | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/app/e2e/browser/turn-collapse.spec.ts b/packages/app/e2e/browser/turn-collapse.spec.ts index d990e33009..cd899d1ee8 100644 --- a/packages/app/e2e/browser/turn-collapse.spec.ts +++ b/packages/app/e2e/browser/turn-collapse.spec.ts @@ -2,6 +2,7 @@ import type { DaemonClient } from "@getpaseo/client/internal/daemon-client"; import path from "node:path"; import { holdOlderHistoryPages, + scrollTimelineRowIntoView, userScrollsTimelineToHistoryStart, } from "../support/helpers/timeline-pagination"; import { getActiveTabTestId } from "../support/helpers/launcher"; @@ -529,6 +530,7 @@ async function expectThinkingPresentation(page: Page, autoExpandReasoning: boole .last(); await expect(group).toContainText(/read 1 file/i); await expect(group.getByRole("button").first()).not.toContainText(/thought/i); + await scrollTimelineRowIntoView(page, group); await group.getByRole("button").first().click(); // Expand thinking is off: the group opens its rows, but the thought row stays closed // until the reader opens it. diff --git a/packages/app/e2e/support/helpers/timeline-pagination.ts b/packages/app/e2e/support/helpers/timeline-pagination.ts index e575f4f0d9..f54fd86812 100644 --- a/packages/app/e2e/support/helpers/timeline-pagination.ts +++ b/packages/app/e2e/support/helpers/timeline-pagination.ts @@ -477,18 +477,29 @@ export async function expectStableHistoryStartGutter(page: Page): Promise .toBe(32); } -export async function scrollTimelinePromptIntoView(page: Page, prompt: string): Promise { +/** + * Wheels the timeline until `row` is centered, as a reader does. Playwright's own + * scroll-into-view is programmatic, which the timeline does not treat as the reader leaving + * the bottom: it snaps back to the bottom and can move the row away mid-click. + */ +export async function scrollTimelineRowIntoView(page: Page, row: Locator): Promise { const timeline = page.locator('[data-testid="agent-chat-scroll"]:visible').first(); - const row = timeline.getByTestId("user-message").filter({ hasText: prompt }); const [viewport, target] = await Promise.all([timeline.boundingBox(), row.boundingBox()]); if (!viewport || !target) { - throw new Error(`Expected a rendered timeline prompt for ${prompt}`); + throw new Error("Expected a rendered timeline row"); } - // Programmatic scrolling does not express the user's intent to stop following output. await timeline.hover(); await page.mouse.wheel(0, target.y - viewport.y - (viewport.height - target.height) / 2); await expect(row).toBeInViewport({ ratio: 1 }); await waitForTimelineGeometryToSettle(page); +} + +export async function scrollTimelinePromptIntoView(page: Page, prompt: string): Promise { + const timeline = page.locator('[data-testid="agent-chat-scroll"]:visible').first(); + await scrollTimelineRowIntoView( + page, + timeline.getByTestId("user-message").filter({ hasText: prompt }), + ); await expect .poll(async () => (await readTimelineViewport(page)).scrollTop) .toBeGreaterThan(HISTORY_START_THRESHOLD_PX);