import { beforeEach, describe, expect, it, vi } from 'vitest'; import { render, screen } from '@testing-library/react'; import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { Settings } from '@/pages/Settings'; import { useGatewayStore } from '@/stores/gateway'; import { useSettingsStore } from '@/stores/settings'; import { useYinianStore } from '@/stores/yinian'; import i18n from '@/i18n'; const hostApiFetchMock = vi.fn(); vi.mock('@/components/settings/ProvidersSettings', () => ({ ProvidersSettings: () =>
模型配置设置
, })); vi.mock('@/components/settings/AgentSystemDocumentsSettings', () => ({ AgentSystemDocumentsSettings: () =>
系统文档管理
, })); vi.mock('@/components/settings/UpdateSettings', () => ({ UpdateSettings: () => null, })); vi.mock('@/pages/Channels', () => ({ Channels: () => null, })); vi.mock('@/pages/YinianSkills', () => ({ YinianSkills: () => null, })); vi.mock('@/lib/host-api', () => ({ hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args), })); vi.mock('@/lib/api-client', () => ({ getGatewayWsDiagnosticEnabled: () => false, invokeIpc: vi.fn().mockResolvedValue({ success: true, command: 'agent' }), setGatewayWsDiagnosticEnabled: vi.fn(), toUserMessage: (error: unknown) => String(error), })); vi.mock('@/lib/telemetry', () => ({ clearUiTelemetry: vi.fn(), getUiTelemetrySnapshot: vi.fn(() => []), subscribeUiTelemetry: vi.fn(() => () => undefined), trackUiEvent: vi.fn(), })); const hotel = { id: 'workspace-1', name: '智念空间', city: '杭州', role: 'manager' as const, permissions: [], ota: [], }; function renderSettings(initialEntry = '/settings/runtime') { return render( } /> , ); } describe('Settings advanced model config', () => { beforeEach(async () => { vi.clearAllMocks(); await i18n.changeLanguage('zh'); hostApiFetchMock.mockImplementation((path: string) => { if (path.startsWith('/api/diagnostics/model-config')) { return Promise.resolve({ capturedAt: 1, ok: true, model: { primary: null, fallbacks: [], providerKey: null, modelId: null }, runtime: { heartbeatEvery: null, heartbeatDisabled: false }, providers: [], authProfiles: { path: '', exists: false, providers: [] }, checks: [], paths: { openclawConfig: '', authProfiles: '' }, }); } if (path.startsWith('/api/diagnostics/office-runtime')) { return Promise.resolve({ capturedAt: 1, ok: true, repairAttempted: false, python: { executable: null, packages: [] }, node: { modules: [] }, dotnet: { available: false, version: null }, checks: [], }); } return Promise.resolve({}); }); useSettingsStore.setState({ devModeUnlocked: false, gatewayAutoStart: false, telemetryEnabled: true, proxyEnabled: false, proxyServer: '', proxyHttpServer: '', proxyHttpsServer: '', proxyAllServer: '', proxyBypassRules: '', }); useGatewayStore.setState({ status: { state: 'stopped', port: 18789, gatewayReady: false }, }); useYinianStore.setState({ status: 'authenticated', session: { authenticated: true, user: { id: 'user-1', name: '王管理员' }, hotels: [hotel], currentHotelId: hotel.id, accessTokenExpiresAt: 100, }, config: { serverTime: 1, user: { id: 'user-1', name: '王管理员' }, hotel, hotels: [hotel], entitlements: [], notificationChannels: [], featureFlags: {}, uiPolicy: { defaultPage: 'today', showAdvancedSettings: false }, }, error: null, }); }); it('keeps model configuration settings inside advanced mode', async () => { useSettingsStore.setState({ devModeUnlocked: true }); renderSettings(); expect(await screen.findByTestId('settings-model-config-section')).toBeVisible(); expect(screen.getByTestId('settings-advanced-model-config-panel')).toHaveTextContent('模型配置设置'); expect(screen.getByTestId('settings-model-diagnostics')).toBeVisible(); }); it('keeps model configuration settings hidden until advanced mode is opened', () => { renderSettings(); expect(screen.queryByTestId('settings-model-config-section')).not.toBeInTheDocument(); expect(screen.queryByTestId('settings-advanced-model-config-panel')).not.toBeInTheDocument(); }); it('opens the agent system documents settings tab by route', () => { renderSettings('/settings/system-docs'); expect(screen.getByText('系统文档')).toBeVisible(); expect(screen.getByTestId('settings-agent-system-documents-panel')).toHaveTextContent('系统文档管理'); }); it('shows only the current default model service in diagnostics', async () => { useSettingsStore.setState({ devModeUnlocked: true }); hostApiFetchMock.mockImplementation((path: string) => { if (path.startsWith('/api/diagnostics/model-config')) { return Promise.resolve({ capturedAt: 1, ok: true, model: { primary: 'deepseek/deepseek-v4-pro', fallbacks: [], providerKey: 'deepseek', modelId: 'deepseek-v4-pro', }, runtime: { heartbeatEvery: '0m', heartbeatDisabled: true }, providers: [{ key: 'deepseek', configured: true, baseUrl: 'https://api.deepseek.com/v1', api: 'openai-completions', timeoutSeconds: null, authHeader: null, modelCount: 1, }], authProfiles: { path: '', exists: true, providers: [{ provider: 'deepseek', profileCount: 1, profileIds: ['deepseek:default'], types: ['api_key'], hasDefaultProfile: true, lastGoodProfileId: 'deepseek:default', }], }, checks: [ { id: 'primary-model', label: '默认模型', status: 'ok', detail: 'deepseek/deepseek-v4-pro' }, { id: 'provider-entry', label: '模型服务', status: 'ok', detail: '已配置 deepseek' }, { id: 'auth-profile', label: '调用凭据', status: 'ok', detail: '已找到 deepseek 的本地凭据' }, ], paths: { openclawConfig: '', authProfiles: '' }, }); } if (path.startsWith('/api/diagnostics/office-runtime')) { return Promise.resolve({ capturedAt: 1, ok: true, repairAttempted: false, python: { executable: null, packages: [] }, node: { modules: [] }, dotnet: { available: false, version: null }, checks: [], }); } return Promise.resolve({}); }); renderSettings(); expect(await screen.findByText('deepseek/deepseek-v4-pro')).toBeVisible(); expect(screen.getByText('当前模型服务')).toBeVisible(); expect(screen.getAllByText('deepseek').length).toBeGreaterThan(0); expect(screen.queryByText('yinian-model')).not.toBeInTheDocument(); expect(screen.queryByText('minimax')).not.toBeInTheDocument(); expect(screen.queryByText('minimax-portal')).not.toBeInTheDocument(); }); });