import { describe, it, expect, vi, beforeEach } from 'vitest' import { flushPromises, mount } from '@vue/test-utils' import ElementPlus, { ElMessage } from 'element-plus' import { createMemoryHistory, createRouter } from 'vue-router' import ReviewRecall from '@/components/ReviewRecall.vue' import { getStandardMindMap, listRecallRecords, recallCompare, } from '@/api/standardMindMap' vi.mock('@/api/standardMindMap', () => ({ findNode: vi.fn(), getStandardMindMap: vi.fn(), listRecallRecords: vi.fn(), recallCompare: vi.fn(), regenerateStandardMindMap: vi.fn(), updateStandardMindMap: vi.fn(), })) const ok = (data: any = null) => ({ code: 200, message: '请求成功', data }) const standardMap = { id: 1, taskNum: 'T001', title: '测试任务', content: JSON.stringify({ title: '根主题', children: [{ title: '子节点' }] }), outline: '根主题\n- 子节点', summary: '', generator: 'AI', sourceReportCount: 1, } /** MindMapViewer 依赖 mind-elixir(无法在 jsdom 运行),用可编程 stub 模拟导出的大纲 */ const toOutline = vi.fn((): string => '根主题\n- 子节点') const MindMapViewerStub = { name: 'MindMapViewerStub', props: ['modelValue', 'nodeSelectable', 'colorByCompare', 'readonly'], template: '
', methods: { toOutline }, } /** * 交互式集成测试:真实挂载 ReviewRecall, * 点击“提交对比”模拟回忆对比流,断言 API 参数与对比结果渲染。 */ describe('ReviewRecall 交互流程(集成)', () => { beforeEach(() => { vi.clearAllMocks() localStorage.clear() localStorage.setItem('isLoggedIn', 'true') vi.mocked(getStandardMindMap).mockResolvedValue(ok(standardMap)) vi.mocked(listRecallRecords).mockResolvedValue( ok([ { id: 11, taskNum: 'T001', standardMapId: 1, recallContent: '根主题', compareResult: JSON.stringify({ matchedTree: { title: '根主题', children: [] }, recallRatio: 0.6, matchedCount: 3, missedCount: 2, extraCount: 0, }), recallRatio: 0.6, matchedCount: 3, missedCount: 2, extraCount: 0, createdTime: '2026-08-27 10:00:00', }, ]), ) vi.mocked(recallCompare).mockResolvedValue(ok(standardMap)) }) const mountPage = async () => { const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/review/recall/:taskNum', component: ReviewRecall, meta: { wide: true } }], }) await router.push('/review/recall/T001') await router.isReady() const wrapper = mount(ReviewRecall, { global: { plugins: [ElementPlus, router], stubs: { ElTag: true, MindMapViewer: MindMapViewerStub }, }, }) await flushPromises() return { wrapper } } it('加载后展示标准导图标题,点击“提交对比”调用对比接口并渲染结果', async () => { const { wrapper } = await mountPage() // 默认折叠标准导图(防剧透),回忆面板可见 expect(wrapper.text()).toContain('你的回忆') const submit = wrapper.findAll('button').find((b) => b.text().trim() === '提交对比')! expect(submit).toBeTruthy() await submit.trigger('click') await flushPromises() await flushPromises() expect(recallCompare).toHaveBeenCalledWith('T001', '根主题\n- 子节点', undefined) expect(ElMessage.success).toHaveBeenCalledWith('回忆对比完成') // 对比完成后出现“清空重写”入口(v-if=hasCompared) expect(wrapper.findAll('button').some((b) => b.text().trim() === '清空重写')).toBe(true) wrapper.unmount() }) it('回忆导图内容为单行时点击“提交对比”,提示先添加节点且不调接口', async () => { const { wrapper } = await mountPage() toOutline.mockReturnValue('只有一个根') const submit = wrapper.findAll('button').find((b) => b.text().trim() === '提交对比')! await submit.trigger('click') await flushPromises() const { ElMessage: Msg } = await import('element-plus') expect(Msg.warning).toHaveBeenCalledWith('请先在回忆导图中添加节点(选中节点后按 Tab 加子节点)') expect(recallCompare).not.toHaveBeenCalled() wrapper.unmount() }) })