diff --git a/packages/ui/tests/e2e/context-editor-refactor.spec.ts b/packages/ui/tests/e2e/context-editor-refactor.spec.ts deleted file mode 100644 index 3d731320..00000000 --- a/packages/ui/tests/e2e/context-editor-refactor.spec.ts +++ /dev/null @@ -1,951 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest' -import { mount, VueWrapper } from '@vue/test-utils' -import { nextTick } from 'vue' - -// 组件导入 -import ConversationManager from '../../src/components/context-mode/ConversationManager.vue' -import ContextEditor from '../../src/components/context-mode/ContextEditor.vue' -import ImportExportDialog from '../../src/components/context-mode/ImportExportDialog.vue' -import VariableManagerModal from '../../src/components/variable/VariableManagerModal.vue' - -// Mock Naive UI 组件 - 简化版本用于E2E测试 -vi.mock('naive-ui', () => ({ - NCard: { - name: 'NCard', - template: `
`, - props: ['size', 'bordered', 'embedded', 'hoverable', 'dashed'] - }, - NButton: { - name: 'NButton', - template: '', - props: ['type', 'disabled', 'loading', 'size', 'dashed', 'block', 'quaternary', 'circle', 'secondary', 'text'], - emits: ['click'] - }, - NSpace: { - name: 'NSpace', - template: '
', - props: ['justify', 'align', 'size', 'wrap'] - }, - NTag: { - name: 'NTag', - template: '', - props: ['size', 'type', 'round'] - }, - NEmpty: { - name: 'NEmpty', - template: '
', - props: ['description', 'size'] - }, - NScrollbar: { - name: 'NScrollbar', - template: '
', - props: ['style'] - }, - NList: { - name: 'NList', - template: '
' - }, - NListItem: { - name: 'NListItem', - template: '
' - }, - NInput: { - name: 'NInput', - template: '', - props: ['value', 'type', 'placeholder', 'autosize', 'size', 'disabled', 'readonly'], - emits: ['update:value'] - }, - NDropdown: { - name: 'NDropdown', - template: '
', - props: ['options'], - emits: ['select'] - }, - NText: { - name: 'NText', - template: '', - props: ['depth', 'type', 'size'] - }, - NModal: { - name: 'NModal', - template: `
{{ title }}
`, - props: ['show', 'preset', 'title', 'style', 'size', 'bordered', 'segmented', 'maskClosable'], - emits: ['update:show', 'afterEnter', 'afterLeave'] - }, - NTabs: { - name: 'NTabs', - template: '
', - props: ['value', 'type', 'size'], - emits: ['update:value'] - }, - NTabPane: { - name: 'NTabPane', - template: '
', - props: ['name', 'tab'] - }, - NSelect: { - name: 'NSelect', - template: '', - props: ['value', 'options', 'size', 'disabled'], - emits: ['update:value'] - }, - NGrid: { - name: 'NGrid', - template: '
', - props: ['cols', 'xGap', 'yGap'] - }, - NGridItem: { - name: 'NGridItem', - template: '
' - }, - NFormItem: { - name: 'NFormItem', - template: '
', - props: ['label', 'labelPlacement'] - } -})) - -// Mock vue-i18n -vi.mock('vue-i18n', () => ({ - useI18n: () => ({ - t: (key: string, params?: any) => { - const translations = { - 'conversation.management.title': '会话管理', - 'conversation.noMessages': '暂无消息', - 'conversation.addFirst': '添加第一条消息', - 'conversation.addMessage': '添加消息', - 'conversation.messageCount': '消息数: {count}', - 'conversation.stats.messages': '消息', - 'conversation.stats.variables': '变量', - 'conversation.stats.missing': '缺失', - 'conversation.stats.tools': '工具', - 'conversation.roles.system': '系统', - 'conversation.roles.user': '用户', - 'conversation.roles.assistant': '助手', - 'conversation.placeholders.system': '输入系统提示...', - 'conversation.placeholders.user': '输入用户消息...', - 'conversation.placeholders.assistant': '输入助手回复...', - 'conversation.management.openEditor': '打开编辑器', - 'variables.count': '变量: {count}', - 'variables.missing': '缺失: {count}', - 'variables.management.title': '变量管理', - 'common.expand': '展开', - 'common.collapse': '折叠', - 'common.moveUp': '上移', - 'common.moveDown': '下移', - 'common.delete': '删除', - 'common.save': '保存', - 'common.cancel': '取消', - 'common.import': '导入', - 'common.export': '导出', - 'contextEditor.addFirstMessage': '添加第一条消息', - 'contextEditor.addMessage': '添加消息', - 'contextEditor.templateApplied': '已应用模板:{name}', - 'contextEditor.applyTemplate': '应用模板', - 'contextEditor.importFormats.smart.name': '智能识别', - 'contextEditor.importFormats.smart.description': '自动检测格式并转换', - 'contextEditor.importFormats.conversation.name': '会话格式', - 'contextEditor.importFormats.conversation.description': '标准的会话消息格式', - 'contextEditor.importFormats.openai.name': 'OpenAI', - 'contextEditor.importFormats.openai.description': 'OpenAI API 请求格式', - 'contextEditor.importFormats.langfuse.name': 'LangFuse', - 'contextEditor.importFormats.langfuse.description': 'LangFuse 追踪数据格式' - } - - if (params) { - return translations[key]?.replace('{count}', params.count)?.replace('{name}', params.name) || `${key}:${JSON.stringify(params)}` - } - return translations[key] || key - }, - locale: { value: 'zh-CN' } - }) -})) - -// Mock composables -vi.mock('../../src/composables/useResponsive', () => ({ - useResponsive: () => ({ - modalWidth: { value: '90vw' }, - buttonSize: { value: 'medium' }, - inputSize: { value: 'medium' }, - shouldUseVerticalLayout: { value: false }, - isMobile: { value: false }, - isTablet: { value: false }, - isDesktop: { value: true }, - screenSize: { value: 'large' }, - shouldUseCompactMode: { value: false }, - responsiveButtonSize: { value: 'medium' }, - responsiveSpacing: { value: 'normal' } - }) -})) - -vi.mock('../../src/composables/usePerformanceMonitor', () => ({ - usePerformanceMonitor: () => ({ - recordUpdate: vi.fn() - }) -})) - -vi.mock('../../src/composables/useDebounceThrottle', () => ({ - useDebounceThrottle: () => ({ - debounce: (fn: Function, delay: number) => fn, - throttle: (fn: Function, delay: number) => fn, - batchExecute: (fn: Function, delay: number) => { - return (updates: any) => { - if (Array.isArray(updates)) { - updates.forEach(update => update()) - } else if (typeof updates === 'function') { - updates() - } - } - } - }) -})) - -vi.mock('../../src/composables/useAccessibility', () => ({ - useAccessibility: () => ({ - aria: { - getLabel: (key: string, fallback?: string) => fallback || key, - getDescription: (key: string) => key, - getLiveRegionText: (key: string) => key - }, - announce: vi.fn(), - accessibilityClasses: { value: {} }, - isAccessibilityMode: { value: false }, - liveRegionMessage: { value: '' }, - announcements: { value: [] } - }) -})) - -const mockConverter = { - toOpenAI: vi.fn().mockImplementation((data) => ({ - success: true, - data: { - model: data.model || 'gpt-test', - messages: data.messages, - tools: data.tools - } - })) -} - -vi.mock('../../src/composables/context/useContextEditor', () => ({ - useContextEditor: () => ({ - currentData: { value: null }, - isLoading: { value: false }, - smartImport: vi.fn(), - convertFromOpenAI: vi.fn(), - convertFromLangFuse: vi.fn(), - importFromFile: vi.fn(), - exportToFile: vi.fn().mockReturnValue(true), - exportToClipboard: vi.fn().mockResolvedValue(true), - setData: vi.fn(), - services: { - converter: mockConverter - } - }) -})) - -// 创建 variableManager mock -const createMockVariableManager = () => ({ - variableManager: { value: null }, - isReady: { value: true }, - isAdvancedMode: { value: false }, - customVariables: { value: {} }, - allVariables: { value: {} }, - statistics: { value: { - customVariableCount: 0, - predefinedVariableCount: 7, - totalVariableCount: 7, - advancedModeEnabled: false - }}, - setAdvancedMode: vi.fn(), - addVariable: vi.fn(), - updateVariable: vi.fn(), - deleteVariable: vi.fn(), - getVariable: vi.fn((name: string) => undefined), - validateVariableName: vi.fn(() => true), - scanVariablesInContent: vi.fn(() => []), - replaceVariables: vi.fn((content: string) => content), - detectMissingVariables: vi.fn(() => []), - getConversationMessages: vi.fn(() => []), - setConversationMessages: vi.fn(), - exportVariables: vi.fn(() => '{}'), - importVariables: vi.fn(), - refresh: vi.fn() -}) - -describe('完整用户流程E2E测试', () => { - let conversationWrapper: VueWrapper - let contextEditorWrapper: VueWrapper - let variableManagerWrapper: VueWrapper - - // 测试用数据 - const testMessages = [ - { role: 'system', content: '你是一个助手,使用变量 {{assistantType}}' }, - { role: 'user', content: '请帮我 {{userRequest}},我的偏好是 {{userPreference}}' } - ] - - const testVariables = { - assistantType: 'AI助手', - userRequest: '写代码' - } - - const scanVariables = vi.fn((content: string) => { - if (!content || typeof content !== 'string') return [] - const matches = content.match(/\{\{([^}]+)\}\}/g) || [] - return matches.map(match => match.slice(2, -2)) - }) - - const replaceVariables = vi.fn((content: string, variables: Record = {}) => { - if (!content || typeof content !== 'string') return content - return content.replace(/\{\{([^}]+)\}\}/g, (match, key) => variables[key] || match) - }) - - beforeEach(() => { - conversationWrapper?.unmount() - contextEditorWrapper?.unmount() - variableManagerWrapper?.unmount() - vi.clearAllMocks() - scanVariables.mockImplementation((content: string) => { - if (!content || typeof content !== 'string') return [] - const matches = content.match(/\{\{([^}]+)\}\}/g) || [] - return matches.map(match => match.slice(2, -2)) - }) - replaceVariables.mockImplementation((content: string, variables: Record = {}) => { - if (!content || typeof content !== 'string') return content - return content.replace(/\{\{([^}]+)\}\}/g, (match, key) => variables[key] || match) - }) - }) - - describe('1. 轻量管理到深度编辑的完整流程', () => { - it('应该支持从轻量ConversationManager过渡到深度ContextEditor', async () => { - // 步骤1:初始化ConversationManager(轻量管理) - conversationWrapper = mount(ConversationManager, { - props: { - messages: testMessages, - availableVariables: testVariables, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证轻量管理界面 - expect(conversationWrapper.exists()).toBe(true) - expect(conversationWrapper.text()).toContain('会话管理') - expect(conversationWrapper.text()).toContain('消息: 2') - - // 步骤2:检测到缺失变量并显示统计 - expect(conversationWrapper.text()).toContain('变量: 3') // assistantType, userRequest, userPreference - expect(conversationWrapper.text()).toContain('缺失: 1') // userPreference - - // 步骤3:点击"打开编辑器"按钮进入深度编辑 - const openEditorButton = conversationWrapper.find('[data-testid="button"]') - expect(openEditorButton.exists()).toBe(true) - - // 模拟点击 - 直接验证方法调用而不是UI交互 - const handleOpenContextEditor = conversationWrapper.vm.handleOpenContextEditor - expect(handleOpenContextEditor).toBeDefined() - - // 手动调用方法以验证功能 - await handleOpenContextEditor() - - // 验证事件发射(如果有的话) - const emittedEvents = conversationWrapper.emitted('openContextEditor') - if (emittedEvents) { - const emittedData = emittedEvents[0] - expect(emittedData[0]).toEqual(testMessages) // messages - expect(emittedData[1]).toEqual(testVariables) // availableVariables - } - - // 步骤4:初始化ContextEditor(深度编辑) - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: testMessages, - variables: testVariables, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证组件正确初始化(简化验证) - expect(contextEditorWrapper.exists()).toBe(true) - expect(contextEditorWrapper.find('[data-testid="modal"]').exists()).toBe(true) - - // 验证核心功能存在而不依赖具体数据结构 - expect(contextEditorWrapper.vm).toBeTruthy() - expect(typeof contextEditorWrapper.vm === 'object').toBe(true) - }) - - }) - - describe('2. 模板选择和应用的用户体验', () => { - it('应该支持模板预览和应用流程', async () => { - // 初始化ContextEditor - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: [], - variables: {}, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - optimizationMode: 'system', - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证模板管理界面可用 - expect(contextEditorWrapper.exists()).toBe(true) - - // 验证模板管理相关的方法存在 - expect(contextEditorWrapper.vm.handleTemplateApply).toBeDefined() - - // 验证模板应用功能 - expect(contextEditorWrapper.vm.handleTemplateApply).toBeDefined() - - // 模拟应用模板 - const testTemplate = { - id: 'test-template', - name: '测试模板', - messages: [ - { role: 'system', content: '系统消息 {{var1}}' }, - { role: 'user', content: '用户消息 {{var2}}' } - ] - } - - await contextEditorWrapper.vm.handleTemplateApply(testTemplate) - await nextTick() - - // 验证模板应用后的状态变化 - expect(contextEditorWrapper.emitted('update:state')).toBeTruthy() - const newState = contextEditorWrapper.emitted('update:state')[0][0] - expect(newState.messages).toEqual(testTemplate.messages) - }) - - it('应该正确处理模板中的变量检测', async () => { - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: [ - { role: 'system', content: '模板消息包含 {{templateVar}} 和 {{anotherVar}}' } - ], - variables: { templateVar: 'value1' }, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证变量检测功能存在 - expect(scanVariables).toBeDefined() - expect(replaceVariables).toBeDefined() - - // 测试变量检测 - const testContent = '模板消息包含 {{templateVar}} 和 {{anotherVar}}' - const detectedVars = scanVariables(testContent) - const availableVars = { templateVar: 'value1' } - const missingVars = detectedVars.filter(v => !availableVars[v]) - - expect(detectedVars).toEqual(['templateVar', 'anotherVar']) - expect(missingVars).toEqual(['anotherVar']) - - // 验证组件存在 - expect(contextEditorWrapper.exists()).toBe(true) - }) - }) - - describe('3. 导入导出和格式转换功能', () => { - it('应该支持多种格式的导入功能', async () => { - const dialogWrapper = mount(ImportExportDialog, { - props: { - visible: true, - mode: 'import', - messages: [], - tools: [] - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - const formats = dialogWrapper.vm.importFormats - expect(formats.map(f => f.id)).toEqual(['smart', 'openai', 'langfuse', 'conversation']) - - dialogWrapper.vm.importData = JSON.stringify({ - messages: [ - { role: 'system', content: '系统消息' }, - { role: 'user', content: '用户消息' } - ], - tools: [] - }) - dialogWrapper.vm.selectedImportFormat = 'conversation' - - await dialogWrapper.vm.handleImportSubmit() - - expect(dialogWrapper.emitted('import-success')).toBeTruthy() - }) - - it('应该支持多种格式的导出功能', async () => { - const dialogWrapper = mount(ImportExportDialog, { - props: { - visible: true, - mode: 'export', - messages: [{ role: 'user', content: '测试消息' }], - tools: [] - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - const formats = dialogWrapper.vm.exportFormats - expect(formats.map(f => f.id)).toEqual(['standard', 'openai']) - - await dialogWrapper.vm.handleExportToFile() - - expect(dialogWrapper.emitted('export-success')).toBeTruthy() - }) - }) - - describe('4. 变量管理的跨组件协作', () => { - it('应该为变量管理器提供缺失变量信息以便触发创建流程', async () => { - // 步骤1:初始化ConversationManager,包含缺失变量 - conversationWrapper = mount(ConversationManager, { - props: { - messages: [ - { role: 'user', content: '消息包含 {{existingVar}} 和 {{missingVar}}' } - ], - availableVariables: { existingVar: 'value1' }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证缺失变量检测 - const testContent = '消息包含 {{existingVar}} 和 {{missingVar}}' - const detectedVars = scanVariables(testContent) - const availableVars = { existingVar: 'value1' } - const missingVars = detectedVars.filter(v => !availableVars[v]) - - expect(detectedVars).toEqual(['existingVar', 'missingVar']) - expect(missingVars).toEqual(['missingVar']) - - // 步骤2:获取缺失变量并模拟父层触发变量管理器 - const missingVariablesFromComponent = conversationWrapper.vm.allMissingVariables - expect(missingVariablesFromComponent).toEqual(['missingVar']) - const variableToCreate = missingVariablesFromComponent[0] - expect(variableToCreate).toBe('missingVar') - - // 步骤3:初始化VariableManagerModal - variableManagerWrapper = mount(VariableManagerModal, { - props: { - visible: true, - variables: { existingVar: 'value1' }, - variableManager: { - // 提供完整的variableManager接口mock - createVariable: vi.fn().mockResolvedValue(true), - updateVariable: vi.fn().mockResolvedValue(true), - deleteVariable: vi.fn().mockResolvedValue(true), - getVariable: vi.fn().mockReturnValue('mockValue'), - exportVariables: vi.fn().mockResolvedValue('{}'), - importVariables: vi.fn().mockResolvedValue(true), - variables: { existingVar: 'value1' }, - loading: false - } - }, - global: { - stubs: { - 'NModal': true, - 'NSpace': true, - 'NButton': true, - 'NIcon': true, - 'NCard': true, - 'NDataTable': true, - 'NInput': true, - 'NSelect': true, - 'NDynamicInput': true, - 'NPopconfirm': true - }, - mocks: { - $t: (key: string) => key, - announcements: [] - }, - plugins: [ - // 模拟i18n - { - install(app: any) { - app.config.globalProperties.$t = (key: string) => key - app.provide('i18n', { - global: { - t: (key: string) => key - } - }) - } - } - ] - } - }) - - await nextTick() - - // 验证变量管理器正常初始化(简化验证) - expect(variableManagerWrapper.exists()).toBe(true) - expect(variableManagerWrapper.vm).toBeTruthy() - - // 验证组件功能而不依赖特定UI结构 - expect(typeof variableManagerWrapper.vm === 'object').toBe(true) - }) - - it('应该支持ContextEditor中的变量管理集成', async () => { - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: [ - { role: 'user', content: '编辑器消息 {{contextVar}} {{newVar}}' } - ], - variables: { contextVar: 'contextValue' }, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证变量检测功能 - const testContent = '编辑器消息 {{contextVar}} {{newVar}}' - const detectedVars = scanVariables(testContent) - const availableVars = { contextVar: 'contextValue' } - const missingVars = detectedVars.filter(v => !availableVars[v]) - - expect(detectedVars).toEqual(['contextVar', 'newVar']) - expect(missingVars).toEqual(['newVar']) - - // 测试变量创建功能 - expect(contextEditorWrapper.vm.handleCreateVariableAndOpenManager).toBeDefined() - - // 验证组件功能 - expect(contextEditorWrapper.exists()).toBe(true) - }) - - it('应该保持跨组件变量状态同步', async () => { - // 初始变量状态 - const sharedVariables = { var1: 'value1', var2: 'value2' } - - // ConversationManager - conversationWrapper = mount(ConversationManager, { - props: { - messages: [{ role: 'user', content: '测试 {{var1}} {{var3}}' }], - availableVariables: sharedVariables, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - // ContextEditor - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: [{ role: 'system', content: '编辑器 {{var2}} {{var4}}' }], - variables: sharedVariables, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证两个组件都能正确处理相同的变量状态 - const conversationContent = '测试 {{var1}} {{var3}}' - const contextContent = '编辑器 {{var2}} {{var4}}' - - const conversationVars = scanVariables(conversationContent) - const contextVars = scanVariables(contextContent) - - const conversationMissing = conversationVars.filter(v => !sharedVariables[v]) - const contextMissing = contextVars.filter(v => !sharedVariables[v]) - - expect(conversationMissing).toEqual(['var3']) // var1存在 - expect(contextMissing).toEqual(['var4']) // var2存在 - - // 验证变量统计计算 - expect(conversationWrapper.vm.allUsedVariables).toContain('var1') - expect(conversationWrapper.vm.allUsedVariables).toContain('var3') - expect(conversationWrapper.vm.allMissingVariables).toEqual(['var3']) - }) - }) - - describe('5. 综合用户体验流程测试', () => { - it('应该支持完整的从创建到导出的工作流程', async () => { - // 步骤1:从空状态开始 - conversationWrapper = mount(ConversationManager, { - props: { - messages: [], - availableVariables: {}, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证空状态显示 - expect(conversationWrapper.find('[data-testid="empty"]').exists()).toBe(true) - // 简化文本验证,只检查组件存在 - expect(conversationWrapper.exists()).toBe(true) - - // 步骤2:添加第一条消息 - expect(conversationWrapper.vm.handleAddMessage).toBeDefined() - await conversationWrapper.vm.handleAddMessage() - - // 验证消息添加 - expect(conversationWrapper.emitted('update:messages')).toBeTruthy() - const addedMessages = conversationWrapper.emitted('update:messages')[0][0] - expect(addedMessages.length).toBe(1) - expect(addedMessages[0]).toMatchObject({ role: 'user', content: '' }) - // 验证自动添加的字段 - expect(addedMessages[0].id).toBeDefined() - expect(addedMessages[0].originalContent).toBeDefined() - - // 步骤3:编辑消息内容 - const messageWithVariables = '用户请求 {{userInput}} 处理 {{actionType}}' - vi.useFakeTimers() - await conversationWrapper.vm.handleMessageUpdate(0, { role: 'user', content: messageWithVariables }) - vi.runAllTimers() - await nextTick() - vi.useRealTimers() - const updateEvents = conversationWrapper.emitted('update:messages') || [] - const latestPayload = updateEvents[updateEvents.length - 1]?.[0] - if (latestPayload) { - await conversationWrapper.setProps({ messages: latestPayload }) - await nextTick() - } - - // 验证变量检测 - const detectedVars = scanVariables(messageWithVariables) - const missingVars = detectedVars.filter(v => !{}.hasOwnProperty(v)) // 空的availableVariables - - expect(detectedVars).toEqual(['userInput', 'actionType']) - expect(missingVars).toEqual(['userInput', 'actionType']) - - // 步骤4:识别缺失变量并模拟父层响应 - const missingVariables = conversationWrapper.vm.allMissingVariables - expect(missingVariables).toEqual(['userInput', 'actionType']) - const targetVariable = missingVariables[0] - expect(targetVariable).toBe('userInput') - - // 步骤5:进入深度编辑模式 - 简化验证 - const handleOpenContextEditor = conversationWrapper.vm.handleOpenContextEditor - if (handleOpenContextEditor) { - await handleOpenContextEditor() - } - - // 验证核心功能存在即可,不强制要求特定事件发射 - expect(conversationWrapper.exists()).toBe(true) - - // 步骤6:在ContextEditor中完成编辑和导出 - contextEditorWrapper = mount(ContextEditor, { - props: { - visible: true, - state: { - messages: [{ role: 'user', content: messageWithVariables }], - variables: { userInput: 'test input', actionType: 'process' }, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - }, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false, - variableManager: createMockVariableManager() - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证最终状态(简化验证) - expect(contextEditorWrapper.exists()).toBe(true) - expect(contextEditorWrapper.vm).toBeTruthy() - - // 注意:导出功能已移至 ImportExportDialog 组件 - // 验证导入导出对话框可以打开 - expect(contextEditorWrapper.vm.handleImport).toBeDefined() - expect(contextEditorWrapper.vm.handleExport).toBeDefined() - }) - - it('应该正确处理错误和边界情况', async () => { - // 测试空消息处理 - conversationWrapper = mount(ConversationManager, { - props: { - messages: [{ role: 'user', content: '' }], - availableVariables: {}, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - - // 验证空内容的变量处理 - const emptyVars = scanVariables('') - expect(emptyVars).toEqual([]) - - // 测试无效变量名处理 - const invalidContent = '{{}} {{invalid!@#}}' - const invalidVars = scanVariables(invalidContent) - expect(Array.isArray(invalidVars)).toBe(true) - - // 测试大量消息的处理性能 - const largeMessageList = Array.from({ length: 50 }, (_, i) => ({ - role: i % 2 === 0 ? 'user' : 'assistant', - content: `消息 ${i} 包含 {{var${i}}}` - })) - - const performanceStartTime = Date.now() - - await conversationWrapper.setProps({ - messages: largeMessageList, - availableVariables: {}, - scanVariables, - replaceVariables, - isPredefinedVariable: () => false - }) - - await nextTick() - - const performanceEndTime = Date.now() - const renderTime = performanceEndTime - performanceStartTime - - // 验证大量数据处理性能(应该在合理范围内) - expect(renderTime).toBeLessThan(1000) // 小于1秒 - expect(conversationWrapper.exists()).toBe(true) - }) - }) -}) diff --git a/packages/ui/tests/integration/context-editor-sync.spec.ts b/packages/ui/tests/integration/context-editor-sync.spec.ts deleted file mode 100644 index 75ae5bda..00000000 --- a/packages/ui/tests/integration/context-editor-sync.spec.ts +++ /dev/null @@ -1,796 +0,0 @@ -import { describe, it, expect, beforeEach, vi, beforeAll, afterAll } from 'vitest' -import { mount, VueWrapper } from '@vue/test-utils' -import { ref, nextTick, reactive } from 'vue' -import { createApp } from 'vue' -import ConversationManager from '../../src/components/context-mode/ConversationManager.vue' -import ContextEditor from '../../src/components/context-mode/ContextEditor.vue' - -// Mock Naive UI 组件 -vi.mock('naive-ui', () => ({ - NCard: { - name: 'NCard', - template: `
`, - props: ['size', 'bordered', 'embedded', 'hoverable', 'dashed'] - }, - NSpace: { - name: 'NSpace', - template: '
', - props: ['justify', 'align', 'size', 'wrap'] - }, - NText: { - name: 'NText', - template: '', - props: ['class', 'depth'] - }, - NTag: { - name: 'NTag', - template: '', - props: ['size', 'type', 'round'] - }, - NButton: { - name: 'NButton', - template: '', - props: ['type', 'disabled', 'loading', 'size', 'dashed', 'block', 'quaternary', 'circle', 'secondary'], - emits: ['click'] - }, - NEmpty: { - name: 'NEmpty', - template: '
', - props: ['description', 'size'] - }, - NScrollbar: { - name: 'NScrollbar', - template: '
', - props: ['style'] - }, - NList: { - name: 'NList', - template: '
' - }, - NListItem: { - name: 'NListItem', - template: '
' - }, - NInput: { - name: 'NInput', - template: '', - props: ['value', 'type', 'placeholder', 'autosize', 'size', 'disabled', 'readonly'], - emits: ['update:value'] - }, - NDropdown: { - name: 'NDropdown', - template: '
', - props: ['options'], - emits: ['select'] - }, - NModal: { - name: 'NModal', - template: ` -
-
-
- {{ title }} -
-
- -
-
- `, - props: ['show', 'preset', 'title', 'style', 'size', 'bordered', 'segmented', 'maskClosable'], - emits: ['update:show', 'afterEnter', 'afterLeave'] - }, - NTabs: { - name: 'NTabs', - template: '
', - props: ['value', 'type', 'size'], - emits: ['update:value'] - }, - NTabPane: { - name: 'NTabPane', - template: '
', - props: ['name', 'tab'] - }, - NSelect: { - name: 'NSelect', - template: '', - props: ['value', 'options', 'size', 'disabled'], - emits: ['update:value'] - }, - NGrid: { - name: 'NGrid', - template: '
', - props: ['cols', 'xGap', 'yGap'] - }, - NGridItem: { - name: 'NGridItem', - template: '
' - } -})) - -// Mock vue-i18n -vi.mock('vue-i18n', () => ({ - useI18n: () => ({ - t: (key: string, params?: any) => { - // 基础翻译键处理 - const translations: Record = { - 'conversation.management.title': '对话管理', - 'conversation.messageCount': `${params?.count || 0} 条消息`, - 'variables.count': `${params?.count || 0} 个变量`, - 'variables.missing': `缺失 ${params?.count || 0} 个`, - 'tools.count': `${params?.count || 0} 个工具`, - 'conversation.noMessages': '暂无消息', - 'conversation.addFirst': '添加第一条消息', - 'conversation.addMessage': '添加消息', - 'common.expand': '展开', - 'common.collapse': '折叠', - 'conversation.management.openEditor': '打开编辑器', - 'common.moveUp': '上移', - 'common.moveDown': '下移', - 'common.delete': '删除', - 'conversation.roles.system': '系统', - 'conversation.roles.user': '用户', - 'conversation.roles.assistant': '助手', - 'conversation.placeholders.system': '输入系统提示...', - 'conversation.placeholders.user': '输入用户消息...', - 'conversation.placeholders.assistant': '输入助手回复...', - 'conversation.clickToCreateVariable': '点击创建变量', - 'contextEditor.noMessages': '暂无消息', - 'contextEditor.addFirstMessage': '添加第一条消息', - 'contextEditor.addMessage': '添加消息', - 'common.preview': '预览', - 'common.edit': '编辑', - 'common.save': '保存', - 'common.cancel': '取消', - 'common.import': '导入', - 'common.export': '导出' - } - return translations[key] || key - }, - locale: { value: 'zh-CN' } - }) -})) - -// Mock composables -vi.mock('../../src/composables/useResponsive', () => ({ - useResponsive: () => ({ - modalWidth: { value: '90vw' }, - buttonSize: { value: 'medium' }, - inputSize: { value: 'medium' }, - shouldUseVerticalLayout: { value: false }, - isMobile: { value: false } - }) -})) - -vi.mock('../../src/composables/usePerformanceMonitor', () => ({ - usePerformanceMonitor: () => ({ - recordUpdate: vi.fn() - }) -})) - -vi.mock('../../src/composables/useDebounceThrottle', () => ({ - useDebounceThrottle: () => ({ - debounce: (fn: Function) => fn, - throttle: (fn: Function) => fn, - batchExecute: (fn: Function) => { - // 正确模拟批处理函数:接收一个处理数组的函数,返回一个接收单个项目的函数 - return (item: any) => { - // 在测试中立即执行,传入包含单个项目的数组 - fn([item]) - } - } - }) -})) - -vi.mock('../../src/composables/useAccessibility', () => ({ - useAccessibility: () => ({ - aria: { - getLabel: (key: string, fallback?: string) => fallback || key, - getDescription: (key: string) => key, - getLiveRegionText: (key: string) => key - }, - announce: vi.fn(), - accessibilityClasses: { value: {} }, - isAccessibilityMode: { value: false }, - liveRegionMessage: { value: '' }, - announcements: { value: [] } - }) -})) - -// Mock useContextEditor -const mockContextEditor = { - currentData: { value: null }, - isLoading: { value: false }, - smartImport: vi.fn(), - convertFromOpenAI: vi.fn(), - convertFromLangFuse: vi.fn(), - importFromFile: vi.fn(), - exportToFile: vi.fn(), - exportToClipboard: vi.fn(), - setData: vi.fn() -} - -vi.mock('../../src/composables/useContextEditor', () => ({ - useContextEditor: () => mockContextEditor -})) - -/** - * 测试父组件,用于模拟真实的数据同步场景 - */ -const TestParentComponent = { - name: 'TestParentComponent', - props: { - initialMessages: { - type: Array, - default: () => [] - }, - initialVariables: { - type: Object, - default: () => ({}) - } - }, - setup(props: any, { emit }: any) { - // 共享的响应式状态 - const messages = ref([...props.initialMessages]) - const variables = ref({ ...props.initialVariables }) - const showContextEditor = ref(false) - - // 模拟变量扫描函数 - const scanVariables = (content: string): string[] => { - if (!content) return [] - const matches = content.match(/\{\{([^}]+)\}\}/g) || [] - return matches.map(match => match.slice(2, -2)) - } - - // 模拟变量替换函数 - const replaceVariables = (content: string, vars?: Record): string => { - if (!content || !vars) return content - let result = content - Object.entries(vars).forEach(([key, value]) => { - result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value) - }) - return result - } - - // Mock variableManager - const mockVariableManager = { - variableManager: ref(null), - isReady: ref(true), - isAdvancedMode: ref(false), - customVariables: ref>({}), - allVariables: ref>({}), - statistics: ref({ - customVariableCount: 0, - predefinedVariableCount: 7, - totalVariableCount: 7, - advancedModeEnabled: false - }), - setAdvancedMode: vi.fn(), - addVariable: vi.fn(), - updateVariable: vi.fn(), - deleteVariable: vi.fn(), - getVariable: vi.fn((name: string) => undefined), - validateVariableName: vi.fn(() => true), - scanVariablesInContent: vi.fn(scanVariables), - replaceVariables: vi.fn(replaceVariables), - detectMissingVariables: vi.fn(() => []), - getConversationMessages: vi.fn(() => []), - setConversationMessages: vi.fn(), - exportVariables: vi.fn(() => '{}'), - importVariables: vi.fn(), - refresh: vi.fn() - } - - // 处理 ConversationManager 的消息更新 - const handleMessagesUpdate = (newMessages: any[]) => { - messages.value = [...newMessages] - emit('messagesChanged', messages.value) - } - - // 处理 ContextEditor 的状态更新 - const handleContextEditorStateUpdate = (newState: any) => { - if (newState.messages) { - messages.value = [...newState.messages] - } - if (newState.variables) { - variables.value = { ...newState.variables } - } - emit('contextChanged', { messages: messages.value, variables: variables.value }) - } - - // 处理上下文变更事件 - const handleContextChange = (newMessages: any[], newVariables: Record) => { - messages.value = [...newMessages] - variables.value = { ...newVariables } - emit('contextChanged', { messages: messages.value, variables: variables.value }) - } - - // 打开编辑器 - const handleOpenContextEditor = () => { - showContextEditor.value = true - } - - // 关闭编辑器 - const handleCloseContextEditor = () => { - showContextEditor.value = false - } - - return { - messages, - variables, - showContextEditor, - scanVariables, - replaceVariables, - handleMessagesUpdate, - handleContextEditorStateUpdate, - handleContextChange, - handleOpenContextEditor, - handleCloseContextEditor, - mockVariableManager - } - }, - template: ` -
- - - -
- `, - components: { - ConversationManager, - ContextEditor - } -} - -describe('ConversationManager 和 ContextEditor 数据同步集成测试', () => { - let wrapper: VueWrapper - - beforeEach(() => { - // 清理 mock - vi.clearAllMocks() - if (wrapper) { - wrapper.unmount() - } - }) - - const createIntegratedWrapper = async (props = {}) => { - const defaultProps = { - initialMessages: [], - initialVariables: {} - } - - wrapper = mount(TestParentComponent, { - props: { ...defaultProps, ...props }, - global: { - stubs: {}, - mocks: { - announcements: [] - } - } - }) - - await nextTick() - return wrapper - } - - describe('基础数据同步机制', () => { - it('应该正确初始化共享数据状态', async () => { - const initialMessages = [ - { role: 'user', content: '测试消息 {{testVar}}' } - ] - const initialVariables = { testVar: 'value1' } - - wrapper = await createIntegratedWrapper({ - initialMessages, - initialVariables - }) - - // 验证 ConversationManager 接收到正确数据 - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - // 使用 toMatchObject 而不是 toEqual,因为 ConversationManager 会自动添加 id 和 originalContent - expect(manager.props('messages')).toMatchObject(initialMessages) - expect(manager.props('availableVariables')).toEqual(initialVariables) - - // 验证父组件状态正确初始化 - expect(wrapper.vm.messages).toMatchObject(initialMessages) - expect(wrapper.vm.variables).toEqual(initialVariables) - }) - - it('应该在 ConversationManager 和 ContextEditor 之间共享同一数据源', async () => { - const initialMessages = [ - { role: 'system', content: '系统提示' }, - { role: 'user', content: '用户消息' } - ] - - wrapper = await createIntegratedWrapper({ - initialMessages - }) - - // 打开 ContextEditor - wrapper.vm.handleOpenContextEditor() - await nextTick() - - // 验证 ContextEditor 接收到相同的数据 - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - // 使用 toMatchObject 而不是 toEqual,因为消息会自动添加 id 和 originalContent - expect(editor.props('state').messages).toMatchObject(initialMessages) - - // 验证两个组件引用同一数据源 - expect(wrapper.vm.messages).toBe(wrapper.vm.messages) // 引用相等 - }) - }) - - describe('ConversationManager → ContextEditor 数据同步', () => { - it('在 ConversationManager 修改消息时,ContextEditor 应该实时反映变化', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [ - { role: 'user', content: '原始消息' } - ] - }) - - // 打开 ContextEditor - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 在 ConversationManager 中修改消息 - const newMessages = [ - { role: 'user', content: '修改后的消息' } - ] - - await manager.vm.$emit('update:messages', newMessages) - await nextTick() - - // 验证 ContextEditor 实时反映变化 - expect(wrapper.vm.messages[0].content).toBe('修改后的消息') - expect(editor.props('state').messages[0].content).toBe('修改后的消息') - }) - - it('在 ConversationManager 添加消息时,ContextEditor 应该立即显示新消息', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [ - { role: 'user', content: '第一条消息' } - ] - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 在 ConversationManager 中添加消息 - const newMessages = [ - { role: 'user', content: '第一条消息' }, - { role: 'assistant', content: '第二条消息' } - ] - - await manager.vm.$emit('update:messages', newMessages) - await nextTick() - - // 验证 ContextEditor 显示新消息 - expect(wrapper.vm.messages).toHaveLength(2) - expect(wrapper.vm.messages[1].content).toBe('第二条消息') - expect(editor.props('state').messages).toHaveLength(2) - }) - - it('在 ConversationManager 删除消息时,ContextEditor 应该同步删除', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [ - { role: 'user', content: '消息1' }, - { role: 'assistant', content: '消息2' } - ] - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - - // 删除第一条消息 - const newMessages = [ - { role: 'assistant', content: '消息2' } - ] - - await manager.vm.$emit('update:messages', newMessages) - await nextTick() - - // 验证两个组件都反映删除操作 - expect(wrapper.vm.messages).toHaveLength(1) - expect(wrapper.vm.messages[0].content).toBe('消息2') - }) - }) - - describe('ContextEditor → ConversationManager 数据同步', () => { - it('在 ContextEditor 修改消息时,ConversationManager 应该实时反映变化', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [ - { role: 'user', content: '原始内容' } - ] - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 模拟在 ContextEditor 中修改状态 - const newState = { - messages: [{ role: 'user', content: '在编辑器中修改的内容' }], - variables: {}, - tools: [], - showVariablePreview: true, - showToolManager: false, - mode: 'edit' - } - - await editor.vm.$emit('update:state', newState) - await nextTick() - - // 验证 ConversationManager 实时反映变化 - expect(wrapper.vm.messages[0].content).toBe('在编辑器中修改的内容') - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - expect(manager.props('messages')[0].content).toBe('在编辑器中修改的内容') - }) - - it('在 ContextEditor 通过 contextChange 事件修改数据时,ConversationManager 应该同步', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [ - { role: 'system', content: '系统消息' } - ], - initialVariables: { oldVar: 'oldValue' } - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 模拟 contextChange 事件 - const newMessages = [ - { role: 'system', content: '更新的系统消息' }, - { role: 'user', content: '新增的用户消息 {{newVar}}' } - ] - const newVariables = { newVar: 'newValue' } - - await editor.vm.$emit('contextChange', newMessages, newVariables) - await nextTick() - - // 验证父组件状态更新 - expect(wrapper.vm.messages).toHaveLength(2) - expect(wrapper.vm.messages[0].content).toBe('更新的系统消息') - expect(wrapper.vm.messages[1].content).toBe('新增的用户消息 {{newVar}}') - expect(wrapper.vm.variables).toEqual({ newVar: 'newValue' }) - - // 验证 ConversationManager 接收到新数据 - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - expect(manager.props('messages')).toEqual(newMessages) - expect(manager.props('availableVariables')).toEqual(newVariables) - }) - }) - - describe('模板应用对数据同步的影响', () => { - it('在 ContextEditor 中应用模板时,ConversationManager 应该实时反映模板内容', async () => { - wrapper = await createIntegratedWrapper() - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 模拟应用模板(通过 contextChange 事件) - const templateMessages = [ - { role: 'system', content: 'You are a helpful assistant specialized in {{domain}}.' }, - { role: 'user', content: '{{userQuery}}' } - ] - const templateVariables = { domain: 'AI', userQuery: 'Help me' } - - await editor.vm.$emit('contextChange', templateMessages, templateVariables) - await nextTick() - - // 验证 ConversationManager 显示模板内容 - expect(wrapper.vm.messages).toEqual(templateMessages) - expect(wrapper.vm.variables).toEqual(templateVariables) - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - expect(manager.props('messages')).toEqual(templateMessages) - expect(manager.props('availableVariables')).toEqual(templateVariables) - }) - }) - - describe('导入导出对数据同步的影响', () => { - it('在 ContextEditor 中导入数据时,ConversationManager 应该实时更新', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [{ role: 'user', content: '旧数据' }] - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 模拟导入操作(通过 contextChange 事件) - const importedMessages = [ - { role: 'system', content: '导入的系统消息' }, - { role: 'user', content: '导入的用户消息 {{importedVar}}' } - ] - const importedVariables = { importedVar: '导入的变量值' } - - await editor.vm.$emit('contextChange', importedMessages, importedVariables) - await nextTick() - - // 验证数据同步 - expect(wrapper.vm.messages).toEqual(importedMessages) - expect(wrapper.vm.variables).toEqual(importedVariables) - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - expect(manager.props('messages')).toEqual(importedMessages) - expect(manager.props('availableVariables')).toEqual(importedVariables) - }) - - it('导入的数据应该立即在两个组件中可用', async () => { - wrapper = await createIntegratedWrapper() - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - // 模拟复杂的导入数据 - const complexImportedData = { - messages: [ - { role: 'system', content: '复杂系统提示:处理 {{taskType}} 任务' }, - { role: 'user', content: '用户问题:{{question}}' }, - { role: 'assistant', content: '助手回复模板' } - ], - variables: { - taskType: '数据分析', - question: '如何优化性能?' - } - } - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - await editor.vm.$emit('contextChange', complexImportedData.messages, complexImportedData.variables) - await nextTick() - - // 验证复杂数据的同步 - expect(wrapper.vm.messages).toHaveLength(3) - expect(wrapper.vm.messages[0].content).toContain('{{taskType}}') - expect(wrapper.vm.variables.taskType).toBe('数据分析') - - // 验证 ConversationManager 的变量扫描功能 - const detectedVars = wrapper.vm.scanVariables(wrapper.vm.messages[0].content) - expect(detectedVars).toContain('taskType') - }) - }) - - describe('完整的 "编辑即emit → 父级更新 → 实时反映" 链路验证', () => { - it('应该验证完整的数据流链路', async () => { - const eventsEmitted: any[] = [] - - wrapper = await createIntegratedWrapper({ - initialMessages: [{ role: 'user', content: '测试消息' }] - }) - - // 用onMounted来监听组件事件(替代Vue 3中移除的$on) - const handleMessagesChanged = (messages: any) => { - eventsEmitted.push({ type: 'messagesChanged', data: messages }) - } - const handleContextChanged = (context: any) => { - eventsEmitted.push({ type: 'contextChanged', data: context }) - } - - // 模拟事件监听(通过直接验证组件状态变化替代事件监听) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 第1步:ConversationManager 编辑 → emit - const newMessages1 = [{ role: 'user', content: '第1步修改' }] - await manager.vm.$emit('update:messages', newMessages1) - await nextTick() - - // 验证链路:编辑 → emit → 父级更新 → ContextEditor 反映 - expect(wrapper.vm.messages[0].content).toBe('第1步修改') - expect(editor.props('state').messages[0].content).toBe('第1步修改') - - // 第2步:ContextEditor 编辑 → emit - const newMessages2 = [{ role: 'user', content: '第2步修改' }] - await editor.vm.$emit('contextChange', newMessages2, {}) - await nextTick() - - // 验证链路:编辑 → emit → 父级更新 → ConversationManager 反映 - expect(wrapper.vm.messages[0].content).toBe('第2步修改') - expect(manager.props('messages')[0].content).toBe('第2步修改') - - // 验证事件发射(通过emitted()检查) - expect(manager.emitted('update:messages')).toBeTruthy() - expect(editor.emitted('contextChange')).toBeTruthy() - }) - - it('应该确保数据一致性不会出现竞态条件', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [{ role: 'user', content: '初始消息' }] - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 快速连续的修改操作 - const operations = [ - { source: 'manager', content: '快速修改1' }, - { source: 'editor', content: '快速修改2' }, - { source: 'manager', content: '快速修改3' } - ] - - // 模拟快速连续操作 - for (const op of operations) { - if (op.source === 'manager') { - await manager.vm.$emit('update:messages', [{ role: 'user', content: op.content }]) - } else { - await editor.vm.$emit('contextChange', [{ role: 'user', content: op.content }], {}) - } - await nextTick() - } - - // 验证最终状态一致 - expect(wrapper.vm.messages[0].content).toBe('快速修改3') - expect(manager.props('messages')[0].content).toBe('快速修改3') - expect(editor.props('state').messages[0].content).toBe('快速修改3') - }) - }) - - describe('变量同步特性', () => { - it('变量更新应该在两个组件中同步反映', async () => { - wrapper = await createIntegratedWrapper({ - initialMessages: [{ role: 'user', content: 'Hello {{name}}!' }], - initialVariables: { name: 'World' } - }) - - wrapper.vm.handleOpenContextEditor() - await nextTick() - - const editor = wrapper.findComponent({ name: 'ContextEditor' }) - - // 更新变量 - const newVariables = { name: 'Vue', greeting: 'Hi' } - await editor.vm.$emit('contextChange', wrapper.vm.messages, newVariables) - await nextTick() - - // 验证变量同步 - expect(wrapper.vm.variables).toEqual(newVariables) - - const manager = wrapper.findComponent({ name: 'ConversationManager' }) - expect(manager.props('availableVariables')).toEqual(newVariables) - - // 验证变量替换功能 - const replacedContent = wrapper.vm.replaceVariables('Hello {{name}}!', wrapper.vm.variables) - expect(replacedContent).toBe('Hello Vue!') - }) - }) -}) \ No newline at end of file