import { describe, it, expect } from 'vitest'; import { registry, createInitialState, BoopState } from '@/samples/boop'; import { createGameContext } from '@/index'; import type { PromptEvent } from '@/utils/command'; function createTestContext() { const ctx = createGameContext(registry, createInitialState()); return { registry, ctx }; } function waitForPrompt(ctx: ReturnType['ctx']): Promise { return new Promise(resolve => { ctx._commands.on('prompt', resolve); }); } describe('Boop Game', () => { describe('Setup', () => { it('should create initial state correctly', () => { const state = createInitialState(); expect(state.currentPlayer).toBe('white'); expect(state.winner).toBeNull(); expect(state.regions.board).toBeDefined(); expect(state.regions.white).toBeDefined(); expect(state.regions.black).toBeDefined(); // 8 kittens per player const whiteKittens = Object.values(state.pieces).filter(p => p.player === 'white' && p.type === 'kitten'); const blackKittens = Object.values(state.pieces).filter(p => p.player === 'black' && p.type === 'kitten'); expect(whiteKittens.length).toBe(8); expect(blackKittens.length).toBe(8); // 8 cats per player (initially in box) const whiteCats = Object.values(state.pieces).filter(p => p.player === 'white' && p.type === 'cat'); const blackCats = Object.values(state.pieces).filter(p => p.player === 'black' && p.type === 'cat'); expect(whiteCats.length).toBe(8); expect(blackCats.length).toBe(8); // All cats should be in box (regionId = '') whiteCats.forEach(cat => expect(cat.regionId).toBe('')); blackCats.forEach(cat => expect(cat.regionId).toBe('')); // Kittens should be in player supplies whiteKittens.forEach(k => expect(k.regionId).toBe('white')); blackKittens.forEach(k => expect(k.regionId).toBe('black')); }); }); describe('Place and Boop Commands', () => { it('should place a kitten via play command', async () => { const { ctx } = createTestContext(); // Use turn command instead of setup which runs indefinitely const promptPromise = waitForPrompt(ctx); const runPromise = ctx.run('turn white'); const promptEvent = await promptPromise; expect(promptEvent).not.toBeNull(); expect(promptEvent.schema.name).toBe('play'); // Place a kitten at position 2,2 const error = promptEvent.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} }); expect(error).toBeNull(); const result = await runPromise; expect(result.success).toBe(true); const state = ctx.value; // Should have placed a piece on the board const boardPieces = Object.keys(state.regions.board.partMap); expect(boardPieces.length).toBeGreaterThan(0); // Should have one less kitten in supply const whiteSupply = state.regions.white.childIds.filter(id => state.pieces[id].type === 'kitten'); expect(whiteSupply.length).toBe(7); }); }); describe('Boop Mechanics', () => { it('should boop adjacent pieces away from placement', async () => { const { ctx } = createTestContext(); // White places at 2,2 let promptPromise = waitForPrompt(ctx); let runPromise = ctx.run('turn white'); let promptEvent = await promptPromise; let error = promptEvent.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} }); expect(error).toBeNull(); let result = await runPromise; expect(result.success).toBe(true); // Black places at 2,3, which will boop white's piece promptPromise = waitForPrompt(ctx); runPromise = ctx.run('turn black'); promptEvent = await promptPromise; error = promptEvent.tryCommit({ name: 'play', params: ['black', 2, 3, 'kitten'], options: {}, flags: {} }); expect(error).toBeNull(); result = await runPromise; expect(result.success).toBe(true); const state = ctx.value; // Check that pieces were placed const boardPieceCount = Object.keys(state.regions.board.partMap).length; expect(boardPieceCount).toBeGreaterThanOrEqual(1); }); it('should handle pieces being booped off the board', async () => { const { ctx } = createTestContext(); // White places at corner const promptPromise = waitForPrompt(ctx); const runPromise = ctx.run('turn white'); const promptEvent = await promptPromise; const error = promptEvent.tryCommit({ name: 'play', params: ['white', 0, 0, 'kitten'], options: {}, flags: {} }); expect(error).toBeNull(); const result = await runPromise; expect(result.success).toBe(true); const state = ctx.value; // Verify placement expect(state.regions.board.partMap['0,0']).toBeDefined(); }); }); describe('Full Game Flow', () => { it('should play a turn and switch players', async () => { const { ctx } = createTestContext(); // White's turn - place at 2,2 let promptPromise = waitForPrompt(ctx); let runPromise = ctx.run('turn white'); let prompt = await promptPromise; const error1 = prompt.tryCommit({ name: 'play', params: ['white', 2, 2, 'kitten'], options: {}, flags: {} }); expect(error1).toBeNull(); let result = await runPromise; expect(result.success).toBe(true); const stateAfterWhite = ctx.value; // Should have placed a piece expect(stateAfterWhite.regions.board.partMap['2,2']).toBeDefined(); }); }); });