v2.0.0-preview.2 - 增加单元和UI测试用例
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { useTaskRowContextMenu } from '@/components/TaskList/composables/taskRow/useTaskRowContextMenu'
|
||||
import { createTask, createTimerTask } from '../../../fixtures/tasks'
|
||||
import { sleep } from '../helpers/mount'
|
||||
|
||||
describe('useTaskRowContextMenu', () => {
|
||||
let task: any
|
||||
let emit: any
|
||||
|
||||
beforeEach(() => {
|
||||
task = ref(createTask())
|
||||
emit = vi.fn()
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('右键菜单', () => {
|
||||
it('应该初始隐藏右键菜单', () => {
|
||||
const { contextMenuVisible } = useTaskRowContextMenu(task, emit)
|
||||
|
||||
expect(contextMenuVisible.value).toBe(false)
|
||||
})
|
||||
|
||||
it('handleContextMenu 应显示右键菜单', () => {
|
||||
// 明确指定为普通任务(非里程碑任务)
|
||||
task.value = createTask({ type: 'task' })
|
||||
|
||||
const { handleContextMenu, contextMenuVisible, contextMenuPosition } =
|
||||
useTaskRowContextMenu(task, emit)
|
||||
|
||||
const event = new MouseEvent('contextmenu', {
|
||||
clientX: 100,
|
||||
clientY: 200,
|
||||
})
|
||||
const preventDefault = vi.spyOn(event, 'preventDefault')
|
||||
|
||||
handleContextMenu(event)
|
||||
|
||||
expect(preventDefault).toHaveBeenCalled()
|
||||
expect(contextMenuVisible.value).toBe(true)
|
||||
expect(contextMenuPosition.value).toEqual({ x: 100, y: 200 })
|
||||
})
|
||||
|
||||
it('closeContextMenu 应隐藏右键菜单', () => {
|
||||
// 明确指定为普通任务
|
||||
task.value = createTask({ type: 'task' })
|
||||
|
||||
const { handleContextMenu, closeContextMenu, contextMenuVisible } =
|
||||
useTaskRowContextMenu(task, emit)
|
||||
|
||||
// 先显示
|
||||
const event = new MouseEvent('contextmenu')
|
||||
handleContextMenu(event)
|
||||
expect(contextMenuVisible.value).toBe(true)
|
||||
|
||||
// 再隐藏
|
||||
closeContextMenu()
|
||||
expect(contextMenuVisible.value).toBe(false)
|
||||
})
|
||||
|
||||
it('删除任务应触发 delete 事件', () => {
|
||||
// 明确指定为普通任务
|
||||
task.value = createTask({ type: 'task' })
|
||||
|
||||
const { handleTaskDelete } = useTaskRowContextMenu(task, emit)
|
||||
|
||||
handleTaskDelete(task.value)
|
||||
|
||||
expect(emit).toHaveBeenCalledWith('delete', task.value, undefined)
|
||||
})
|
||||
|
||||
it('contextMenuTask 应返回当前任务', () => {
|
||||
const { contextMenuTask } = useTaskRowContextMenu(task, emit)
|
||||
|
||||
expect(contextMenuTask.value).toBe(task.value)
|
||||
})
|
||||
})
|
||||
|
||||
describe('计时器格式化', () => {
|
||||
it('应该将毫秒格式化为 HH:MM:SS', async () => {
|
||||
task.value = createTimerTask({
|
||||
isTimerRunning: false,
|
||||
timerElapsedTime: 3661000, // 1小时1分1秒
|
||||
})
|
||||
|
||||
const { formattedTimer } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
expect(formattedTimer.value).toBe('01:01:01')
|
||||
})
|
||||
|
||||
it('应该正确格式化零时间', async () => {
|
||||
task.value = createTask({
|
||||
isTimerRunning: false,
|
||||
timerElapsedTime: 0,
|
||||
})
|
||||
|
||||
const { formattedTimer } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
expect(formattedTimer.value).toBe('00:00:00')
|
||||
})
|
||||
|
||||
it('应该正确格式化超过24小时的时间', async () => {
|
||||
task.value = createTask({
|
||||
isTimerRunning: false,
|
||||
timerElapsedTime: 90000000, // 25小时
|
||||
})
|
||||
|
||||
const { formattedTimer } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
expect(formattedTimer.value).toBe('25:00:00')
|
||||
})
|
||||
})
|
||||
|
||||
describe('计时器更新', () => {
|
||||
it('任务不在运行时应显示累计时间', async () => {
|
||||
task.value = createTask({
|
||||
isTimerRunning: false,
|
||||
timerElapsedTime: 5000, // 5秒
|
||||
})
|
||||
|
||||
const { timerElapsed } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
expect(timerElapsed.value).toBe(5000)
|
||||
})
|
||||
|
||||
it('任务运行中应实时更新时间', async () => {
|
||||
const now = Date.now()
|
||||
task.value = createTimerTask({
|
||||
isTimerRunning: true,
|
||||
timerStartTime: now - 3000, // 3秒前启动
|
||||
timerElapsedTime: 5000, // 之前累计5秒
|
||||
})
|
||||
|
||||
const { timerElapsed } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
// 应该是 5000 (之前) + 3000 (现在) = 8000ms
|
||||
expect(timerElapsed.value).toBeGreaterThanOrEqual(8000)
|
||||
expect(timerElapsed.value).toBeLessThan(9000)
|
||||
})
|
||||
|
||||
it('计时器启动时应每秒更新', async () => {
|
||||
const now = Date.now()
|
||||
task.value = createTimerTask({
|
||||
isTimerRunning: true,
|
||||
timerStartTime: now,
|
||||
timerElapsedTime: 0,
|
||||
})
|
||||
|
||||
const { timerElapsed } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
const initialValue = timerElapsed.value
|
||||
|
||||
// 推进1秒
|
||||
vi.advanceTimersByTime(1000)
|
||||
await nextTick()
|
||||
|
||||
// 时间应该增加约1秒
|
||||
expect(timerElapsed.value).toBeGreaterThan(initialValue)
|
||||
})
|
||||
|
||||
it('计时器停止时应停止更新', async () => {
|
||||
const now = Date.now()
|
||||
task.value = createTimerTask({
|
||||
isTimerRunning: true,
|
||||
timerStartTime: now,
|
||||
timerElapsedTime: 0,
|
||||
})
|
||||
|
||||
const { timerElapsed } = useTaskRowContextMenu(task, emit)
|
||||
await nextTick()
|
||||
|
||||
// 停止计时器
|
||||
task.value.isTimerRunning = false
|
||||
await nextTick()
|
||||
|
||||
const stoppedValue = timerElapsed.value
|
||||
|
||||
// 推进时间
|
||||
vi.advanceTimersByTime(5000)
|
||||
await nextTick()
|
||||
|
||||
// 值不应该变化
|
||||
expect(timerElapsed.value).toBe(stoppedValue)
|
||||
})
|
||||
})
|
||||
|
||||
describe('清理工作', () => {
|
||||
it('组件卸载时应清除计时器', () => {
|
||||
task.value = createTimerTask()
|
||||
|
||||
const result = useTaskRowContextMenu(task, emit)
|
||||
|
||||
// onUnmounted 会在组件卸载时自动清理
|
||||
// 这里只验证返回值包含必要的函数
|
||||
expect(result.timerElapsed).toBeDefined()
|
||||
expect(result.formattedTimer).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,377 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import { useTaskRowEventHandlers } from '@/components/TaskList/composables/taskRow/useTaskRowEventHandlers'
|
||||
import { createTask, createParentTask } from '../../../fixtures/tasks'
|
||||
|
||||
describe('useTaskRowEventHandlers', () => {
|
||||
let task: any
|
||||
let isStoryTask: any
|
||||
let hasChildren: any
|
||||
let isMilestoneGroup: any
|
||||
let isSplitterDragging: any
|
||||
let taskRowRef: any
|
||||
let enableDrag: any
|
||||
let onHover: any
|
||||
let emit: any
|
||||
let dragStart: any
|
||||
let dragOver: any
|
||||
|
||||
beforeEach(() => {
|
||||
task = ref(createTask())
|
||||
isStoryTask = ref(false)
|
||||
hasChildren = ref(false)
|
||||
isMilestoneGroup = ref(false)
|
||||
isSplitterDragging = ref(false)
|
||||
taskRowRef = ref(null)
|
||||
enableDrag = ref(true)
|
||||
onHover = vi.fn()
|
||||
emit = vi.fn()
|
||||
dragStart = vi.fn()
|
||||
dragOver = vi.fn()
|
||||
})
|
||||
|
||||
describe('handleToggle', () => {
|
||||
it('应该触发 toggle 事件', () => {
|
||||
const { handleToggle } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleToggle()
|
||||
|
||||
expect(emit).toHaveBeenCalledWith('toggle', task.value)
|
||||
})
|
||||
|
||||
it('未传入 emit 时不应报错', () => {
|
||||
const { handleToggle } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover
|
||||
)
|
||||
|
||||
expect(() => handleToggle()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleRowClick', () => {
|
||||
it('点击有子任务的行应触发 toggle', () => {
|
||||
hasChildren.value = true
|
||||
|
||||
const { handleRowClick } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleRowClick()
|
||||
|
||||
expect(emit).toHaveBeenCalledWith('toggle', task.value)
|
||||
})
|
||||
|
||||
it('点击 story 类型任务应触发 toggle', () => {
|
||||
isStoryTask.value = true
|
||||
|
||||
const { handleRowClick } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleRowClick()
|
||||
|
||||
expect(emit).toHaveBeenCalledWith('toggle', task.value)
|
||||
})
|
||||
|
||||
it('点击里程碑分组不应触发 toggle', () => {
|
||||
isMilestoneGroup.value = true
|
||||
hasChildren.value = true
|
||||
|
||||
const { handleRowClick } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleRowClick()
|
||||
|
||||
expect(emit).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('点击普通任务不应触发 toggle', () => {
|
||||
const { handleRowClick } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleRowClick()
|
||||
|
||||
expect(emit).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleTaskRowDoubleClick', () => {
|
||||
it('应该触发 dblclick 事件', () => {
|
||||
const { handleTaskRowDoubleClick } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
const event = new MouseEvent('dblclick', { bubbles: true })
|
||||
const stopPropagation = vi.spyOn(event, 'stopPropagation')
|
||||
|
||||
handleTaskRowDoubleClick(event)
|
||||
|
||||
expect(stopPropagation).toHaveBeenCalled()
|
||||
expect(emit).toHaveBeenCalledWith('dblclick', task.value)
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleMouseEnter / handleMouseLeave', () => {
|
||||
it('鼠标进入应调用 onHover 并传入任务ID', () => {
|
||||
task.value.id = 123
|
||||
|
||||
const { handleMouseEnter } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleMouseEnter()
|
||||
|
||||
expect(onHover).toHaveBeenCalledWith(123)
|
||||
})
|
||||
|
||||
it('鼠标离开应调用 onHover 并传入 null', () => {
|
||||
const { handleMouseLeave } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleMouseLeave()
|
||||
|
||||
expect(onHover).toHaveBeenCalledWith(null)
|
||||
})
|
||||
|
||||
it('Splitter 拖拽时应忽略鼠标进入事件', () => {
|
||||
isSplitterDragging.value = true
|
||||
|
||||
const { handleMouseEnter } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleMouseEnter()
|
||||
|
||||
expect(onHover).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('Splitter 拖拽时应忽略鼠标离开事件', () => {
|
||||
isSplitterDragging.value = true
|
||||
|
||||
const { handleMouseLeave } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
handleMouseLeave()
|
||||
|
||||
expect(onHover).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleMouseDown - 拖拽', () => {
|
||||
it('enableDrag 为 false 时不应启动拖拽', () => {
|
||||
enableDrag.value = false
|
||||
taskRowRef.value = document.createElement('div')
|
||||
|
||||
const { handleMouseDown } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit,
|
||||
dragStart,
|
||||
dragOver
|
||||
)
|
||||
|
||||
const event = new MouseEvent('mousedown')
|
||||
handleMouseDown(event)
|
||||
|
||||
expect(dragStart).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('enableDrag 为 true 时应启动拖拽', () => {
|
||||
enableDrag.value = true
|
||||
const mockElement = document.createElement('div')
|
||||
taskRowRef.value = mockElement
|
||||
|
||||
const { handleMouseDown } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit,
|
||||
dragStart,
|
||||
dragOver
|
||||
)
|
||||
|
||||
const mockDiv = document.createElement('div')
|
||||
const event = new MouseEvent('mousedown', { bubbles: true })
|
||||
Object.defineProperty(event, 'target', { value: mockDiv, enumerable: true })
|
||||
|
||||
handleMouseDown(event)
|
||||
|
||||
expect(dragStart).toHaveBeenCalledWith(task.value, mockElement, event)
|
||||
})
|
||||
|
||||
it('taskRowRef 为 null 时不应启动拖拽', () => {
|
||||
enableDrag.value = true
|
||||
taskRowRef.value = null
|
||||
|
||||
const { handleMouseDown } = useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit,
|
||||
dragStart,
|
||||
dragOver
|
||||
)
|
||||
|
||||
const mockDiv = document.createElement('div')
|
||||
const event = new MouseEvent('mousedown', { bubbles: true })
|
||||
Object.defineProperty(event, 'target', { value: mockDiv, enumerable: true })
|
||||
|
||||
handleMouseDown(event)
|
||||
|
||||
expect(dragStart).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Splitter 拖拽状态', () => {
|
||||
// 注意:Splitter 拖拽事件是通过 window 事件监听器注册的(onMounted 中)
|
||||
// 在单元测试中无法直接测试,需要通过组件集成测试来验证
|
||||
it.skip('handleSplitterDragStart 通过 window 事件触发', () => {
|
||||
useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
// 触发 window 事件
|
||||
window.dispatchEvent(new Event('splitter-drag-start'))
|
||||
|
||||
expect(isSplitterDragging.value).toBe(true)
|
||||
})
|
||||
|
||||
it.skip('handleSplitterDragEnd 通过 window 事件触发', () => {
|
||||
isSplitterDragging.value = true
|
||||
|
||||
useTaskRowEventHandlers(
|
||||
task,
|
||||
isStoryTask,
|
||||
hasChildren,
|
||||
isMilestoneGroup,
|
||||
isSplitterDragging,
|
||||
taskRowRef,
|
||||
enableDrag,
|
||||
onHover,
|
||||
emit
|
||||
)
|
||||
|
||||
// 触发 window 事件
|
||||
window.dispatchEvent(new Event('splitter-drag-end'))
|
||||
|
||||
expect(isSplitterDragging.value).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,287 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import { useTaskRowState } from '@/components/TaskList/composables/taskRow/useTaskRowState'
|
||||
import { createTask, createParentTask, createMilestone, createOvertimeTask, createOverdueTask } from '../../../fixtures/tasks'
|
||||
|
||||
describe('useTaskRowState', () => {
|
||||
describe('taskState - 基础状态计算', () => {
|
||||
it('应该正确计算普通任务的缩进', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const { indent } = useTaskRowState(task, level)
|
||||
|
||||
expect(indent.value).toBe('10px')
|
||||
})
|
||||
|
||||
it('应该根据层级计算正确的缩进', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(2)
|
||||
const { indent } = useTaskRowState(task, level)
|
||||
|
||||
// 10 + 2 * 20 = 50px
|
||||
expect(indent.value).toBe('50px')
|
||||
})
|
||||
|
||||
it('应该识别父任务(有子任务)', () => {
|
||||
const task = ref(createParentTask({ children: [createTask()] }))
|
||||
const level = ref(0)
|
||||
const { isParentTask, hasChildren } = useTaskRowState(task, level)
|
||||
|
||||
expect(isParentTask.value).toBe(true)
|
||||
expect(hasChildren.value).toBe(true)
|
||||
})
|
||||
|
||||
it('应该识别 story 类型任务为父任务', () => {
|
||||
const task = ref(createTask({ type: 'story' }))
|
||||
const level = ref(0)
|
||||
const { isStoryTask, isParentTask } = useTaskRowState(task, level)
|
||||
|
||||
expect(isStoryTask.value).toBe(true)
|
||||
expect(isParentTask.value).toBe(true)
|
||||
})
|
||||
|
||||
it('应该识别里程碑分组任务', () => {
|
||||
const task = ref(createTask({ type: 'milestone-group' }))
|
||||
const level = ref(0)
|
||||
const { isMilestoneGroup, isParentTask } = useTaskRowState(task, level)
|
||||
|
||||
expect(isMilestoneGroup.value).toBe(true)
|
||||
expect(isParentTask.value).toBe(true)
|
||||
})
|
||||
|
||||
it('应该识别里程碑任务', () => {
|
||||
const task = ref(createMilestone())
|
||||
const level = ref(0)
|
||||
const { isMilestoneTask } = useTaskRowState(task, level)
|
||||
|
||||
expect(isMilestoneTask.value).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('progressClass - 进度状态', () => {
|
||||
it('应该为新任务(进度0)返回空类名', () => {
|
||||
const tomorrow = new Date()
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
const task = ref(createTask({
|
||||
progress: 0,
|
||||
endDate: tomorrow.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { progressClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(progressClass.value).toBe('')
|
||||
})
|
||||
|
||||
it('应该为进行中的任务返回 warning 类名', () => {
|
||||
const tomorrow = new Date()
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
const task = ref(createTask({
|
||||
progress: 50,
|
||||
endDate: tomorrow.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { progressClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(progressClass.value).toBe('progress-warning')
|
||||
})
|
||||
|
||||
it('应该为完成的任务返回 success 类名', () => {
|
||||
const task = ref(createTask({ progress: 100 }))
|
||||
const level = ref(0)
|
||||
const { progressClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(progressClass.value).toBe('progress-success')
|
||||
})
|
||||
|
||||
it('应该为逾期未完成的任务返回 danger 类名', () => {
|
||||
const yesterday = new Date()
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
|
||||
const task = ref(createTask({
|
||||
progress: 50,
|
||||
endDate: yesterday.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { progressClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(progressClass.value).toBe('progress-danger')
|
||||
})
|
||||
|
||||
it('逾期但已完成的任务应返回 success 类名', () => {
|
||||
const yesterday = new Date()
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
|
||||
const task = ref(createTask({
|
||||
progress: 100,
|
||||
endDate: yesterday.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { progressClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(progressClass.value).toBe('progress-success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isOvertime - 超时判断', () => {
|
||||
it('应该识别超时任务', () => {
|
||||
const task = ref(createOvertimeTask())
|
||||
const level = ref(0)
|
||||
const { isOvertime } = useTaskRowState(task, level)
|
||||
|
||||
expect(isOvertime()).toBe(true)
|
||||
})
|
||||
|
||||
it('未超时任务应返回 false', () => {
|
||||
const task = ref(createTask({
|
||||
estimatedHours: 40,
|
||||
actualHours: 30
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { isOvertime } = useTaskRowState(task, level)
|
||||
|
||||
expect(isOvertime()).toBe(false)
|
||||
})
|
||||
|
||||
it('缺少工时数据应返回 undefined', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const { isOvertime } = useTaskRowState(task, level)
|
||||
|
||||
expect(isOvertime()).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('overdueDays - 逾期天数', () => {
|
||||
it('应该正确计算逾期天数', () => {
|
||||
const threeDaysAgo = new Date()
|
||||
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3)
|
||||
|
||||
const task = ref(createTask({
|
||||
progress: 50,
|
||||
endDate: threeDaysAgo.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { overdueDays } = useTaskRowState(task, level)
|
||||
|
||||
expect(overdueDays()).toBe(3)
|
||||
})
|
||||
|
||||
it('未逾期任务应返回 0', () => {
|
||||
const tomorrow = new Date()
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
|
||||
const task = ref(createTask({
|
||||
progress: 50,
|
||||
endDate: tomorrow.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { overdueDays } = useTaskRowState(task, level)
|
||||
|
||||
expect(overdueDays()).toBe(0)
|
||||
})
|
||||
|
||||
it('已完成任务即使逾期也应返回 0', () => {
|
||||
const yesterday = new Date()
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
|
||||
const task = ref(createTask({
|
||||
progress: 100,
|
||||
endDate: yesterday.toISOString().split('T')[0]
|
||||
}))
|
||||
const level = ref(0)
|
||||
const { overdueDays } = useTaskRowState(task, level)
|
||||
|
||||
expect(overdueDays()).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('customRowClass - 自定义行类名', () => {
|
||||
it('未配置时应返回空字符串', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const { customRowClass } = useTaskRowState(task, level)
|
||||
|
||||
expect(customRowClass.value).toBe('')
|
||||
})
|
||||
|
||||
it('应该支持字符串类名', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const rowIndex = ref(0)
|
||||
const taskListRowClassName = ref('custom-class')
|
||||
|
||||
const { customRowClass } = useTaskRowState(
|
||||
task,
|
||||
level,
|
||||
rowIndex,
|
||||
taskListRowClassName
|
||||
)
|
||||
|
||||
expect(customRowClass.value).toBe('custom-class')
|
||||
})
|
||||
|
||||
it('应该支持函数类名', () => {
|
||||
const task = ref(createTask({ id: 5 }))
|
||||
const level = ref(0)
|
||||
const rowIndex = ref(2)
|
||||
const taskListRowClassName = ref((row: any, index: number) => {
|
||||
return `task-${row.id}-row-${index}`
|
||||
})
|
||||
|
||||
const { customRowClass } = useTaskRowState(
|
||||
task,
|
||||
level,
|
||||
rowIndex,
|
||||
taskListRowClassName
|
||||
)
|
||||
|
||||
expect(customRowClass.value).toBe('task-5-row-2')
|
||||
})
|
||||
})
|
||||
|
||||
describe('customRowStyle - 自定义行样式', () => {
|
||||
it('未配置时应返回空对象', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const { customRowStyle } = useTaskRowState(task, level)
|
||||
|
||||
expect(customRowStyle.value).toEqual({})
|
||||
})
|
||||
|
||||
it('应该支持对象样式', () => {
|
||||
const task = ref(createTask())
|
||||
const level = ref(0)
|
||||
const rowIndex = ref(0)
|
||||
const taskListRowStyle = ref({ backgroundColor: 'red' })
|
||||
|
||||
const { customRowStyle } = useTaskRowState(
|
||||
task,
|
||||
level,
|
||||
rowIndex,
|
||||
undefined,
|
||||
taskListRowStyle
|
||||
)
|
||||
|
||||
expect(customRowStyle.value).toEqual({ backgroundColor: 'red' })
|
||||
})
|
||||
|
||||
it('应该支持函数样式', () => {
|
||||
const task = ref(createTask({ progress: 100 }))
|
||||
const level = ref(0)
|
||||
const rowIndex = ref(0)
|
||||
const taskListRowStyle = ref((row: any) => {
|
||||
return { backgroundColor: row.progress === 100 ? 'green' : 'yellow' }
|
||||
})
|
||||
|
||||
const { customRowStyle } = useTaskRowState(
|
||||
task,
|
||||
level,
|
||||
rowIndex,
|
||||
undefined,
|
||||
taskListRowStyle
|
||||
)
|
||||
|
||||
expect(customRowStyle.value).toEqual({ backgroundColor: 'green' })
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user