import assert from 'node:assert/strict'; import { chromium } from 'playwright'; // Run against Vite with: node tools/playwright/frontend-resources.test.mjs [url] const browser = await chromium.launch({ headless: true }); try { const page = await browser.newPage({ ignoreHTTPSErrors: true }); const errors = []; page.on('pageerror', error => errors.push(error.message)); const activity = { state: 'idle', running: false, queued: false, active_task_ids: [], observed_at_ms: 0 }; const agent = { id: 'test', name: 'Resource test', activity, running: false }; const transcript = Array.from({ length: 2000 }, (_, i) => ({ role: i % 2 ? 'assistant' : 'user', content: `Message ${i}\n\n**Markdown** content`, at: new Date(1700000000000 + i * 60000).toISOString() })); transcript[1999].content = 'Large message ' + 'x'.repeat(25000); let requests = 0; let concurrent = 0; let peak = 0; await page.addInitScript(() => { window.sources = []; window.EventSource = class { constructor() { window.sources.push(this); setTimeout(() => this.onopen?.(), 0); } close() { this.closed = true; } }; }); await page.route('**/api/**', async route => { const path = new URL(route.request().url()).pathname; let body = {}; if (path === '/api/agents') body = [agent]; if (path === '/api/agents/test/activity') body = activity; if (path === '/api/agents/test/computer') body = { ready: true, viewer_url: '/fake-desktop' }; if (path === '/api/agents/test') { requests++; peak = Math.max(peak, ++concurrent); await new Promise(resolve => setTimeout(resolve, 120)); concurrent--; body = { ...agent, transcript }; } await route.fulfill({ json: body }); }); await page.route('**/fake-desktop*', route => route.fulfill({ body: 'Desktop', contentType: 'text/html' })); await page.goto(process.argv[2] || 'https://127.0.0.1:5174'); await page.waitForSelector('.message-block'); assert.equal(await page.locator('.message-block').count(), 40); assert.equal(await page.locator('.message-body pre').count(), 1, 'Large messages skip Markdown parsing'); await page.locator('.history-pages button').first().click(); assert.equal(await page.locator('.message-block').count(), 40); assert.match(await page.locator('.message-block').first().textContent(), /Message 1920/); await page.locator('.history-pages button').last().click(); await page.waitForSelector('iframe'); await page.locator('.meeting-stage-head button').click(); assert.equal(await page.locator('iframe').count(), 0, 'Collapsed desktop must unmount'); await page.waitForTimeout(300); requests = 0; peak = 0; await page.evaluate(() => { const source = window.sources.filter(source => !source.closed).at(-1); source.onmessage({ data: JSON.stringify({ events: Array.from({ length: 100 }, (_, id) => ({ id, kind: 'reply', payload: {} })) }) }); }); await page.waitForTimeout(600); assert.equal(peak, 1, 'Reply bursts must not overlap transcript requests'); assert.equal(requests, 2, 'Reply burst should coalesce to one request and one trailing refresh'); await page.evaluate(() => { Object.defineProperty(document, 'hidden', { configurable: true, value: true }); document.dispatchEvent(new Event('visibilitychange')); }); await page.waitForTimeout(100); assert.equal(await page.evaluate(() => window.sources.every(source => source.closed)), true); assert.deepEqual(errors, []); console.log('PASS: 2,000 messages → 40 mounted; older/latest navigation; large text fallback; collapsed iframe removed; 100 events → 2 serial requests; background SSE closed; no runtime errors.'); } finally { await browser.close(); }