diff --git a/packages/app/e2e/agent-stream-ui.spec.ts b/packages/app/e2e/agent-stream-ui.spec.ts index aeeb6cff73..45f9cb9df2 100644 --- a/packages/app/e2e/agent-stream-ui.spec.ts +++ b/packages/app/e2e/agent-stream-ui.spec.ts @@ -3,6 +3,7 @@ import { awaitAssistantMessage, expectAgentIdle, expectInlineWorkingIndicator, + expectRunningAgentChrome, expectTurnCopyButton, expectScrollFollowsNewContent, } from "./helpers/agent-stream"; @@ -18,11 +19,35 @@ import { delayCreatedAgentInitialTailResponse } from "./helpers/agent-timeline-g import { selectModel } from "./helpers/app"; import { clickNewChat } from "./helpers/launcher"; import { expectComposerVisible, startRunningMockAgent } from "./helpers/composer"; -import { openAgentRoute, seedMockAgentWorkspace } from "./helpers/mock-agent"; +import { + openAgentRoute, + seedMockAgentWorkspace, + seedRunningMockAgentWorkspace, +} from "./helpers/mock-agent"; const SCROLL_AWAY_MIN_SCROLLABLE_DISTANCE = 360; test.describe("Agent stream UI", () => { + test("keeps running agent chrome after page refresh", async ({ page }) => { + const title = "Running agent refresh"; + const agent = await seedRunningMockAgentWorkspace({ + repoPrefix: "stream-running-refresh-", + title, + model: "five-minute-stream", + initialPrompt: "Stay running while the page refreshes.", + }); + try { + await openAgentRoute(page, agent); + await expectRunningAgentChrome(page, title); + + await page.reload(); + + await expectRunningAgentChrome(page, title); + } finally { + await agent.cleanup(); + } + }); + test("auto-scroll sticks to bottom across token bursts", async ({ page }) => { test.setTimeout(120_000); const agent = await startRunningMockAgent(page, { diff --git a/packages/app/e2e/helpers/agent-stream.ts b/packages/app/e2e/helpers/agent-stream.ts index a6086513f6..3fc23212b0 100644 --- a/packages/app/e2e/helpers/agent-stream.ts +++ b/packages/app/e2e/helpers/agent-stream.ts @@ -22,6 +22,19 @@ export async function expectInlineWorkingIndicator(page: Page): Promise { await expect(page.getByTestId("turn-working-indicator")).toBeVisible({ timeout: 30_000 }); } +export async function expectRunningAgentChrome(page: Page, title: string): Promise { + const tab = page.getByRole("button", { name: title, exact: true }); + + await expect(tab).toBeVisible({ timeout: 30_000 }); + await expect(tab.getByRole("progressbar", { name: "Agent running" })).toBeVisible({ + timeout: 30_000, + }); + await expect(page.getByRole("button", { name: /stop agent|canceling agent/i })).toBeVisible({ + timeout: 30_000, + }); + await expectInlineWorkingIndicator(page); +} + export async function expectTurnCopyButton(page: Page): Promise { await expect(page.getByRole("button", { name: "Copy turn" }).first()).toBeVisible({ timeout: 30_000, diff --git a/packages/app/e2e/helpers/mock-agent.ts b/packages/app/e2e/helpers/mock-agent.ts index 6878911363..82dad5169a 100644 --- a/packages/app/e2e/helpers/mock-agent.ts +++ b/packages/app/e2e/helpers/mock-agent.ts @@ -53,6 +53,23 @@ export async function seedMockAgentWorkspace( } } +export async function seedRunningMockAgentWorkspace( + options: MockAgentOptions, +): Promise { + const agent = await seedMockAgentWorkspace(options); + try { + await agent.client.waitForAgentUpsert( + agent.agentId, + (snapshot) => snapshot.status === "running", + 15_000, + ); + return agent; + } catch (error) { + await agent.cleanup(); + throw error; + } +} + export function buildAgentRoute(workspaceId: string, agentId: string): string { return buildHostAgentDetailRoute(getServerId(), agentId, workspaceId); } diff --git a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx index dea7fe017b..44dfc3fd62 100644 --- a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx +++ b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx @@ -162,7 +162,11 @@ export function WorkspaceTabIcon({ if (shouldShowLoader) { return ( - + );