91 lines
3.5 KiB
TypeScript
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);
|
|
});
|