// @ts-check import { test, expect } from '@playwright/test'; import { loadTestUser } from '../utils/testUser.js'; test.describe('@p0 Chat drawer — Support stream', () => { test.setTimeout(20000); test('FAB opens Support → create/load thread → send prompt → assistant reply appears', async ({ page }) => { const user = loadTestUser(); // ---- Stub chat API before the app mounts (ensureSupportThread runs on mount) ---- let createdId = 'thread-e2e'; // list existing -> none await page.route('**/api/chat/threads', async (route) => { if (route.request().method() === 'GET') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ threads: [] }), }); return; } // create new if (route.request().method() === 'POST') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: createdId }), }); return; } await route.fallback(); }); // preload thread messages -> empty await page.route(`**/api/chat/threads/${createdId}`, async (route) => { if (route.request().method() === 'GET') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ messages: [] }), }); return; } await route.fallback(); }); // streaming endpoint -> return simple line chunks as text/event-stream await page.route('**/api/chat/threads/*/stream', async (route) => { const reply = 'E2E assistant reply.'; await route.fulfill({ status: 200, headers: { 'Content-Type': 'text/event-stream' }, body: `${reply}\n`, }); }); // ---- 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 via FAB (forces Support pane) ---- const fab = page.getByRole('button', { name: /^Open chat$/i }); await expect(fab).toBeVisible({ timeout: 5000 }); await fab.click(); // Drawer visible; Support tab selected const supportTab = page.getByRole('button', { name: /^Aptiva\s*Support$/i }); await expect(supportTab).toBeVisible({ timeout: 5000 }); // Input and Send present const input = page.getByPlaceholder('Ask me anything…'); const send = page.getByRole('button', { name: /^Send$/i }); await expect(input).toBeVisible(); await expect(send).toBeDisabled(); // empty prompt // Send a message -> should render user bubble + streamed assistant reply await input.fill('Hi from E2E'); await expect(send).toBeEnabled(); await send.click(); // User bubble await expect(page.getByText(/^Hi from E2E$/)).toBeVisible({ timeout: 5000 }); // Assistant reply (from our stream stub) await expect(page.getByText(/^E2E assistant reply\./)).toBeVisible({ timeout: 8000 }); }); });