import { holdAssistantStream } from "./agent-timeline-gate"; import { expect, type Page, type TestInfo } from "@playwright/test"; import { openAgentRoute, seedMockAgentWorkspace, type MockAgentWorkspace } from "./mock-agent"; interface StreamingMarkdownAgent extends MockAgentWorkspace { stream: Awaited>; } export async function withStreamingMarkdown( page: Page, testInfo: TestInfo, run: (agent: StreamingMarkdownAgent) => Promise, ): Promise { testInfo.setTimeout(120_000); const agent = await seedMockAgentWorkspace({ repoPrefix: "streaming-markdown-", title: "Streaming Markdown", featureValues: { mockStreamingAssistantResponse: "**Bold text stays bold** and [Paseo docs](https://example.com/documentation). Done.", mockStreamingAssistantIntervalMs: 400, }, }); const stream = await holdAssistantStream(page, agent.agentId); try { await openAgentRoute(page, agent); await stream.waitForInitialTimeline(); await run({ ...agent, stream }); } finally { stream.release(); await agent.cleanup(); } } export async function requestStreamingMarkdown(agent: StreamingMarkdownAgent): Promise { await agent.client.sendAgentMessage(agent.agentId, "Show the formatted streaming response."); // Exercise late assertions: the producer finishes before the browser consumes its frames. await agent.client.waitForFinish(agent.agentId, 30_000); // The mock splits "**Bold" into two frames. Whether they land in one store // commit or two is up to the browser's task scheduling, and word pacing only // releases a word once the whitespace after it has arrived, so stop after // that whitespace: every batching then reveals "Bold" and nothing past "text". await agent.stream.showThrough("**Bold text"); } export async function expectUnfinishedBold(page: Page): Promise { const message = page.getByTestId("assistant-message").last(); await expect(message.locator(".enrm-text")).toContainText("Bold"); await expect(message).not.toContainText("stays bold"); } export async function expectUnfinishedLink( page: Page, agent: StreamingMarkdownAgent, testInfo: TestInfo, ): Promise { const message = page.getByTestId("assistant-message").last(); await agent.stream.showThrough("**Bold text stays bold** and [Paseo docs"); await expect(message).toContainText("Paseo docs"); await expect(message.getByRole("link", { name: "Paseo docs" })).toHaveCount(0); await expect(message).not.toContainText("https:"); await captureMarkdown(page, testInfo, "unfinished-link"); } export async function expectFinishedMarkdown( page: Page, agent: StreamingMarkdownAgent, testInfo: TestInfo, ): Promise { agent.stream.release(); await agent.client.waitForFinish(agent.agentId, 30_000); await expectCompletedMarkdown(page); await captureMarkdown(page, testInfo, "completed-markdown"); } export async function expectReloadedMarkdown(page: Page): Promise { await page.reload({ waitUntil: "domcontentloaded" }); await expectCompletedMarkdown(page); } async function captureMarkdown(page: Page, testInfo: TestInfo, name: string): Promise { await testInfo.attach(name, { body: await page.screenshot({ path: testInfo.outputPath(`${name}.png`) }), contentType: "image/png", }); } async function expectCompletedMarkdown(page: Page): Promise { const message = page.getByTestId("assistant-message").last(); await expect(message).toHaveText("Bold text stays bold and Paseo docs. Done."); await expect( message.getByRole("link", { name: "Paseo docs" }).and(message.locator("a")), ).toHaveAttribute("href", "https://example.com/documentation"); await expect(message.locator("strong")).toHaveCSS("font-weight", "700"); }