import { describe, expect, it } from 'vitest'; import { buildContent } from '../../content/schema'; import { createGameState, enqueueAction, startAction } from '../../engine/game'; import { formatOfflineDuration, toView } from '../viewModel'; function testContent() { return buildContent({ resources: [{ id: 'gold', name: 'Gold', startAmount: 4 }], actions: [ { id: 'forage', name: 'Forage', durationMs: 200, yields: [{ resourceId: 'gold', amount: 1 }], }, ], }); } describe('toView()', () => { it('maps resources with their names and amounts', () => { const content = testContent(); const view = toView(createGameState(content), content); expect(view.resources).toEqual([{ id: 'gold', name: 'Gold', amount: 4 }]); }); it('reports no progress and no action name when idle', () => { const content = testContent(); const view = toView(createGameState(content), content); expect(view.activeActionId).toBeNull(); expect(view.actionName).toBeNull(); expect(view.actionProgress).toBe(0); }); it('reports the active action name and fractional progress', () => { const content = testContent(); const state = createGameState(content); startAction(state, content, 'forage'); state.actionElapsedMs = 50; // of 200ms const view = toView(state, content); expect(view.actionName).toBe('Forage'); expect(view.actionProgress).toBeCloseTo(0.25); }); it('clamps progress to at most 1', () => { const content = testContent(); const state = createGameState(content); startAction(state, content, 'forage'); state.actionElapsedMs = 999; expect(toView(state, content).actionProgress).toBe(1); }); it('includes queued action ids in order', () => { const content = buildContent({ resources: [{ id: 'gold', name: 'Gold' }], actions: [ { id: 'a', name: 'Alpha', durationMs: 1000, yields: [{ resourceId: 'gold', amount: 1 }] }, { id: 'b', name: 'Bravo', durationMs: 1000, yields: [{ resourceId: 'gold', amount: 1 }] }, ], }); const state = createGameState(content); enqueueAction(state, content, 'a'); enqueueAction(state, content, 'b'); const view = toView(state, content); expect(view.queuedActionIds).toEqual(['b']); expect(view.queuedActionNames).toEqual(['Bravo']); }); }); describe('formatOfflineDuration()', () => { it('formats sub-minute durations in seconds', () => { expect(formatOfflineDuration(0)).toBe('0s'); expect(formatOfflineDuration(45_000)).toBe('45s'); }); it('formats minutes with seconds', () => { expect(formatOfflineDuration(90_000)).toBe('1m 30s'); expect(formatOfflineDuration(120_000)).toBe('2m'); }); it('formats hours with minutes', () => { expect(formatOfflineDuration(3_660_000)).toBe('1h 1m'); expect(formatOfflineDuration(7_200_000)).toBe('2h'); }); });