UNPKG

track-dev-time

Version:

CLI tool to track your coding sessions with start, pause and stop commands. Outputs session data to a JSON file.

195 lines (157 loc) 5.86 kB
//@ts-nocheck import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import fs from "fs"; import path from "path"; import { ensureSessionStorage, getCurrentSession, saveSession, } from "../lib/storage"; import { cleanupTestData, createAndSaveSession, createPause, makeCurrentSession, mockLog, restoreMockLog, TEST_PATH, } from "./utils"; import * as storage from "../lib/storage.js"; let logSpy, errorSpy; describe("ensureSessionStorage", () => { beforeEach(() => { cleanupTestData(); storage.__setSessionPath(TEST_PATH); mockLog(logSpy, errorSpy); }); afterEach(() => { restoreMockLog(logSpy, errorSpy); }); it("creates the directory and sessions.json file with initial content", () => { ensureSessionStorage(TEST_PATH); expect(fs.existsSync(TEST_PATH)).toBe(true); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); expect(data.projectName).toBe(path.basename(process.cwd())); expect(Array.isArray(data.sessions)).toBe(true); expect(data.sessions.length).toBe(0); }); }); describe("getCurrentSession", () => { beforeEach(() => { cleanupTestData(); logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); }); afterEach(() => { restoreMockLog(logSpy, errorSpy); }); it("should return null if no session is open", () => { ensureSessionStorage(TEST_PATH); const currentSession = getCurrentSession(TEST_PATH); expect(currentSession).toBeNull(); }); it("should return the open session if one exists (without using saveSession)", () => { ensureSessionStorage(TEST_PATH); const data = { projectName: "test-project", sessions: [ { id: "session-1", start: new Date().toISOString(), pauses: [], end: null, duration: null, }, ], }; fs.writeFileSync(TEST_PATH, JSON.stringify(data, null, 2), "utf-8"); const currentSession = getCurrentSession(TEST_PATH); expect(currentSession).not.toBeNull(); expect(currentSession.id).toBe("session-1"); expect(currentSession.end).toBeNull(); }); }); describe("saveSession", () => { beforeEach(() => { cleanupTestData(); logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); }); afterEach(() => { restoreMockLog(logSpy, errorSpy); }); it("should add a new session on START action", () => { const currentSession = makeCurrentSession(); saveSession(currentSession, "START", TEST_PATH); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); const session = data.sessions[0]; expect(data.sessions.length).toBe(1); expect(session.id).toBe(currentSession.id); expect(session.start).toBe(currentSession.start); expect(Array.isArray(session.pauses)).toBe(true); expect(session.pauses.length).toBe(0); expect(session.end).toBe(null); expect(session.duration).toBe(null); }); it("should pause an existing session on PAUSE action", () => { const lastSession = createAndSaveSession(); const openPause = createPause(); lastSession.pauses.push(openPause); saveSession(lastSession, "PAUSE", TEST_PATH); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); const updatedSession = data.sessions.find((s) => s.id === lastSession.id); expect(updatedSession).toBeDefined(); expect(Array.isArray(updatedSession.pauses)).toBe(true); expect(updatedSession.pauses.length).toBeGreaterThan(0); const lastPause = updatedSession.pauses.at(-1); expect(lastPause.id).toBe(openPause.id); expect(lastPause.start).toBe(openPause.start); expect(lastPause.end).toBe(null); }); it("should close the session on END action without pauses in session", () => { const lastSession = createAndSaveSession(); const endDate = new Date( new Date(lastSession.start).getTime() + 60 * 60 * 1000 ); lastSession.end = endDate.toISOString(); lastSession.duration = (endDate - new Date(lastSession.start)) / 1000; saveSession(lastSession, "END", TEST_PATH); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); const session = data.sessions[0]; expect(session.end).toBe(endDate.toISOString()); expect(session.duration).toBe(3600); }); it("should close the session on END action with pauses active", () => { const lastSession = createAndSaveSession(); lastSession.pauses.push({ id: "pause1", start: new Date( new Date(lastSession.start).getTime() + 15 * 60 * 1000 ).toISOString(), end: null, }); saveSession(lastSession, "PAUSE", TEST_PATH); const endDate = new Date( new Date(lastSession.start).getTime() + 60 * 60 * 1000 ); lastSession.end = endDate.toISOString(); lastSession.duration = (endDate - new Date(lastSession.start)) / 1000; saveSession(lastSession, "END", TEST_PATH); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); const session = data.sessions[0]; expect(session.end).toBe(endDate.toISOString()); expect(session.duration).toBe(3600); const pause = session.pauses.find((p) => p.id === "pause1"); expect(pause).toBeDefined(); expect(pause.end).not.toBeNull(); }); it("should handle unknown action gracefully", () => { const session = makeCurrentSession(); saveSession(session, "INVALID", TEST_PATH); expect(logSpy).toHaveBeenCalledWith("Unknown action"); expect(logSpy).toHaveBeenCalledWith("Session saved!"); const data = JSON.parse(fs.readFileSync(TEST_PATH, "utf-8")); expect(Array.isArray(data.sessions)).toBe(true); expect(data.sessions.length).toBe(0); }); });