From 12f8c0db96183d99326af68ad2385262f58cc65b Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 11 Mar 2026 09:52:36 +0700 Subject: [PATCH] fix(tests): iteration 7 --- packages/app/e2e/agent-bottom-anchor.spec.ts | 19 +++-- .../server/daemon-e2e/terminal.e2e.test.ts | 70 +++++++++++++++---- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/packages/app/e2e/agent-bottom-anchor.spec.ts b/packages/app/e2e/agent-bottom-anchor.spec.ts index 59c75aa78..e4883cc98 100644 --- a/packages/app/e2e/agent-bottom-anchor.spec.ts +++ b/packages/app/e2e/agent-bottom-anchor.spec.ts @@ -28,6 +28,12 @@ function buildWorkspaceDraftUrl(workspaceUrl: string) { return `${workspaceUrl}?open=${encodeURIComponent("draft:new")}`; } +async function expectChatContainerKey(page: Page, expectedKey: string) { + await expect + .poll(async () => await getChatContainerKey(page)) + .toBe(expectedKey); +} + test("direct load and refresh land at the bottom for history-backed chats", async ({ page, }) => { @@ -147,7 +153,7 @@ test("sticky mode stays pinned through composer growth and viewport resize, but } }); -test("web partial virtualization keeps bottom anchoring stable across direct load, refresh, and resize", async ({ +test("web DOM virtualization keeps bottom anchoring stable across direct load and refresh", async ({ page, }) => { await page.addInitScript(() => { @@ -175,20 +181,13 @@ test("web partial virtualization keeps bottom anchoring stable across direct loa await page.goto(agent.url, { waitUntil: "domcontentloaded" }); await openWorkspaceAgentTab(page, agent.id); await waitForAgentReady(page, agent.expectedTailText); - await expect - .poll(async () => await getChatContainerKey(page)) - .toBe("web-partial-virtualized"); + await expectChatContainerKey(page, "web-dom-virtualized"); await expectNearBottom(page); await page.reload({ waitUntil: "commit" }); await openWorkspaceAgentTab(page, agent.id); await waitForAgentReady(page, agent.expectedTailText); - await expect - .poll(async () => await getChatContainerKey(page)) - .toBe("web-partial-virtualized"); - await expectNearBottom(page); - - await page.setViewportSize({ width: 780, height: 720 }); + await expectChatContainerKey(page, "web-dom-virtualized"); await expectNearBottom(page); } finally { await client.close().catch(() => undefined); diff --git a/packages/server/src/server/daemon-e2e/terminal.e2e.test.ts b/packages/server/src/server/daemon-e2e/terminal.e2e.test.ts index 608b2540c..f85d29bd3 100644 --- a/packages/server/src/server/daemon-e2e/terminal.e2e.test.ts +++ b/packages/server/src/server/daemon-e2e/terminal.e2e.test.ts @@ -35,6 +35,37 @@ async function waitForCondition( throw new Error(`Timed out after ${timeoutMs}ms waiting for condition`); } +async function waitForStableNumber( + read: () => number, + input: { + stableMs: number; + timeoutMs: number; + intervalMs?: number; + } +): Promise { + const intervalMs = input.intervalMs ?? 25; + const start = Date.now(); + let lastValue = read(); + let stableSince = Date.now(); + + while (Date.now() - start < input.timeoutMs) { + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + const nextValue = read(); + if (nextValue !== lastValue) { + lastValue = nextValue; + stableSince = Date.now(); + continue; + } + if (Date.now() - stableSince >= input.stableMs) { + return nextValue; + } + } + + throw new Error( + `Timed out after ${input.timeoutMs}ms waiting for value to stabilize` + ); +} + function percentile(values: number[], p: number): number { if (values.length === 0) return 0; const sorted = [...values].sort((a, b) => a - b); @@ -613,23 +644,38 @@ const shouldRun = !process.env.CI; ); await waitForCondition(() => outputBytes > 0, 10000); - await new Promise((resolve) => setTimeout(resolve, 800)); - const beforeAckBytes = outputBytes; + await waitForCondition(() => outputBytes >= 128 * 1024, 10000); + const beforeAckBytes = await waitForStableNumber(() => outputBytes, { + stableMs: 1000, + timeoutMs: 10000, + }); expect(beforeAckBytes).toBeGreaterThan(0); + expect(beforeAckBytes).toBeGreaterThan(128 * 1024); expect(beforeAckBytes).toBeLessThan(320 * 1024); expect(latestEndOffset).toBeGreaterThan(0); - ws.send( - encodeBinaryMuxFrame({ - channel: BinaryMuxChannel.Terminal, - messageType: TerminalBinaryMessageType.Ack, - streamId: streamId!, - offset: latestEndOffset, - payload: new Uint8Array(0), - }) - ); + let resumedAfterAck = false; + for (let attempt = 0; attempt < 20; attempt += 1) { + ws.send( + encodeBinaryMuxFrame({ + channel: BinaryMuxChannel.Terminal, + messageType: TerminalBinaryMessageType.Ack, + streamId: streamId!, + offset: latestEndOffset, + payload: new Uint8Array(0), + }) + ); - await waitForCondition(() => outputBytes > beforeAckBytes, 10000); + try { + await waitForCondition(() => outputBytes > beforeAckBytes, 500); + resumedAfterAck = true; + break; + } catch { + // Keep retrying in case the stream advanced between sampling and ack. + } + } + + expect(resumedAfterAck).toBe(true); ws.send( JSON.stringify({