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);
}
});
});