import { describe, it, expect, vi, beforeEach } from 'vitest' import { ref, reactive } from 'vue' // Mock API vi.mock('@/api/studySessions', () => ({ continueSession: vi.fn(), pauseSession: vi.fn(), endSession: vi.fn(), startOrContinueStudySession: vi.fn(), getSessionDetail: vi.fn(), })) vi.mock('@/api/reportFragments', () => ({ getFragmentsBySession: vi.fn(), updateFragments: vi.fn(), })) import { continueSession, pauseSession, endSession, startOrContinueStudySession, } from '@/api/studySessions' const mockedContinueSession = vi.mocked(continueSession) const mockedPauseSession = vi.mocked(pauseSession) const mockedEndSession = vi.mocked(endSession) const mockedStartOrContinue = vi.mocked(startOrContinueStudySession) // 模拟 StartTask.vue 中的核心状态和方法 function createSessionSimulator() { const taskInfo = reactive({ sessionNum: 'SESSION_001', sessionState: 'ONGOING' as string, taskName: '测试任务', taskNum: 'T001', startTime: '', endTime: '', lastStartTime: '', actualTime: 0, effectiveTime: 0, effectivenessRatio: '--', pointerPosition: 1500000, systemMessage: '', }) const timerRunning = ref(true) const clear = () => { timerRunning.value = false } const runCountdown = (duration: number) => { timerRunning.value = true } // 从 StartTask.vue 提取的 stopTimer 逻辑 const stopTimer = async () => { const res = await pauseSession(taskInfo.sessionNum) if (res.code === 200) { taskInfo.sessionState = 'PAUSED' } clear() } // 从 StartTask.vue 提取的 startTimer 逻辑 const startTimer = async () => { if (taskInfo.sessionState === 'PAUSED') { const res = await continueSession(taskInfo.sessionNum) if (res.code === 200) { // ElMessage.success("任务继续") } // loadTaskSession 模拟 const sessionRes = await startOrContinueStudySession(taskInfo.taskNum) Object.assign(taskInfo, sessionRes.data) } const duration = taskInfo.pointerPosition || 25 * 60 * 1000 runCountdown(duration) } // 从 StartTask.vue 提取的 endTimer 逻辑(简化版,跳过 confirm) const endTimer = async (content: string) => { if (!content) return const res = await endSession(taskInfo.sessionNum, content) if (res.code === 200) { if (res.message && res.message !== '请求成功') { // ElMessage.warning(res.message) } } clear() } return { taskInfo, timerRunning, stopTimer, startTimer, endTimer, clear } } describe('StartTask.vue 暂停/继续逻辑', () => { beforeEach(() => { vi.clearAllMocks() }) describe('stopTimer - 暂停', () => { it('调用 pauseSession API', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) const { stopTimer } = createSessionSimulator() await stopTimer() expect(mockedPauseSession).toHaveBeenCalledWith('SESSION_001') }) it('暂停成功后 sessionState 应更新为 PAUSED', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) const { taskInfo, stopTimer } = createSessionSimulator() expect(taskInfo.sessionState).toBe('ONGOING') await stopTimer() expect(taskInfo.sessionState).toBe('PAUSED') }) it('暂停后 timerRunning 应为 false', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) const { timerRunning, stopTimer } = createSessionSimulator() expect(timerRunning.value).toBe(true) await stopTimer() expect(timerRunning.value).toBe(false) }) }) describe('startTimer - 继续', () => { it('PAUSED 状态下点击开始,应调用 continueSession API', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedContinueSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedStartOrContinue.mockResolvedValue({ code: 200, data: { sessionNum: 'SESSION_001', sessionState: 'ONGOING', pointerPosition: 1500000, }, } as any) const { taskInfo, stopTimer, startTimer } = createSessionSimulator() // 先暂停 await stopTimer() expect(taskInfo.sessionState).toBe('PAUSED') // 再开始 await startTimer() expect(mockedContinueSession).toHaveBeenCalledWith('SESSION_001') }) it('PAUSED 状态下点击开始,应调用 loadTaskSession 刷新数据', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedContinueSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedStartOrContinue.mockResolvedValue({ code: 200, data: { sessionNum: 'SESSION_001', sessionState: 'ONGOING', pointerPosition: 1200000, }, } as any) const { taskInfo, stopTimer, startTimer } = createSessionSimulator() await stopTimer() await startTimer() expect(mockedStartOrContinue).toHaveBeenCalledWith('T001') expect(taskInfo.sessionState).toBe('ONGOING') }) it('ONGOING 状态下点击开始,不应调用 continueSession API', async () => { mockedStartOrContinue.mockResolvedValue({ code: 200, data: { sessionState: 'ONGOING', pointerPosition: 1500000 }, } as any) const { taskInfo, startTimer } = createSessionSimulator() // 直接在 ONGOING 状态调用 startTimer expect(taskInfo.sessionState).toBe('ONGOING') await startTimer() expect(mockedContinueSession).not.toHaveBeenCalled() }) it('开始后 timerRunning 应为 true', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedContinueSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedStartOrContinue.mockResolvedValue({ code: 200, data: { sessionState: 'ONGOING', pointerPosition: 1500000 }, } as any) const { timerRunning, stopTimer, startTimer } = createSessionSimulator() await stopTimer() expect(timerRunning.value).toBe(false) await startTimer() expect(timerRunning.value).toBe(true) }) }) describe('endTimer - 结束会话', () => { it('调用 endSession API 并传递 content', async () => { mockedEndSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) const { endTimer } = createSessionSimulator() await endTimer('学习总结内容') expect(mockedEndSession).toHaveBeenCalledWith('SESSION_001', '学习总结内容') }) it('后端返回 warning 消息时,message 应包含提示信息', async () => { mockedEndSession.mockResolvedValue({ code: 200, message: '本次有效学习时间不足10分钟,不计入总学习时间', } as any) const { endTimer } = createSessionSimulator() const res = await endSession('SESSION_001', '内容') expect(res.message).toBe('本次有效学习时间不足10分钟,不计入总学习时间') expect(res.message).not.toBe('请求成功') }) it('content 为空时不调用 API', async () => { const { endTimer } = createSessionSimulator() await endTimer('') expect(mockedEndSession).not.toHaveBeenCalled() }) }) describe('完整暂停→继续→暂停流程', () => { it('多次暂停/继续应正确更新状态和调用 API', async () => { mockedPauseSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedContinueSession.mockResolvedValue({ code: 200, message: '请求成功' } as any) mockedStartOrContinue.mockResolvedValue({ code: 200, data: { sessionState: 'ONGOING', pointerPosition: 1500000 }, } as any) const { taskInfo, stopTimer, startTimer } = createSessionSimulator() // 初始状态 expect(taskInfo.sessionState).toBe('ONGOING') // 第一次暂停 await stopTimer() expect(taskInfo.sessionState).toBe('PAUSED') expect(mockedPauseSession).toHaveBeenCalledTimes(1) // 继续 await startTimer() expect(taskInfo.sessionState).toBe('ONGOING') expect(mockedContinueSession).toHaveBeenCalledTimes(1) // 第二次暂停 await stopTimer() expect(taskInfo.sessionState).toBe('PAUSED') expect(mockedPauseSession).toHaveBeenCalledTimes(2) // 再次继续 await startTimer() expect(taskInfo.sessionState).toBe('ONGOING') expect(mockedContinueSession).toHaveBeenCalledTimes(2) }) }) })