diff --git a/packages/core/tests/integration/prompt/service.integration.test.ts b/packages/core/tests/integration/prompt/service.integration.test.ts index 9c2dd9ff..ba9e1760 100644 --- a/packages/core/tests/integration/prompt/service.integration.test.ts +++ b/packages/core/tests/integration/prompt/service.integration.test.ts @@ -18,9 +18,10 @@ import { TextAdapterRegistry } from '../../../src/services/llm/adapters/registry */ describe('PromptService Integration Tests', () => { const hasGeminiKey = !!process.env.VITE_GEMINI_API_KEY; - const DELAY_BETWEEN_TESTS = 6000; // 1分钟延迟避免速率限制 + const DELAY_BETWEEN_TESTS = 60000; // 1分钟延迟避免速率限制 const TEST_TIMEOUT = 120000; // 2分钟超时 + let promptService: PromptService; let modelManager: ModelManager; let llmService: any; diff --git a/packages/ui/env.d.ts b/packages/ui/env.d.ts index 128cde31..8ca741af 100644 --- a/packages/ui/env.d.ts +++ b/packages/ui/env.d.ts @@ -9,5 +9,10 @@ declare module '*.vue' { // Vite 已经通过 /// 提供了内置的环境变量类型 +// E2E: 注入到 window 的测试辅助变量 +interface Window { + __TEST_DB_NAME__?: string +} + // 引入 Electron 类型定义 -/// \ No newline at end of file +/// diff --git a/packages/ui/src/components/FavoriteManager.vue b/packages/ui/src/components/FavoriteManager.vue index 2542ed2f..b959038f 100644 --- a/packages/ui/src/components/FavoriteManager.vue +++ b/packages/ui/src/components/FavoriteManager.vue @@ -58,7 +58,7 @@ :options="actionMenuOptions" @select="handleActionMenuSelect" > - + @@ -475,12 +475,14 @@ const actionMenuOptions = computed(() => [ { label: t('favorites.manager.actions.manageTags'), key: 'manageTags', - icon: () => h(NIcon, null, { default: () => h(Tags) }) + icon: () => h(NIcon, null, { default: () => h(Tags) }), + props: { 'data-testid': 'favorites-manager-action-manage-tags' } }, { label: t('favorites.manager.actions.manageCategories'), key: 'manageCategories', - icon: () => h(NIcon, null, { default: () => h(Folder) }) + icon: () => h(NIcon, null, { default: () => h(Folder) }), + props: { 'data-testid': 'favorites-manager-action-manage-categories' } }, { type: 'divider' @@ -488,7 +490,8 @@ const actionMenuOptions = computed(() => [ { label: t('favorites.manager.actions.export'), key: 'export', - icon: () => h(NIcon, null, { default: () => h(Download) }) + icon: () => h(NIcon, null, { default: () => h(Download) }), + props: { 'data-testid': 'favorites-manager-action-export' } }, { type: 'divider' @@ -496,7 +499,8 @@ const actionMenuOptions = computed(() => [ { label: t('favorites.manager.actions.clear'), key: 'clear', - icon: () => h(NIcon, null, { default: () => h(Trash) }) + icon: () => h(NIcon, null, { default: () => h(Trash) }), + props: { 'data-testid': 'favorites-manager-action-clear' } } ]); diff --git a/packages/ui/src/components/FunctionModeSelector.vue b/packages/ui/src/components/FunctionModeSelector.vue index 800bfa9e..41f4bb08 100644 --- a/packages/ui/src/components/FunctionModeSelector.vue +++ b/packages/ui/src/components/FunctionModeSelector.vue @@ -1,24 +1,27 @@ - {{ t('nav.basicMode') }} {{ t('nav.contextMode') }} diff --git a/packages/ui/src/components/OptimizationModeSelector.vue b/packages/ui/src/components/OptimizationModeSelector.vue index 35589d67..3eadd39f 100644 --- a/packages/ui/src/components/OptimizationModeSelector.vue +++ b/packages/ui/src/components/OptimizationModeSelector.vue @@ -1,6 +1,6 @@ - {{ systemLabel }} @@ -25,6 +27,7 @@ @@ -32,6 +35,7 @@ diff --git a/packages/ui/src/components/OutputDisplay.vue b/packages/ui/src/components/OutputDisplay.vue index 2049963f..5fc5ae12 100644 --- a/packages/ui/src/components/OutputDisplay.vue +++ b/packages/ui/src/components/OutputDisplay.vue @@ -13,6 +13,7 @@ :loading="loading" :streaming="streaming" :compareService="compareService" + :test-id="testId" :style="{ height: '100%', maxHeight: '100%', flex: 1, minHeight: 0, overflow: 'hidden' }" @update:content="emit('update:content', $event)" @update:reasoning="emit('update:reasoning', $event)" @@ -71,6 +72,9 @@ interface Props { placeholder?: string loading?: boolean streaming?: boolean + + /** 透传给 OutputDisplayCore 的 data-testid(挂在根节点) */ + testId?: string } const props = withDefaults(defineProps(), { @@ -79,6 +83,7 @@ const props = withDefaults(defineProps(), { enableEdit: true, enableDiff: true, enableFavorite: true, + testId: undefined, }); // Emits @@ -97,6 +102,8 @@ const emit = defineEmits<{ const isShowingFullscreen = ref(false); +const testId = computed(() => props.testId || undefined) + // 注入服务并获取 CompareService const services = inject>('services'); if (!services) { diff --git a/packages/ui/src/components/OutputDisplayCore.vue b/packages/ui/src/components/OutputDisplayCore.vue index 479f6195..df50edba 100644 --- a/packages/ui/src/components/OutputDisplayCore.vue +++ b/packages/ui/src/components/OutputDisplayCore.vue @@ -3,6 +3,7 @@ :bordered="false" class="output-display-core h-full max-height: 100% " content-style="padding: 0; height: 100%; max-height: 100%; display: flex; flex-direction: column; overflow: hidden;" + :data-testid="testId" > @@ -221,6 +222,9 @@ interface Props { content?: string originalContent?: string reasoning?: string + + /** E2E/测试定位用的 data-testid(挂在组件根节点) */ + testId?: string // 显示模式 mode: 'readonly' | 'editable' @@ -245,6 +249,7 @@ const props = withDefaults(defineProps(), { content: '', originalContent: '', reasoning: '', + testId: undefined, mode: 'readonly', reasoningMode: 'auto', enabledActions: () => ['fullscreen', 'diff', 'copy', 'edit', 'reasoning', 'favorite'], @@ -252,6 +257,8 @@ const props = withDefaults(defineProps(), { placeholder: '' }) +const testId = computed(() => props.testId || undefined) + // 事件定义 const emit = defineEmits<{ 'update:content': [content: string] diff --git a/packages/ui/src/components/PromptPanel.vue b/packages/ui/src/components/PromptPanel.vue index caf2d20d..540f04d8 100644 --- a/packages/ui/src/components/PromptPanel.vue +++ b/packages/ui/src/components/PromptPanel.vue @@ -213,6 +213,7 @@ - + - emit('message-change', index, message, action)" - @variable-extracted="handleVariableExtracted" - @add-missing-variable="handleAddMissingVariable" - /> + { + // Pro Multi:新增/更新消息后自动选中最新消息,确保“优化”按钮可用 + if ((action === 'add' || action === 'update') && (message.role === 'system' || message.role === 'user') && message.id) { + void conversationOptimization.selectMessage(message) + } + emit('message-change', index, message, action) + }" + :available-variables="availableVariables" + :temporary-variables="tempVars.temporaryVariables.value" + :scan-variables="scanVariables" + :optimization-mode="optimizationMode" + :tool-count="toolCount" + @open-variable-manager="emit('open-variable-manager')" + @open-context-editor="emit('open-context-editor')" + @open-tool-manager="emit('open-tool-manager')" + :enable-tool-management="true" + :collapsible="true" + :max-height="300" + :selected-message-id="selectedMessageId" + :enable-message-optimization="enableMessageOptimization" + :is-message-optimizing="conversationOptimization.isOptimizing.value" + @message-select="conversationOptimization.selectMessage" + @optimize-message="handleOptimizeClick" + @variable-extracted="handleVariableExtracted" + @add-missing-variable="handleAddMissingVariable" + /> @@ -95,6 +101,7 @@ :disabled="displayAdapter.displayedIsOptimizing.value || !selectedMessageId" @click="handleOptimizeClick" block + data-testid="pro-multi-optimize-button" > {{ displayAdapter.displayedIsOptimizing.value ? $t('common.loading') : $t('promptOptimizer.optimize') }} @@ -112,6 +119,7 @@ > @@ -409,8 +418,9 @@ const toolCount = computed(() => { }) const enableMessageOptimization = computed(() => { - // 固定为 false(消息优化功能已移除) - return false + // Pro Multi:自动选中最新消息进行优化(不需要显式“选择”按钮) + // 这里仍需启用“消息优化模式”,以便 PromptPanel 展示优化结果区。 + return optimizationMode === 'system' }) // 🆕 初始化临时变量管理器(与 ContextEditor 共享) @@ -498,6 +508,22 @@ onMounted(() => { // ✅ 刷新模型列表 modelSelection.refreshTextModels() + // Pro Multi:自动选中最新一条可优化消息(system/user),以便直接启用“优化” + const latestSelectable = [...(optimizationContext.value || [])] + .reverse() + .find((msg) => msg && (msg.role === 'system' || msg.role === 'user') && !!msg.id) + if (latestSelectable) { + void conversationOptimization.selectMessage(latestSelectable) + } + + // 兜底:如果还没有消息可选,但 session store 已有选中消息(刷新/恢复场景),尝试同步一次 + if (!latestSelectable && proMultiSession.selectedMessageId) { + const restored = (optimizationContext.value || []).find((m) => m.id === proMultiSession.selectedMessageId) + if (restored) { + void conversationOptimization.selectMessage(restored) + } + } + const saved = proMultiSession.testResults if (!saved) { return diff --git a/packages/ui/src/components/context-mode/ContextUserWorkspace.vue b/packages/ui/src/components/context-mode/ContextUserWorkspace.vue index 31d367cb..49c9e1e4 100644 --- a/packages/ui/src/components/context-mode/ContextUserWorkspace.vue +++ b/packages/ui/src/components/context-mode/ContextUserWorkspace.vue @@ -161,6 +161,7 @@ content-style="height: 100%; max-height: 100%; overflow: hidden;" > - - {{ t("conversation.addFirst") }} - + + {{ t("conversation.addFirst") }} + @@ -117,16 +118,18 @@ :key="`message-${index}`" style="padding: 0" > - + @@ -195,6 +198,7 @@ quaternary circle :title="message.id === selectedMessageId ? t('conversation.selected') : t('conversation.selectForOptimization')" + :data-testid="`pro-multi-select-message-${index}`" >