fix(app): anchor restored timeline position
This commit is contained in:
parent
f7b287987b
commit
3783cda020
6 changed files with 140 additions and 21 deletions
|
|
@ -222,7 +222,10 @@ function turn(index: number): Message[] {
|
|||
return [user, assistantMessage(targetID, index, user.info.id, parts)]
|
||||
}
|
||||
|
||||
const targetMessages = Array.from({ length: 72 }, (_, index) => turn(index)).flat()
|
||||
export const timelineMessages = (count: number, start = 0) =>
|
||||
Array.from({ length: count }, (_, index) => turn(start + index)).flat()
|
||||
|
||||
const targetMessages = timelineMessages(72)
|
||||
const sourceMessages = Array.from({ length: 12 }, (_, index) => [
|
||||
userMessage(sourceID, index + 1000, 120),
|
||||
assistantMessage(sourceID, index + 1000, id("msg_user", index + 1000), [textPart(index + 1000, 0, 240)]),
|
||||
|
|
@ -301,6 +304,10 @@ export const fixture = {
|
|||
|
||||
export function pageMessages(sessionID: string, limit: number, before?: string) {
|
||||
const messages = fixture.messages[sessionID as keyof typeof fixture.messages] ?? []
|
||||
return pageMessageList(messages, limit, before)
|
||||
}
|
||||
|
||||
export function pageMessageList(messages: Message[], limit: number, before?: string) {
|
||||
const end = before
|
||||
? Math.max(
|
||||
0,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, test, type Page } from "@playwright/test"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { fixture, pageMessages } from "./session-timeline.fixture"
|
||||
import { fixture, pageMessageList, pageMessages, timelineMessages } from "./session-timeline.fixture"
|
||||
import { trackPageErrors, expectNoSmokeErrors } from "../utils/errors"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { APP_READY_TIMEOUT, expectAppVisible, expectSessionTitle } from "../utils/waits"
|
||||
|
|
@ -116,12 +116,14 @@ test.describe("smoke: session timeline", () => {
|
|||
})
|
||||
|
||||
test("restores the persisted timeline position after reload", async ({ page }) => {
|
||||
let messages = timelineMessages(140)
|
||||
await mockOpenCodeServer(page, {
|
||||
sessions: fixture.sessions,
|
||||
provider: fixture.provider,
|
||||
directory: fixture.directory,
|
||||
project: fixture.project,
|
||||
pageMessages,
|
||||
pageMessages: (sessionID, limit, before) =>
|
||||
sessionID === fixture.targetID ? pageMessageList(messages, limit, before) : pageMessages(sessionID, limit, before),
|
||||
})
|
||||
await configureSmokePage(page, fixture.directory)
|
||||
|
||||
|
|
@ -143,6 +145,9 @@ test.describe("smoke: session timeline", () => {
|
|||
(element) => element.scrollHeight - element.clientHeight - element.scrollTop,
|
||||
),
|
||||
).toBeGreaterThan(100)
|
||||
const anchor = await firstVisibleMessage(page)
|
||||
expect(anchor).toBeTruthy()
|
||||
messages = [...messages, ...timelineMessages(120, 140)]
|
||||
await page.reload()
|
||||
await waitForTimelineStable(page)
|
||||
await expect.poll(() => timelineScroller(page).evaluate((element) => element.scrollTop)).toBeGreaterThan(100)
|
||||
|
|
@ -153,6 +158,7 @@ test.describe("smoke: session timeline", () => {
|
|||
),
|
||||
)
|
||||
.toBeGreaterThan(100)
|
||||
await expect.poll(() => firstVisibleMessage(page)).toBe(anchor)
|
||||
})
|
||||
|
||||
test("paints cached session tabs at the latest message", async ({ page }) => {
|
||||
|
|
@ -597,6 +603,16 @@ function timelineScroller(page: Page) {
|
|||
return page.locator(".scroll-view__viewport", { has: page.locator("[data-timeline-row]") })
|
||||
}
|
||||
|
||||
function firstVisibleMessage(page: Page) {
|
||||
return timelineScroller(page).evaluate((element) => {
|
||||
const box = element.getBoundingClientRect()
|
||||
return [...element.querySelectorAll<HTMLElement>("[data-message-id]")]
|
||||
.map((message) => ({ id: message.dataset.messageId, rect: message.getBoundingClientRect() }))
|
||||
.filter((message) => message.rect.bottom > box.top && message.rect.top < box.bottom)
|
||||
.sort((a, b) => a.rect.top - b.rect.top)[0]?.id
|
||||
})
|
||||
}
|
||||
|
||||
async function pointAtTimeline(page: Page) {
|
||||
const box = await timelineScroller(page).boundingBox()
|
||||
if (!box) throw new Error("Timeline scroller is not visible")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue