From d8018dcc6887694bef751d3dc80b71bad948dcd8 Mon Sep 17 00:00:00 2001
From: linshen <32978552+linshenkx@users.noreply.github.com>
Date: Sun, 23 Nov 2025 21:45:41 +0800
Subject: [PATCH] =?UTF-8?q?test(ui):=20=E7=A7=BB=E9=99=A4=E8=BF=87?=
=?UTF-8?q?=E5=BA=A6=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
删除 context-editor-sync.spec.ts 和 context-editor-refactor.spec.ts,
这些测试存在以下问题:
- Mock 过度复杂,维护成本高
- 测试的是 mock 配置而非真实组件行为
- 本地与 CI 环境因模块缓存差异导致结果不一致
---
.../tests/e2e/context-editor-refactor.spec.ts | 951 ------------------
.../integration/context-editor-sync.spec.ts | 796 ---------------
2 files changed, 1747 deletions(-)
delete mode 100644 packages/ui/tests/e2e/context-editor-refactor.spec.ts
delete mode 100644 packages/ui/tests/integration/context-editor-sync.spec.ts
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: ``,
- 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: `
-
- `,
- 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: `
-
-
-
- showContextEditor = visible"
- @update:state="handleContextEditorStateUpdate"
- @contextChange="handleContextChange"
- data-testid="context-editor"
- />
-
- `,
- 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