Files
paseo/packages/app/e2e/agent-bottom-anchor.spec.ts
T

184 lines
6.0 KiB
TypeScript

import { test, expect, type Page } from "./fixtures";
import { createTempGitRepo } from "./helpers/workspace";
import {
connectDaemonClient,
createReplyTurn,
expectDetachedFromBottom,
expectNearBottom,
getChatContainerKey,
readScrollMetrics,
scrollUpFromBottom,
seedBottomAnchorAgent,
waitForAgentReady,
waitForContentGrowth,
} from "./helpers/agent-bottom-anchor";
test.describe.configure({ timeout: 180000 });
async function openWorkspaceAgentTab(page: Page, agentId: string) {
const tab = page.getByTestId(`workspace-tab-agent_${agentId}`).first();
await expect(tab).toBeVisible({ timeout: 30000 });
await tab.click();
}
test("direct load and refresh land at the bottom for history-backed chats", async ({
page,
}) => {
const repo = await createTempGitRepo("paseo-e2e-bottom-anchor-direct-");
const client = await connectDaemonClient();
try {
const agent = await seedBottomAnchorAgent({
client,
cwd: repo.path,
title: `bottom-anchor-direct-${Date.now()}`,
turnCount: 4,
});
await page.goto(agent.url, { waitUntil: "domcontentloaded" });
await openWorkspaceAgentTab(page, agent.id);
await waitForAgentReady(page, agent.expectedTailText);
await expectNearBottom(page);
await page.reload({ waitUntil: "commit" });
await openWorkspaceAgentTab(page, agent.id);
await waitForAgentReady(page, agent.expectedTailText);
await expectNearBottom(page);
} finally {
await client.close().catch(() => undefined);
await repo.cleanup();
}
});
test("revisiting a loaded chat restores bottom anchoring", async ({
page,
}) => {
const repo = await createTempGitRepo("paseo-e2e-bottom-anchor-switch-");
const client = await connectDaemonClient();
try {
const agent = await seedBottomAnchorAgent({
client,
cwd: repo.path,
title: `bottom-anchor-switch-${Date.now()}`,
turnCount: 4,
});
await page.goto(agent.url, { waitUntil: "domcontentloaded" });
await openWorkspaceAgentTab(page, agent.id);
await waitForAgentReady(page, agent.expectedTailText);
await expectNearBottom(page);
await page.getByTestId("sidebar-new-agent").first().click();
await expect(page.getByRole("textbox", { name: "Message agent..." }).first()).toBeVisible({
timeout: 30000,
});
await openWorkspaceAgentTab(page, agent.id);
await waitForAgentReady(page, agent.expectedTailText);
await expectNearBottom(page);
} finally {
await client.close().catch(() => undefined);
await repo.cleanup();
}
});
test("sticky mode stays pinned through composer growth and viewport resize, but detached mode does not fight streamed updates", async ({
page,
}) => {
const repo = await createTempGitRepo("paseo-e2e-bottom-anchor-sticky-");
const client = await connectDaemonClient();
try {
const agent = await seedBottomAnchorAgent({
client,
cwd: repo.path,
title: `bottom-anchor-sticky-${Date.now()}`,
turnCount: 10,
});
await page.setViewportSize({ width: 1320, height: 920 });
await page.goto(agent.url, { waitUntil: "domcontentloaded" });
await openWorkspaceAgentTab(page, agent.id);
await waitForAgentReady(page, agent.expectedTailText);
await expectNearBottom(page);
const composer = page.getByRole("textbox", { name: "Message agent..." }).first();
await composer.click();
for (let index = 0; index < 6; index += 1) {
await composer.pressSequentially(`composer growth line ${index + 1}`);
if (index < 5) {
await page.keyboard.press("Shift+Enter");
}
}
await expectNearBottom(page);
await expect(page.getByTestId("scroll-to-bottom-button")).toHaveCount(0);
await page.setViewportSize({ width: 820, height: 760 });
await expectNearBottom(page);
await scrollUpFromBottom(page, 720);
await expectDetachedFromBottom(page);
const beforeExternalUpdate = await readScrollMetrics(page);
const externalTurn = createReplyTurn(`external-stream-${Date.now()}`);
await client.sendAgentMessage(agent.id, externalTurn.message);
await waitForContentGrowth(page, beforeExternalUpdate.contentHeight);
const finish = await client.waitForFinish(agent.id, 120000);
expect(finish.status).toBe("idle");
await expectDetachedFromBottom(page);
} finally {
await client.close().catch(() => undefined);
await repo.cleanup();
}
});
test("web partial virtualization keeps bottom anchoring stable across direct load, refresh, and resize", async ({
page,
}) => {
await page.addInitScript(() => {
(window as typeof window & {
__PASEO_E2E_WEB_PARTIAL_VIRTUALIZATION_THRESHOLD?: number;
__PASEO_E2E_WEB_MOUNTED_RECENT_STREAM_ITEMS?: number;
}).__PASEO_E2E_WEB_PARTIAL_VIRTUALIZATION_THRESHOLD = 6;
(window as typeof window & {
__PASEO_E2E_WEB_PARTIAL_VIRTUALIZATION_THRESHOLD?: number;
__PASEO_E2E_WEB_MOUNTED_RECENT_STREAM_ITEMS?: number;
}).__PASEO_E2E_WEB_MOUNTED_RECENT_STREAM_ITEMS = 4;
});
const repo = await createTempGitRepo("paseo-e2e-bottom-anchor-virtualized-");
const client = await connectDaemonClient();
try {
const agent = await seedBottomAnchorAgent({
client,
cwd: repo.path,
title: `bottom-anchor-virtualized-${Date.now()}`,
turnCount: 4,
});
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 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 expectNearBottom(page);
} finally {
await client.close().catch(() => undefined);
await repo.cleanup();
}
});