Files
DndGamePlayer/app/main/sessionTimer/timerSessionStore.test.ts
T
Ivan Fontosh db1168a46c feat(control): таймер сессии поверх сцены
Отдельное окно countdown с «Показать игрокам»; на превью — move/resize/rotate, на презентации тот же layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 16:51:56 +08:00

84 lines
3.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { TimerSessionStore } from './timerSessionStore';
import {
clampSessionTimerLayout,
formatSessionTimerMs,
getSessionTimerRemainingMs,
parseSessionTimerDurationInput,
} from '../../shared/types/sessionTimer';
void test('getSessionTimerRemainingMs: paused uses remainingMs', () => {
const store = new TimerSessionStore();
const s = store.getState();
assert.equal(getSessionTimerRemainingMs(s, 1_000_000), s.remainingMs);
});
void test('start pause start preserves remaining', () => {
const store = new TimerSessionStore();
const t0 = 1_000_000;
store.dispatch({ kind: 'setDuration', durationMs: 60_000 }, t0);
store.dispatch({ kind: 'start' }, t0);
store.dispatch({ kind: 'pause' }, t0 + 10_000);
const afterPause = store.getState();
assert.equal(afterPause.running, false);
assert.equal(afterPause.remainingMs, 50_000);
store.dispatch({ kind: 'start' }, t0 + 20_000);
assert.equal(getSessionTimerRemainingMs(store.getState(), t0 + 25_000), 45_000);
});
void test('reset restores duration', () => {
const store = new TimerSessionStore();
store.dispatch({ kind: 'setDuration', durationMs: 90_000 });
store.dispatch({ kind: 'start' }, 0);
store.dispatch({ kind: 'pause' }, 30_000);
store.dispatch({ kind: 'reset' });
assert.equal(store.getState().remainingMs, 90_000);
assert.equal(store.getState().running, false);
});
void test('setVisibleToPlayers and layout.set', () => {
const store = new TimerSessionStore();
store.dispatch({ kind: 'setVisibleToPlayers', visible: true });
assert.equal(store.getState().visibleToPlayers, true);
store.dispatch({
kind: 'layout.set',
layout: { cx: 0.2, cy: 0.3, scale: 2, rotationDeg: 45 },
});
assert.equal(store.getState().layout.cx, 0.2);
assert.equal(store.getState().layout.scale, 2);
assert.equal(store.getState().layout.rotationDeg, 45);
});
void test('settleIfExpired stops at zero', () => {
const store = new TimerSessionStore();
store.dispatch({ kind: 'setDuration', durationMs: 5_000 }, 0);
store.dispatch({ kind: 'start' }, 0);
store.settleIfExpired(10_000);
assert.equal(store.getState().running, false);
assert.equal(store.getState().remainingMs, 0);
});
void test('clampSessionTimerLayout / format / parse', () => {
const clamped = clampSessionTimerLayout({ cx: 9, cy: -9, scale: 100, rotationDeg: 370 });
assert.ok(clamped.cx <= 1.2);
assert.ok(clamped.cy >= -0.2);
assert.ok(clamped.scale <= 8);
assert.equal(clamped.rotationDeg, 10);
assert.equal(formatSessionTimerMs(65_000), '01:05');
assert.equal(formatSessionTimerMs(3_661_000), '1:01:01');
assert.equal(parseSessionTimerDurationInput('5:00'), 300_000);
assert.equal(parseSessionTimerDurationInput('1:02:03'), 3723_000);
assert.equal(parseSessionTimerDurationInput('bad'), null);
});
void test('clear restores defaults', () => {
const store = new TimerSessionStore();
store.dispatch({ kind: 'setVisibleToPlayers', visible: true });
store.dispatch({ kind: 'start' }, 0);
store.clear();
assert.equal(store.getState().visibleToPlayers, false);
assert.equal(store.getState().running, false);
});