TickSimple v1.0.0 — Dead-simple time tracker for freelancers. One-tap timer, PDF reports, invoice generation, Chrome extension MV3.

This commit is contained in:
Bun Bun
2026-06-20 00:30:24 +00:00
commit c02d5ef2ae
37 changed files with 4689 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
// Mock chrome APIs for Node test environment
;(globalThis as any).chrome = {
storage: {
local: {
get: async () => ({}),
set: async () => {},
clear: async () => {},
},
},
runtime: {
sendMessage: async () => ({}),
onMessage: { addListener: () => {} },
onStartup: { addListener: () => {} },
onInstalled: { addListener: () => {} },
},
alarms: {
create: async () => {},
clear: async () => true,
onAlarm: { addListener: () => {} },
},
action: {
setBadgeText: async () => {},
setBadgeBackgroundColor: async () => {},
},
}
import { describe, it } from 'node:test'
import assert from 'node:assert'
import {
formatDuration,
formatDurationShort,
formatDate,
formatTime,
getTodayEntries,
getWeekEntries,
generateId,
} from '../src/storage.js'
describe('formatDuration', () => {
it('formats 0 ms', () => {
assert.strictEqual(formatDuration(0), '0h 00m')
})
it('formats 1 hour', () => {
assert.strictEqual(formatDuration(3600000), '1h 00m')
})
it('formats 90 minutes', () => {
assert.strictEqual(formatDuration(5400000), '1h 30m')
})
it('formats 25 minutes', () => {
assert.strictEqual(formatDuration(1500000), '0h 25m')
})
})
describe('formatDurationShort', () => {
it('formats 0 ms', () => {
assert.strictEqual(formatDurationShort(0), '0:00')
})
it('formats 1 hour 5 min', () => {
assert.strictEqual(formatDurationShort(3900000), '1:05')
})
})
describe('formatDate', () => {
it('formats a timestamp', () => {
const ts = new Date('2024-06-20T10:00:00Z').getTime()
const result = formatDate(ts)
assert.ok(result.includes('Jun') || result.includes('20'))
})
})
describe('formatTime', () => {
it('formats a timestamp', () => {
const ts = new Date('2024-06-20T10:30:00Z').getTime()
const result = formatTime(ts)
assert.ok(result.includes(':'))
})
})
describe('getTodayEntries', () => {
it('returns only today entries', () => {
const now = new Date()
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
const todayEntry = startOfDay + 3600000 // 1 hour after start of day
const yesterdayEntry = startOfDay - 3600000 // 1 hour before start of day
const entries = [
{ id: '1', startTime: todayEntry, endTime: todayEntry + 1800000, duration: 1800000, description: 'A', client: 'C', project: 'P', createdAt: todayEntry },
{ id: '2', startTime: yesterdayEntry, endTime: yesterdayEntry + 1800000, duration: 1800000, description: 'B', client: 'C', project: 'P', createdAt: yesterdayEntry },
]
const result = getTodayEntries(entries)
assert.strictEqual(result.length, 1)
assert.strictEqual(result[0].id, '1')
})
it('excludes running entries', () => {
const now = Date.now()
const entries = [
{ id: '1', startTime: now - 3600000, endTime: null, duration: 0, description: 'A', client: 'C', project: 'P', createdAt: now },
]
const result = getTodayEntries(entries)
assert.strictEqual(result.length, 0)
})
})
describe('getWeekEntries', () => {
it('returns entries from this week', () => {
const now = new Date()
const dayOfWeek = now.getDay()
const startOfWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - dayOfWeek)
const weekEntry = startOfWeek.getTime() + 3600000
const oldEntry = startOfWeek.getTime() - 86400000
const entries = [
{ id: '1', startTime: weekEntry, endTime: weekEntry + 1800000, duration: 1800000, description: 'A', client: 'C', project: 'P', createdAt: weekEntry },
{ id: '2', startTime: oldEntry, endTime: oldEntry + 1800000, duration: 1800000, description: 'B', client: 'C', project: 'P', createdAt: oldEntry },
]
const result = getWeekEntries(entries)
assert.strictEqual(result.length, 1)
assert.strictEqual(result[0].id, '1')
})
})
describe('generateId', () => {
it('generates unique ids', () => {
const id1 = generateId()
const id2 = generateId()
assert.notStrictEqual(id1, id2)
assert.ok(id1.length > 10)
})
})