86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
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');
|
|
});
|
|
});
|