Files
Genarrative/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx
suzmii 34e3f70409
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
补充服务启动配置校验并优化模型菜单
启动时校验 LLM Router 地址、模型和相关密钥配置

将刷新操作移入模型展开菜单并修复菜单样式覆盖

更新启动运维文档与定向测试
2026-09-05 19:48:27 +08:00

91 lines
3.5 KiB
TypeScript

// @vitest-environment jsdom
import {
cleanup,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import { resolveTauriInvoke } from '../src/app/tauri';
import { ConversationModelSelect } from '../src/features/project-workspace/ConversationModelSelect';
import { loadClientLlmModels } from '../src/services/clientApi';
vi.mock('../src/app/tauri', () => ({ resolveTauriInvoke: vi.fn() }));
vi.mock('../src/services/clientApi', () => ({ loadClientLlmModels: vi.fn() }));
const invoke = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(resolveTauriInvoke).mockReturnValue(invoke);
vi.mocked(loadClientLlmModels).mockResolvedValue({
defaultModelId: 'quality',
models: [
{ id: 'quality', displayName: '高质量' },
{ id: 'fast', displayName: '快速' },
],
});
invoke.mockImplementation(async (command, input) => ({
config: {
selectedModelId:
command === 'select_game_creator_model' ? input.modelId : 'quality',
},
}));
});
afterEach(cleanup);
test('only displays aliases and persists selection through the native command', async () => {
const onReady = vi.fn();
render(<ConversationModelSelect disabled={false} onReady={onReady} />);
await screen.findByRole('button', { name: '对话模型' });
expect(screen.queryByText('gpt-6-astra')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '对话模型' }));
fireEvent.click(screen.getByRole('option', { name: '快速' }));
await waitFor(() =>
expect(invoke).toHaveBeenCalledWith('select_game_creator_model', {
modelId: 'fast',
}),
);
await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true));
expect(
screen.getByRole('button', { name: '对话模型' }).textContent,
).toContain('快速');
});
test('does not mark a removed selection ready or expose the old identifier', async () => {
invoke.mockResolvedValue({
config: { selectedModelId: 'private-old-model' },
});
const onReady = vi.fn();
render(<ConversationModelSelect disabled={false} onReady={onReady} />);
await screen.findByText('请选择可用模型');
expect(onReady).toHaveBeenLastCalledWith(false);
expect(screen.queryByText('private-old-model')).toBeNull();
});
test('failed catalog can be refreshed without enabling submission', async () => {
vi.mocked(loadClientLlmModels).mockRejectedValueOnce(new Error('offline'));
const onReady = vi.fn();
render(<ConversationModelSelect disabled={false} onReady={onReady} />);
await screen.findByText('模型列表加载失败');
expect(onReady).toHaveBeenLastCalledWith(false);
fireEvent.click(screen.getByRole('button', { name: '对话模型' }));
fireEvent.click(screen.getByRole('button', { name: '刷新模型列表' }));
await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true));
});
test('a failed save keeps submission unavailable', async () => {
invoke.mockImplementation(async (command) => {
if (command === 'select_game_creator_model') throw new Error('disk full');
return { config: { selectedModelId: 'quality' } };
});
const onReady = vi.fn();
render(<ConversationModelSelect disabled={false} onReady={onReady} />);
await screen.findByRole('button', { name: '对话模型' });
fireEvent.click(screen.getByRole('button', { name: '对话模型' }));
fireEvent.click(screen.getByRole('option', { name: '快速' }));
await screen.findByText('模型选择保存失败');
expect(onReady).toHaveBeenLastCalledWith(false);
});