test: 新增 StartTask 组件单元测试 (11 cases)

覆盖场景:暂停调用 API 并更新状态、继续调用 continueSession API、
ONGOING 状态不误触发 continue、结束会话 API、多次暂停/继续完整流程
This commit is contained in:
2026-05-30 17:38:25 +08:00
parent 15eea25598
commit 7a55063174
+276
View File
@@ -0,0 +1,276 @@
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)
})
})
})