140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
import {describe, it, expect, beforeEach, vi} from 'vitest'
|
|
import {setActivePinia, createPinia} from 'pinia'
|
|
|
|
import {useTimeTrackingStore} from './timeTracking'
|
|
import type {ITimeEntry} from '@/modelTypes/ITimeEntry'
|
|
|
|
const {getAllMock, removeMock, authInfo} = vi.hoisted(() => ({
|
|
getAllMock: vi.fn(),
|
|
removeMock: vi.fn(),
|
|
authInfo: {value: {id: 7} as {id: number} | null},
|
|
}))
|
|
|
|
vi.mock('@/services/timeEntry', async importOriginal => {
|
|
const actual = await importOriginal<typeof import('@/services/timeEntry')>()
|
|
return {
|
|
...actual,
|
|
useTimeEntryService: () => ({
|
|
getAll: getAllMock,
|
|
remove: removeMock,
|
|
}),
|
|
}
|
|
})
|
|
|
|
vi.mock('@/stores/auth', () => ({
|
|
useAuthStore: () => ({
|
|
info: authInfo.value,
|
|
}),
|
|
}))
|
|
|
|
function entry(id: number, endTime: Date | null): ITimeEntry {
|
|
return {
|
|
id,
|
|
userId: 1,
|
|
taskId: 1,
|
|
projectId: 0,
|
|
startTime: new Date(),
|
|
endTime,
|
|
comment: '',
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
maxPermission: null,
|
|
}
|
|
}
|
|
|
|
describe('timeTracking store', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
getAllMock.mockReset()
|
|
removeMock.mockReset()
|
|
authInfo.value = {id: 7}
|
|
})
|
|
|
|
it('a running entry becomes the active timer', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.applyTimerEvent(entry(4, null))
|
|
expect(store.activeTimer?.id).toBe(4)
|
|
expect(store.hasActiveTimer).toBe(true)
|
|
})
|
|
|
|
it('a stopped entry clears the matching active timer', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.applyTimerEvent(entry(4, null))
|
|
store.applyTimerEvent(entry(4, new Date()))
|
|
expect(store.activeTimer).toBeNull()
|
|
})
|
|
|
|
it('a stop for a different timer leaves the active one alone', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.applyTimerEvent(entry(4, null))
|
|
store.applyTimerEvent(entry(5, new Date()))
|
|
expect(store.activeTimer?.id).toBe(4)
|
|
})
|
|
|
|
it('patches a stopped entry in the loaded list', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.browsedEntries = [entry(4, null), entry(5, null)]
|
|
const stopped = entry(4, new Date('2026-01-01T10:00:00Z'))
|
|
store.applyTimerEvent(stopped)
|
|
expect(store.browsedEntries.find((e: ITimeEntry) => e.id === 4)?.endTime).toEqual(stopped.endTime)
|
|
expect(store.browsedEntries).toHaveLength(2)
|
|
})
|
|
|
|
it('does not insert an unknown entry into the loaded list', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.browsedEntries = [entry(4, null)]
|
|
store.applyTimerEvent(entry(9, new Date()))
|
|
expect(store.browsedEntries).toHaveLength(1)
|
|
expect(store.browsedEntries.find((e: ITimeEntry) => e.id === 9)).toBeUndefined()
|
|
})
|
|
|
|
it('hydrates the active timer scoped to the current user', async () => {
|
|
getAllMock.mockResolvedValue({items: [entry(4, null)]})
|
|
|
|
const store = useTimeTrackingStore()
|
|
await store.hydrateActiveTimer()
|
|
|
|
expect(getAllMock).toHaveBeenCalledWith({
|
|
filter: 'user_id = 7 && end_time = null',
|
|
perPage: 1,
|
|
})
|
|
expect(store.activeTimer?.id).toBe(4)
|
|
})
|
|
|
|
it('clears the active timer when deleting the running entry', async () => {
|
|
removeMock.mockResolvedValue(undefined)
|
|
|
|
const store = useTimeTrackingStore()
|
|
store.browsedEntries = [entry(4, null), entry(5, new Date())]
|
|
store.applyTimerEvent(entry(4, null))
|
|
|
|
await store.removeEntry(4)
|
|
|
|
expect(removeMock).toHaveBeenCalledWith(4)
|
|
expect(store.browsedEntries.map((e: ITimeEntry) => e.id)).toEqual([5])
|
|
expect(store.activeTimer).toBeNull()
|
|
})
|
|
|
|
it('applyTimerDeletion drops the entry and clears the matching active timer', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.browsedEntries = [entry(4, null), entry(5, new Date())]
|
|
store.applyTimerEvent(entry(4, null))
|
|
|
|
store.applyTimerDeletion(4)
|
|
|
|
expect(store.browsedEntries.map((e: ITimeEntry) => e.id)).toEqual([5])
|
|
expect(store.activeTimer).toBeNull()
|
|
})
|
|
|
|
it('applyTimerDeletion of another entry leaves the active timer alone', () => {
|
|
const store = useTimeTrackingStore()
|
|
store.browsedEntries = [entry(4, null), entry(5, new Date())]
|
|
store.applyTimerEvent(entry(4, null))
|
|
|
|
store.applyTimerDeletion(5)
|
|
|
|
expect(store.browsedEntries.map((e: ITimeEntry) => e.id)).toEqual([4])
|
|
expect(store.activeTimer?.id).toBe(4)
|
|
})
|
|
})
|