import assert from 'node:assert/strict'; import test from 'node:test'; import { DiceRollSessionStore } from './diceRollSessionStore'; import { D100_FACE_VALUES, formatPredeterminedDiceNotation, snapD100Value, } from '../../shared/types/diceRoll'; void test('diceRoll: roll produces values in range and notation', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'roll', count: 3, sides: 6 }); const st = store.getState(); assert.equal(st.active, true); assert.equal(st.visibleToPlayers, true); assert.equal(st.notation, '3d6'); assert.equal(st.values.length, 3); assert.ok(st.values.every((v) => v >= 1 && v <= 6)); assert.equal(st.total, st.values.reduce((a, b) => a + b, 0)); }); void test('diceRoll: roll without visibleToPlayers keeps master result only', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'setVisibleToPlayers', visible: false }); store.dispatch({ kind: 'roll', count: 2, sides: 8 }); const st = store.getState(); assert.equal(st.visibleToPlayers, false); assert.equal(st.active, false); assert.equal(st.notation, '2d8'); assert.equal(st.values.length, 2); assert.ok(st.values.every((v) => v >= 1 && v <= 8)); assert.equal(st.startedAt, null); }); void test('diceRoll: unchecking visibleToPlayers hides overlay', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'roll', count: 1, sides: 20 }); assert.equal(store.getState().active, true); const values = [...store.getState().values]; store.dispatch({ kind: 'setVisibleToPlayers', visible: false }); assert.equal(store.getState().active, false); assert.equal(store.getState().visibleToPlayers, false); assert.deepEqual(store.getState().values, values); }); void test('diceRoll: d100 values are tens faces only', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'roll', count: 5, sides: 100 }); const st = store.getState(); assert.equal(st.notation, '5d100'); assert.equal(st.values.length, 5); const allowed = new Set(D100_FACE_VALUES); assert.ok(st.values.every((v) => allowed.has(v))); assert.equal(st.total, st.values.reduce((a, b) => a + b, 0)); }); void test('diceRoll: clear resets but keeps visibleToPlayers', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'setVisibleToPlayers', visible: false }); store.dispatch({ kind: 'roll', count: 1, sides: 20 }); store.dispatch({ kind: 'clear' }); assert.equal(store.getState().active, false); assert.deepEqual(store.getState().values, []); assert.equal(store.getState().visibleToPlayers, false); }); void test('diceRoll: hideOverlay keeps values for master UI', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'roll', count: 2, sides: 6 }); const values = [...store.getState().values]; const total = store.getState().total; store.dispatch({ kind: 'hideOverlay' }); assert.equal(store.getState().active, false); assert.deepEqual(store.getState().values, values); assert.equal(store.getState().total, total); assert.equal(store.getState().notation, '2d6'); assert.equal(store.getState().visibleToPlayers, true); }); void test('diceRoll: roll with zero count is no-op', () => { const store = new DiceRollSessionStore(); store.dispatch({ kind: 'setVisibleToPlayers', visible: false }); const before = store.getState().revision; store.dispatch({ kind: 'roll', count: 0, sides: 6 }); assert.equal(store.getState().revision, before); assert.deepEqual(store.getState().values, []); }); void test('formatPredeterminedDiceNotation', () => { assert.equal(formatPredeterminedDiceNotation(2, 20, [15, 7]), '2d20@15,7'); assert.equal(formatPredeterminedDiceNotation(2, 100, [20, 100]), '2d100@20,100'); assert.equal(formatPredeterminedDiceNotation(1, 100, [27]), '1d100@30'); }); void test('snapD100Value', () => { assert.equal(snapD100Value(20), 20); assert.equal(snapD100Value(21), 30); assert.equal(snapD100Value(100), 100); assert.equal(snapD100Value(1), 10); });