Files
MyGoNavi/frontend/src/components/AISettingsModal.edit-password.test.tsx
Syngnat 5b8bbd672e feat(ai-mcp): 优化外部客户端接入引导与状态识别
- 调整 Claude Code 和 Codex 的安装文案与状态标签,明确是把 GoNavi 暴露给外部客户端使用
- 优化 MCP 客户端默认选择逻辑,优先聚焦未接入或需更新的目标并避免刷新后乱跳
- 同步补齐前端 mock、后端状态文案和定向测试,确保安装区交互与状态展示一致
2026-06-08 16:29:53 +08:00

92 lines
5.7 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.
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
const source = readFileSync(new URL('./AISettingsModal.tsx', import.meta.url), 'utf8');
const aiChatPanelCss = readFileSync(new URL('./AIChatPanel.css', import.meta.url), 'utf8');
const providersSectionSource = readFileSync(new URL('./ai/AISettingsProvidersSection.tsx', import.meta.url), 'utf8');
describe('AISettingsModal edit password behavior', () => {
it('loads editable provider details before opening the edit modal', () => {
expect(source).toContain("typeof Service?.AIGetEditableProvider === 'function'");
expect(source).toContain('await Service.AIGetEditableProvider(p.id)');
});
it('loads and saves user-level custom prompts through the AI service', () => {
expect(source).toContain("callOrFallback(() => Service.AIGetUserPromptSettings?.(), EMPTY_AI_USER_PROMPT_SETTINGS)");
expect(source).toContain('await Service?.AISaveUserPromptSettings?.(payload);');
expect(source).toContain("window.dispatchEvent(new CustomEvent('gonavi:ai:config-changed'))");
expect(source).toContain("import AISettingsPromptsSection from './ai/AISettingsPromptsSection';");
expect(source).toContain('<AISettingsPromptsSection');
});
it('loads MCP servers and skills through the AI service', () => {
expect(source).toContain('Service.AIGetMCPClientInstallStatuses?.()');
expect(source).toContain('Service.AIGetMCPServers?.()');
expect(source).toContain('Service.AIListMCPTools?.()');
expect(source).toContain('Service.AIGetSkills?.()');
expect(source).toContain("import AISettingsSkillsSection from './ai/AISettingsSkillsSection';");
expect(source).toContain('<AISettingsSkillsSection');
});
it('delegates bulky MCP and built-in tool sections to dedicated ai components', () => {
expect(source).toContain("import AIBuiltinToolsCatalog from './ai/AIBuiltinToolsCatalog';");
expect(source).toContain("import AISettingsProvidersSection from './ai/AISettingsProvidersSection';");
expect(source).toContain("import AISettingsSidebar, { type AISettingsSectionKey } from './ai/AISettingsSidebar';");
expect(source).toContain("import AISettingsSafetySection from './ai/AISettingsSafetySection';");
expect(source).toContain("import AISettingsContextSection from './ai/AISettingsContextSection';");
expect(source).toContain('<AISettingsProvidersSection');
expect(source).toContain("import AISettingsMCPSection, { type MCPClientKey } from './ai/AISettingsMCPSection';");
expect(source).toContain('<AISettingsSidebar');
expect(source).toContain('<AISettingsSafetySection');
expect(source).toContain('<AISettingsContextSection');
expect(source).toContain('<AISettingsMCPSection');
expect(source).toContain('<AIBuiltinToolsCatalog');
});
it('wires the external MCP client install panel actions back to the modal handlers', () => {
expect(source).toContain('mcpClientStatuses={mcpClientStatuses}');
expect(source).toContain('selectedMCPClient={selectedMCPClient}');
expect(source).toContain('const [mcpClientSelectionTouched, setMCPClientSelectionTouched] = useState(false);');
expect(source).toContain('const handleSelectMCPClient = useCallback((client: MCPClientKey) => {');
expect(source).toContain('pickPreferredMCPClient(normalizedStatuses, mcpClientSelectionTouched ? prev : undefined)');
expect(source).toContain('setMCPClientSelectionTouched(true);');
expect(source).toContain('onSelectClient={handleSelectMCPClient}');
expect(source).toContain('onRefreshStatus={() => void loadMCPClientStatuses()}');
expect(source).toContain('onCopyConfigPath={() => void handleCopySelectedMCPConfigPath()}');
expect(source).toContain('onCopyLaunchCommand={() => void handleCopySelectedMCPLaunchCommand()}');
expect(source).toContain('onInstallSelectedClient={handleInstallSelectedMCPClient}');
});
it('waits briefly for the AI service bridge before warning and removes noisy provider debug logs', () => {
expect(source).toContain('const resolveAIService = useCallback(async () => {');
expect(source).toContain('const service = await waitForAIService();');
expect(source).not.toContain("console.log('[AI] AIGetProviders result:'");
expect(source).not.toContain("console.log('[AI] AIGetActiveProvider result:'");
});
it('keeps the prefilled api key masked by default', () => {
expect(source).toContain('const [primaryPasswordVisible, setPrimaryPasswordVisible] = useState(false);');
expect(providersSectionSource).toContain('visible: primaryPasswordVisible,');
});
it('does not render the clear helper block anymore', () => {
expect(source).not.toContain('当前已保存 API Key。留空表示继续沿用输入新值表示替换。');
expect(source).not.toContain('清除已保存 API Key');
expect(source).not.toContain('留空表示继续沿用已保存密钥');
});
it('renders in-modal test errors through the local message host', () => {
expect(source).toContain('antdMessage.useMessage({ getContainer: () => modalBodyRef.current || document.body })');
expect(source).toContain("void messageApi.error(`测试失败: ${res?.message || '未知错误'}`);");
});
it('keeps long ai settings toast errors wrapped within the modal body', () => {
expect(aiChatPanelCss).toContain('.ai-settings-body .ant-message {');
expect(aiChatPanelCss).toContain('width: min(100%, 720px);');
expect(aiChatPanelCss).toContain('max-width: calc(100% - 32px);');
expect(aiChatPanelCss).toContain('.ai-settings-body .ant-message .ant-message-notice-content {');
expect(aiChatPanelCss).toContain('white-space: normal;');
expect(aiChatPanelCss).toContain('overflow-wrap: anywhere;');
});
});