chore: pass PR1 verification gate (format, lint, coverage)
CI / verify (push) Successful in 48s
CI / verify (pull_request) Successful in 50s

This commit is contained in:
ginnoir
2026-06-11 18:23:43 -05:00
parent cc4955abab
commit 1c5fcc80ad
8 changed files with 127 additions and 19 deletions
+60 -1
View File
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import { buildContent } from '../../content/schema';
import {
cancelQueuedAction,
canUnlockAction,
clearQueue,
createGameState,
enqueueAction,
@@ -13,7 +14,12 @@ function testContent() {
return buildContent({
resources: [{ id: 'gold', name: 'Gold', startAmount: 5 }],
actions: [
{ id: 'forage', name: 'Forage', durationMs: 300, yields: [{ resourceId: 'gold', amount: 2 }] },
{
id: 'forage',
name: 'Forage',
durationMs: 300,
yields: [{ resourceId: 'gold', amount: 2 }],
},
],
});
}
@@ -180,6 +186,24 @@ describe('unlock conditions', () => {
enqueueAction(state, content, 'scout');
expect(state.activeActionId).toBe('scout');
});
it('rejects actions until required story flags are set', () => {
const content = buildContent({
resources: [{ id: 'gold', name: 'Gold', startAmount: 0 }],
actions: [
{
id: 'secret',
name: 'Secret',
durationMs: 100,
yields: [{ resourceId: 'gold', amount: 1 }],
unlock: { requireStoryFlags: ['path_scouted'] },
},
],
});
const state = createGameState(content);
expect(() => enqueueAction(state, content, 'secret')).toThrow(/cannot enqueue/i);
expect(canUnlockAction(state, content, 'secret', { path_scouted: true })).toBe(true);
});
});
describe('completion advances queue', () => {
@@ -203,6 +227,41 @@ describe('completion advances queue', () => {
expect(state.actionElapsedMs).toBe(0);
});
it('skips queued actions that are unaffordable when their turn arrives', () => {
const content = buildContent({
resources: [{ id: 'supplies', name: 'Supplies', startAmount: 0 }],
actions: [
{
id: 'cheap',
name: 'Cheap',
durationMs: 100,
yields: [{ resourceId: 'supplies', amount: 1 }],
},
{
id: 'dear',
name: 'Dear',
durationMs: 100,
costs: [{ resourceId: 'supplies', amount: 6 }],
yields: [{ resourceId: 'supplies', amount: 1 }],
},
{
id: 'free',
name: 'Free',
durationMs: 100,
yields: [{ resourceId: 'supplies', amount: 1 }],
},
],
});
const state = createGameState(content);
state.activeActionId = 'cheap';
state.actionElapsedMs = 99;
state.actionQueue = ['dear', 'free'];
state.resources.supplies = 3;
tickGame(state, content, 1);
expect(state.activeActionId).toBe('free');
expect(state.actionQueue).toEqual([]);
});
it('grants all yields on completion', () => {
const content = buildContent({
resources: [
+6 -1
View File
@@ -15,7 +15,12 @@ function testContent() {
return buildContent({
resources: [{ id: 'gold', name: 'Gold', startAmount: 0 }],
actions: [
{ id: 'forage', name: 'Forage', durationMs: 3000, yields: [{ resourceId: 'gold', amount: 1 }] },
{
id: 'forage',
name: 'Forage',
durationMs: 3000,
yields: [{ resourceId: 'gold', amount: 1 }],
},
],
});
}
+4 -1
View File
@@ -99,7 +99,10 @@ function beginAction(state: GameState, content: Content, actionId: string): void
function startNextFromQueue(state: GameState, content: Content): void {
while (state.actionQueue.length > 0) {
const nextId = state.actionQueue.shift()!;
const nextId = state.actionQueue.shift();
if (!nextId) {
break;
}
if (isActionAvailable(state, content, nextId)) {
beginAction(state, content, nextId);
return;