Files
Genarrative/apps/ai-game-creator-shell/tests/customLlmSettings.test.tsx
T
k88936 25c5ecdf6e
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m58s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m34s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m29s
Project CI / Backend tests (pull_request) Failing after 4m38s
Project CI / Frontend tests (pull_request) Successful in 3m33s
Project CI / Repository checks (pull_request) Failing after 4m4s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m13s
Project CI / Native shell tests (pull_request) Successful in 6m35s
测试:全仓测试统一 Tauri 运行时替身安装点
- 78 处内联 window.__TAURI__ 赋值改为 installTauriRuntime,覆盖 appSurface 之外的 37 个测试文件
- 31 处 delete window.__TAURI__ / window.__TAURI__ = undefined 复位改为 resetTauriRuntime
- 删除 recentProjectsHook、projectCreationDirectory、projectResourceLiveIntegration、useProjectResourceCardPreviews、resourceVersionReplacementModel、unityProjectOpen 里的本地 asTauriInvoke / asTauriListen 适配器与相关类型别名
- designProjectRestore 的 setup 参数由 ReturnType<typeof vi.fn> 收窄为具体 invoke 签名
- 迁移后仅 tests/tauriRuntimeFake.ts 直接读写 window.__TAURI__
2026-10-03 20:19:45 +08:00

159 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment jsdom
import {
act,
cleanup,
fireEvent,
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import { useState } from 'react';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import defaults from '../game-creator.config.json';
import type {
GameCreatorAppConfig,
GameCreatorLlmConfig,
} from '../src/app/types';
import { CustomLlmSettings } from '../src/features/runtime-config/CustomLlmSettings';
import { RuntimeConfigDialog } from '../src/features/runtime-config/RuntimeConfigDialog';
import { installTauriRuntime, resetTauriRuntime } from './tauriRuntimeFake';
const invoke = vi.fn();
const llm = {
...defaults.llm,
customEnabled: true,
baseUrl: 'https://models.example/v1',
apiKey: 'test-custom-key',
visibleModels: ['vendor/model.v1'],
} as GameCreatorLlmConfig;
function Harness() {
const [draft, setDraft] = useState(llm);
return <CustomLlmSettings llm={draft} disabled={false} onChange={setDraft} />;
}
beforeEach(() => {
invoke.mockReset();
installTauriRuntime({ core: { invoke } });
});
afterEach(() => {
cleanup();
resetTauriRuntime();
});
test('reads endpoint models, filters candidates, and previews only checked models', async () => {
invoke.mockResolvedValue([
'vendor/model.v1',
'vendor/fast:latest',
'hidden-model',
]);
render(<Harness />);
fireEvent.click(screen.getByRole('button', { name: '读取模型列表' }));
await screen.findByText('已读取 3 个模型');
expect(invoke).toHaveBeenCalledWith('discover_game_creator_llm_models', {
llm,
});
fireEvent.click(screen.getByRole('checkbox', { name: 'vendor/fast:latest' }));
const preview = screen.getByRole('region', { name: '已勾选模型预览' });
expect(
within(preview)
.getAllByRole('listitem')
.map((item) => item.textContent),
).toEqual(['vendor/model.v1 默认', 'vendor/fast:latest']);
expect(within(preview).queryByText('hidden-model')).toBeNull();
fireEvent.change(screen.getByRole('textbox', { name: '搜索模型' }), {
target: { value: 'FAST' },
});
expect(screen.getAllByRole('checkbox')).toHaveLength(1);
fireEvent.click(screen.getByRole('checkbox', { name: 'vendor/fast:latest' }));
expect(within(preview).getAllByRole('listitem')).toHaveLength(1);
});
test('failed discovery preserves checked models and allows retry', async () => {
invoke
.mockRejectedValueOnce('模型列表读取失败(HTTP 401)')
.mockResolvedValueOnce(['vendor/model.v1']);
render(<Harness />);
fireEvent.click(screen.getByRole('button', { name: '读取模型列表' }));
await screen.findByRole('alert');
expect(
within(screen.getByRole('region', { name: '已勾选模型预览' })).getByText(
/vendor\/model.v1/,
),
).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '读取模型列表' }));
await screen.findByText('已读取 1 个模型');
expect(screen.queryByRole('alert')).toBeNull();
});
test('changing endpoint discards an old in-flight result and clears old selections', async () => {
let finish!: (models: string[]) => void;
invoke.mockImplementation(
() =>
new Promise((resolve) => {
finish = resolve;
}),
);
render(<Harness />);
fireEvent.click(screen.getByRole('button', { name: '读取模型列表' }));
fireEvent.change(screen.getByLabelText('自定义 LLM API 地址'), {
target: { value: 'https://new.example/v1' },
});
await act(async () => finish(['old-model']));
expect(screen.queryByRole('checkbox', { name: 'old-model' })).toBeNull();
expect(screen.getByText('尚未勾选模型')).not.toBeNull();
expect(screen.getByRole('button', { name: '读取模型列表' })).toHaveProperty(
'disabled',
false,
);
});
test('settings save retains custom credentials and checked models, and reopening restores them', async () => {
let config = {
...defaults,
llm,
agentLlm: {},
editorApi: { baseUrl: 'https://platform.example', apiKey: '' },
} as GameCreatorAppConfig;
invoke.mockImplementation(async (command, args) => {
if (command === 'write_game_creator_app_config') config = args.config;
if (
command === 'read_game_creator_app_config' ||
command === 'write_game_creator_app_config'
)
return { path: '/private/game-creator.config.json', config };
if (command === 'discover_game_creator_llm_models')
return ['vendor/model.v1', 'vendor/fast:latest'];
return [];
});
const first = render(<RuntimeConfigDialog onClose={() => {}} />);
await screen.findByLabelText('自定义 LLM API 地址');
expect(screen.getByLabelText('自定义 LLM API Key')).toHaveProperty(
'type',
'password',
);
expect(screen.getByText('OpenAI Responses')).not.toBeNull();
expect(screen.getByText('最高')).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '读取模型列表' }));
await screen.findByText('已读取 2 个模型');
fireEvent.click(screen.getByRole('checkbox', { name: 'vendor/fast:latest' }));
fireEvent.click(screen.getByRole('button', { name: '保存' }));
await waitFor(() =>
expect(config.llm.visibleModels).toEqual([
'vendor/model.v1',
'vendor/fast:latest',
]),
);
expect(config.llm.apiKey).toBe('test-custom-key');
expect(config.llm.customEnabled).toBe(true);
first.unmount();
render(<RuntimeConfigDialog onClose={() => {}} />);
await screen.findByLabelText('自定义 LLM API 地址');
expect(
within(screen.getByRole('region', { name: '已勾选模型预览' })).getAllByRole(
'listitem',
),
).toHaveLength(2);
});