184 lines
6.0 KiB
TypeScript
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();
|
|
}
|
|
});
|