// @ts-check import { test, expect } from '@playwright/test'; import { loadTestUser } from '../utils/testUser.js'; test.describe('@p0 Chat drawer — stream chunking', () => { test.setTimeout(20000); test('assistant reply is assembled from multiple chunks/lines', async ({ page }) => { const user = loadTestUser(); // Stub threads list/create -> empty -> create {id} const threadId = 'thread-chunks'; await page.route('**/api/chat/threads', async route => { if (route.request().method() === 'GET') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ threads: [] }) }); } if (route.request().method() === 'POST') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: threadId }) }); } return route.fallback(); }); await page.route(`**/api/chat/threads/${threadId}`, async route => { if (route.request().method() === 'GET') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ messages: [] }) }); } return route.fallback(); }); // Stub stream with multiple line fragments (simulate chunked SSE) await page.route('**/api/chat/threads/*/stream', async route => { const body = [ 'This is line 1.', 'This is line 2.', 'Final line.', ].map(l => l + '\n').join(''); await route.fulfill({ status: 200, headers: { 'Content-Type': 'text/event-stream' }, body }); }); // Sign in await page.context().clearCookies(); await page.goto('/signin', { waitUntil: 'networkidle' }); await page.getByPlaceholder('Username', { exact: true }).fill(user.username); await page.getByPlaceholder('Password', { exact: true }).fill(user.password); await page.getByRole('button', { name: /^Sign In$/ }).click(); await page.waitForURL('**/signin-landing**', { timeout: 15000 }); // Open chat await page.getByRole('button', { name: /^Open chat$/i }).click(); // Send prompt const input = page.getByPlaceholder('Ask me anything…'); const send = page.getByRole('button', { name: /^Send$/i }); await input.fill('test chunked stream'); await send.click(); // Expect all lines to appear merged in the **same** assistant message const assistantBubble = page.locator('div.text-left.text-gray-800').last(); await expect(assistantBubble).toBeVisible({ timeout: 8000 }); await expect(assistantBubble).toContainText('This is line 1.'); await expect(assistantBubble).toContainText('This is line 2.'); await expect(assistantBubble).toContainText('Final line.'); }); });