import { describe, it, expect } from 'vitest'; import { createGameContext, createGameCommand, IGameContext } from '../../src/core/game'; import { createCommandRegistry, parseCommandSchema, type CommandRegistry } from '../../src/utils/command'; type MyState = { score: number; round: number; }; type MyContext = IGameContext & { state: MyState; }; describe('createGameContext', () => { it('should create a game context with empty parts and regions', () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry); expect(ctx.parts.collection.value).toEqual({}); expect(ctx.regions.collection.value).toEqual({}); }); it('should wire commands to the context', () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry); expect(ctx.commands).not.toBeNull(); expect(ctx.commands.registry).toBe(registry); expect(ctx.commands.context).toBe(ctx); }); it('should accept initial state as an object', () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry, { state: { score: 0, round: 1 }, }); expect(ctx.state.score).toBe(0); expect(ctx.state.round).toBe(1); }); it('should accept initial state as a factory function', () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry, () => ({ state: { score: 10, round: 3 }, })); expect(ctx.state.score).toBe(10); expect(ctx.state.round).toBe(3); }); it('should forward prompt events to the prompts queue', async () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry); const schema = parseCommandSchema('test '); registry.set('test', { schema, run: async function () { return this.prompt('prompt '); }, }); const runPromise = ctx.commands.run('test hello'); await new Promise((r) => setTimeout(r, 0)); const promptEvent = await ctx.prompts.pop(); expect(promptEvent).not.toBeNull(); expect(promptEvent.schema.name).toBe('prompt'); promptEvent.resolve({ name: 'prompt', params: ['yes'], options: {}, flags: {} }); const result = await runPromise; expect(result.success).toBe(true); if (result.success) { expect((result.result as any).params[0]).toBe('yes'); } }); }); describe('createGameCommand', () => { it('should create a command from a string schema', () => { const cmd = createGameCommand('test ', async function () { return 1; }); expect(cmd.schema.name).toBe('test'); expect(cmd.schema.params[0].name).toBe('a'); expect(cmd.schema.params[0].required).toBe(true); }); it('should create a command from a CommandSchema object', () => { const schema = parseCommandSchema('foo [y]'); const cmd = createGameCommand(schema, async function () { return 2; }); expect(cmd.schema.name).toBe('foo'); expect(cmd.schema.params[0].name).toBe('x'); expect(cmd.schema.params[0].required).toBe(true); expect(cmd.schema.params[1].name).toBe('y'); expect(cmd.schema.params[1].required).toBe(false); }); it('should run a command with access to game context', async () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry); const addRegion = createGameCommand('add-region ', async function (cmd) { const id = cmd.params[0] as string; this.context.regions.add({ id, axes: [], children: [] }); return id; }); registry.set('add-region', addRegion); const result = await ctx.commands.run('add-region board'); expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe('board'); } expect(ctx.regions.get('board')).not.toBeNull(); }); it('should run a command that adds parts', async () => { const registry = createCommandRegistry(); const ctx = createGameContext(registry); ctx.regions.add({ id: 'zone', axes: [], children: [] }); const addPart = createGameCommand('add-part ', async function (cmd) { const id = cmd.params[0] as string; const part = { id, sides: 1, side: 0, region: this.context.regions.get('zone'), position: [0], }; this.context.parts.add(part); return id; }); registry.set('add-part', addPart); const result = await ctx.commands.run('add-part piece-1'); expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe('piece-1'); } expect(ctx.parts.get('piece-1')).not.toBeNull(); }); it('should run a typed command with extended context', async () => { const registry = createCommandRegistry(); const addScore = createGameCommand( 'add-score ', async function (cmd) { const amount = cmd.params[0] as number; this.context.state.score += amount; return this.context.state.score; } ); registry.set('add-score', addScore); const ctx = createGameContext(registry, () => ({ state: { score: 0, round: 1 }, })); const result = await ctx.commands.run('add-score 5'); expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe(5); } expect(ctx.state.score).toBe(5); }); });