feat: pure session clock with pause/resume
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
export function initialClock() {
|
||||
return { startedAt: null, pausedAt: null, pausedAccumMs: 0 };
|
||||
}
|
||||
|
||||
export function start(state, now) {
|
||||
if (state.startedAt != null) return state;
|
||||
return { startedAt: now, pausedAt: null, pausedAccumMs: 0 };
|
||||
}
|
||||
|
||||
export function pause(state, now) {
|
||||
if (state.startedAt == null || state.pausedAt != null) return state;
|
||||
return { ...state, pausedAt: now };
|
||||
}
|
||||
|
||||
export function resume(state, now) {
|
||||
if (state.pausedAt == null) return state;
|
||||
return { ...state, pausedAt: null, pausedAccumMs: state.pausedAccumMs + (now - state.pausedAt) };
|
||||
}
|
||||
|
||||
export function elapsedMs(state, now) {
|
||||
if (state.startedAt == null) return 0;
|
||||
const end = state.pausedAt != null ? state.pausedAt : now;
|
||||
return Math.max(0, end - state.startedAt - state.pausedAccumMs);
|
||||
}
|
||||
|
||||
export function isRunning(state) { return state.startedAt != null && state.pausedAt == null; }
|
||||
|
||||
export function isPaused(state) { return state.pausedAt != null; }
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { initialClock, start, pause, resume, elapsedMs, isRunning, isPaused } from '../../src/core/clock.js';
|
||||
|
||||
describe('session clock', () => {
|
||||
it('reads zero before start', () => {
|
||||
const c = initialClock();
|
||||
expect(elapsedMs(c, 1000)).toBe(0);
|
||||
expect(isRunning(c)).toBe(false);
|
||||
});
|
||||
|
||||
it('measures elapsed from start', () => {
|
||||
const c = start(initialClock(), 1000);
|
||||
expect(isRunning(c)).toBe(true);
|
||||
expect(elapsedMs(c, 61000)).toBe(60000); // 1 minute
|
||||
});
|
||||
|
||||
it('freezes while paused and excludes paused time after resume', () => {
|
||||
let c = start(initialClock(), 0);
|
||||
c = pause(c, 60000); // paused at 1:00
|
||||
expect(isPaused(c)).toBe(true);
|
||||
expect(elapsedMs(c, 120000)).toBe(60000); // frozen at 1:00 while paused
|
||||
c = resume(c, 180000); // resumed after 2 min paused
|
||||
expect(elapsedMs(c, 240000)).toBe(120000); // 4 min wall − 2 min paused = 2 min
|
||||
});
|
||||
|
||||
it('ignores double start', () => {
|
||||
const c = start(start(initialClock(), 100), 5000);
|
||||
expect(elapsedMs(c, 1100)).toBe(1000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user