import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron'; const alphaModelRef = 'custom-alpha123/model-alpha'; const betaModelRef = 'custom-beta5678/provider/model-beta'; test.describe('ClawX chat model picker', () => { test('switches the current agent model without requesting a gateway refresh', async ({ launchElectronApp }) => { const app = await launchElectronApp({ skipSetup: true }); try { await app.evaluate(async ({ app: _app }, refs) => { const { ipcMain } = process.mainModule!.require('electron') as typeof import('electron'); let currentModelRef = refs.alphaModelRef; const hostRequests: Array<{ path: string; method: string; body: unknown }> = []; const now = new Date().toISOString(); const makeResponse = (json: unknown, status = 200) => ({ ok: true, data: { status, ok: status >= 200 && status < 300, json, }, }); const agentsSnapshot = () => ({ success: true, agents: [{ id: 'main', name: 'Main', isDefault: true, modelDisplay: currentModelRef.split('/').slice(1).join('/'), modelRef: currentModelRef, overrideModelRef: currentModelRef, inheritedModel: false, workspace: '~/.openclaw/workspace', agentDir: '~/.openclaw/agents/main/agent', mainSessionKey: 'agent:main:main', channelTypes: [], }], defaultAgentId: 'main', defaultModelRef: refs.alphaModelRef, configuredChannelTypes: [], channelOwners: {}, channelAccountOwners: {}, }); ipcMain.removeHandler('gateway:status'); ipcMain.handle('gateway:status', async () => ({ state: 'running', port: 18789, pid: 12345 })); ipcMain.removeHandler('gateway:rpc'); ipcMain.handle('gateway:rpc', async (_event: unknown, method: string, params: unknown) => { hostRequests.push({ path: `gateway:${method}`, method: 'RPC', body: params ?? null }); if (method === 'sessions.list') { return { success: true, result: { sessions: [{ key: 'agent:main:main', displayName: 'main' }] } }; } if (method === 'chat.history') { return { success: true, result: { messages: [] } }; } return { success: true, result: {} }; }); ipcMain.removeHandler('hostapi:fetch'); ipcMain.handle('hostapi:fetch', async (_event: unknown, request: { path?: string; method?: string; body?: string | null }) => { const path = request?.path ?? ''; const method = request?.method ?? 'GET'; const body = request?.body ? JSON.parse(request.body) : null; hostRequests.push({ path, method, body }); if (path === '/api/gateway/status' && method === 'GET') { return makeResponse({ state: 'running', port: 18789, pid: 12345, gatewayReady: true }); } if (path === '/api/agents' && method === 'GET') { return makeResponse(agentsSnapshot()); } if (path === '/api/agents/main/model' && method === 'PUT') { currentModelRef = body?.modelRef ?? refs.alphaModelRef; return makeResponse(agentsSnapshot()); } if (path === '/api/provider-accounts' && method === 'GET') { return makeResponse([ { id: 'alpha1234', vendorId: 'custom', label: 'Alpha', authMode: 'api_key', baseUrl: 'http://127.0.0.1:1111/v1', model: 'model-alpha', enabled: true, isDefault: true, createdAt: now, updatedAt: now, }, { id: 'beta5678', vendorId: 'custom', label: 'Beta', authMode: 'api_key', baseUrl: 'http://127.0.0.1:2222/v1', model: refs.betaModelRef, enabled: true, isDefault: false, createdAt: now, updatedAt: now, }, ]); } if (path === '/api/providers' && method === 'GET') { return makeResponse([ { id: 'alpha1234', type: 'custom', name: 'Alpha', enabled: true, hasKey: true, keyMasked: 'sk-***', createdAt: now, updatedAt: now }, { id: 'beta5678', type: 'custom', name: 'Beta', enabled: true, hasKey: true, keyMasked: 'sk-***', createdAt: now, updatedAt: now }, ]); } if (path === '/api/provider-vendors' && method === 'GET') { return makeResponse([]); } if (path === '/api/provider-accounts/default' && method === 'GET') { return makeResponse({ accountId: 'alpha1234' }); } return makeResponse({}); }); (globalThis as typeof globalThis & { __chatModelPickerRequests?: typeof hostRequests }).__chatModelPickerRequests = hostRequests; }, { alphaModelRef, betaModelRef }); const page = await getStableWindow(app); await page.reload(); await expect(page.getByTestId('main-layout')).toBeVisible(); await app.evaluate(({ BrowserWindow }) => { const win = BrowserWindow.getAllWindows()[0]; win?.webContents.send('gateway:status-changed', { state: 'running', port: 18789, pid: 12345, gatewayReady: true }); }); await expect(page.getByTestId('chat-model-picker-button')).toContainText('model-alpha'); await page.getByTestId('chat-model-picker-button').click(); await expect(page.getByTestId('chat-model-picker-menu')).toBeVisible(); await expect(page.getByTestId('chat-model-picker-menu')).toContainText('provider/model-beta'); await page.getByTestId('chat-model-picker-menu').getByRole('button', { name: 'provider/model-beta' }).click(); await expect(page.getByTestId('chat-model-picker-button')).toContainText('provider/model-beta'); const requests = await app.evaluate(() => ( (globalThis as typeof globalThis & { __chatModelPickerRequests?: Array<{ path: string; method: string; body: unknown }> }).__chatModelPickerRequests ?? [] )); expect(requests).toContainEqual({ path: '/api/agents/main/model', method: 'PUT', body: { modelRef: betaModelRef }, }); expect(requests.some((request) => request.path === '/api/gateway/restart' || request.path === '/api/gateway/start' || request.path === 'gateway:config.patch' )).toBe(false); } finally { await closeElectronApp(app); } }); });