78 lines
4.3 KiB
JavaScript
78 lines
4.3 KiB
JavaScript
// @ts-check
|
|
// Conversation Playground Metrics view (all /api routes mocked; no live writes).
|
|
const { test, expect } = require('./_guard');
|
|
|
|
const sse = (/** @type {any[]} */ evts) => evts.map(e => `data: ${JSON.stringify(e)}\n\n`).join('');
|
|
|
|
async function boot(page, { usage = true, lang = '' } = {}) {
|
|
let n = 0;
|
|
await page.route('**/api/**', route => route.request().method() === 'GET' ? route.fulfill({ json: {} }) : route.fulfill({ json: { ok: true } }));
|
|
await page.route('**/api/conversation/turn', route => {
|
|
n++;
|
|
const stats = { type: 'stats', stt_ms: 100 + n, llm_ttft_ms: 200 + n * 10, llm_total_ms: 900, tts_ms: 300 + n, total_ms: 1500,
|
|
usage: usage ? { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 } : null };
|
|
return route.fulfill({ contentType: 'text/event-stream', body: sse([
|
|
{ type: 'transcript', text: 'hi', stt_ms: null }, { type: 'token', delta: 'Hello.' },
|
|
{ type: 'llm_done', text: 'Hello.', llm_ttft_ms: 1, llm_total_ms: 2 }, stats, { type: 'done' }]) });
|
|
});
|
|
await page.route('**/static/dist/main.min.js*', r => r.abort());
|
|
if (lang) await page.addInitScript(l => { try { localStorage.setItem('app-lang', l); } catch (e) {} }, lang);
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });
|
|
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
|
|
await page.evaluate(() => window.navTo('s-conversation'));
|
|
}
|
|
|
|
async function turn(page, i) {
|
|
await page.locator('#conv-text-input').fill('hello ' + i);
|
|
await page.locator('#conv-text-input').press('Enter');
|
|
await expect(page.locator('#conv-turn-history .conv-hist-item')).toHaveCount(i);
|
|
await expect(page.locator('#conv-send-btn')).toBeEnabled();
|
|
}
|
|
|
|
test('3 turns -> 3 charts with 3 points, token sums, latency panel intact', async ({ page }) => {
|
|
await boot(page);
|
|
for (let i = 1; i <= 3; i++) await turn(page, i);
|
|
// existing Latency panel unchanged
|
|
for (const id of ['cpv-stt', 'cpv-ttft', 'cpv-llm', 'cpv-tts', 'cpv-total']) await expect(page.locator('#' + id)).not.toHaveText('—');
|
|
await expect(page.locator('#conv-stats-panel .conv-stats-head', { hasText: 'Turn history' })).toBeVisible();
|
|
|
|
await expect(page.getByRole('tab')).toHaveCount(0);
|
|
await expect(page.locator('#conv-stats-panel .conv-chart')).toHaveCount(3);
|
|
for (const k of ['stt', 'llm', 'tts']) {
|
|
await expect(page.locator(`.conv-chart[data-metric="${k}"] circle`)).toHaveCount(3);
|
|
await expect(page.locator(`.conv-chart[data-metric="${k}"] tbody tr`)).toHaveCount(3);
|
|
}
|
|
await expect(page.locator('.conv-chart[data-metric="llm"] svg')).toHaveAttribute('aria-label', 'LLM first token, last 230 ms, 3 points');
|
|
await expect(page.locator('.conv-chart[data-metric="llm"] svg')).toContainText('Time (ms)');
|
|
await expect(page.locator('.conv-chart[data-metric="llm"] svg')).toContainText('TTFB (LLM)');
|
|
const d = page.locator('.conv-chart[data-metric="stt"] details');
|
|
await expect(d).not.toHaveAttribute('open', '');
|
|
await d.locator('summary').click();
|
|
await expect(d.locator('table')).toBeVisible();
|
|
await expect(page.locator('#cm-tok-prompt')).toHaveText('30');
|
|
await expect(page.locator('#cm-tok-completion')).toHaveText('15');
|
|
await expect(page.locator('#cm-tok-total')).toHaveText('45');
|
|
expect(await page.locator('.conv-chart[data-metric="stt"] tbody td').first().textContent()).toMatch(/^\d{2}:\d{2}:\d{2}$/);
|
|
|
|
// Clear resets
|
|
await page.locator('#conv-clear-btn').click();
|
|
await expect(page.locator('.conv-chart')).toHaveCount(0);
|
|
await expect(page.locator('#cm-empty')).toBeVisible();
|
|
await expect(page.locator('#cm-tok-total')).toHaveText('n/a');
|
|
});
|
|
|
|
test('no usage from the server shows n/a, never 0', async ({ page }) => {
|
|
await boot(page, { usage: false });
|
|
await turn(page, 1);
|
|
for (const id of ['prompt', 'completion', 'total']) await expect(page.locator('#cm-tok-' + id)).toHaveText('n/a');
|
|
await expect(page.locator('.conv-chart[data-metric="llm"] circle')).toHaveCount(1);
|
|
});
|
|
|
|
test('empty state and German labels', async ({ page }) => {
|
|
await boot(page, { lang: 'de' });
|
|
await expect(page.locator('#cm-empty')).toBeVisible();
|
|
await expect(page.locator('#cm-tok-total')).toHaveText('k. A.');
|
|
await expect(page.locator('#conv-stats-panel .conv-stats-head', { hasText: 'Token-Verbrauch' })).toBeVisible();
|
|
});
|