// @ts-check // Conversation Playground LLM endpoint picker (all /api routes mocked). const { test, expect } = require('./_guard'); const CONTAINERS = { containers: [ { name: 'ollama', label: 'Ollama', role: 'llm', port: 11434, status: 'running' }, { name: 'vllm', label: 'vLLM', role: 'llm', port: 8000, status: 'exited' }, { name: 'ghost', label: 'Ghost', role: 'llm', port: 1, status: 'not_found' }, { name: 'kokoro-tts', label: 'Kokoro', role: 'tts', port: 8880, status: 'running' }, ] }; async function boot(page, settings, containers = CONTAINERS) { /** @type {any[]} */ const patches = []; /** @type {string[]} */ const modelReqs = []; await page.route('**/api/**', route => route.request().method() === 'GET' ? route.fulfill({ json: {} }) : route.fulfill({ json: { ok: true } })); await page.route('**/api/local-containers*', route => route.fulfill({ json: containers })); await page.route('**/api/conversation/llm-models*', route => { modelReqs.push(route.request().url()); return route.fulfill({ json: { models: ['m1'] } }); }); await page.route('**/api/settings', route => { if (route.request().method() === 'GET') return route.fulfill({ json: settings }); patches.push(route.request().postDataJSON()); return route.fulfill({ json: { ok: true } }); }); await page.route('**/static/dist/main.min.js*', r => r.abort()); 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')); return { patches, modelReqs }; } test('lists configured endpoints, picking one saves it and fetches models', async ({ page }) => { const { patches, modelReqs } = await boot(page, {}); const pick = page.locator('#conv-llm-endpoint'); await expect(pick.locator('option')).toHaveCount(3); await expect(pick.locator('option').nth(0)).toContainText('Ollama'); await expect(pick.locator('option').nth(0)).toContainText('http://host.docker.internal:11434/v1'); await expect(pick.locator('option').nth(1)).toContainText('✗'); const box = await pick.boundingBox(); expect(box && box.height).toBeGreaterThanOrEqual(44); await pick.selectOption({ index: 1 }); await expect(page.locator('#conv-llm-url')).toBeHidden(); await expect.poll(() => patches.some(p => p && p.conv_llm_url === 'http://host.docker.internal:8000/v1')).toBe(true); await expect.poll(() => modelReqs.some(u => u.includes(encodeURIComponent('http://host.docker.internal:8000/v1')))).toBe(true); await expect(page.locator('#conv-llm-model-select')).toContainText('m1'); }); test('saved matching URL preselects its entry and hides the input', async ({ page }) => { await boot(page, { conv_llm_url: 'http://host.docker.internal:11434/v1' }); await expect(page.locator('#conv-llm-endpoint')).toHaveValue('http://host.docker.internal:11434/v1'); await expect(page.locator('#conv-llm-url')).toBeHidden(); }); test('saved custom URL preselects Custom', async ({ page }) => { await boot(page, { conv_llm_url: 'http://192.168.178.8:17861/v1' }); await expect(page.locator('#conv-llm-endpoint')).toHaveValue('__custom__'); await expect(page.locator('#conv-llm-url')).toBeVisible(); await expect(page.locator('#conv-llm-url')).toHaveValue('http://192.168.178.8:17861/v1'); }); test('Custom… reveals the input right after the select and focuses it', async ({ page }) => { await boot(page, { conv_llm_url: 'http://host.docker.internal:11434/v1' }); await page.locator('#conv-llm-endpoint').selectOption('__custom__'); const inp = page.getByRole('textbox', { name: 'Custom LLM base URL' }); await expect(inp).toBeFocused(); expect(await page.evaluate(() => document.getElementById('conv-llm-endpoint').nextElementSibling.id)).toBe('conv-llm-url'); }); test('empty list shows only Custom… and the hint linking to Engines', async ({ page }) => { await boot(page, {}, { containers: [] }); await expect(page.locator('#conv-llm-endpoint option')).toHaveCount(1); await expect(page.locator('#conv-llm-url')).toBeVisible(); const hint = page.locator('#conv-llm-empty-hint a'); await expect(hint).toBeVisible(); await hint.click(); await expect(page.locator('#s-llms')).toBeVisible(); });