fix: fix tests
This commit is contained in:
@@ -19,7 +19,7 @@ function createTestHost() {
|
||||
|
||||
function waitForPromptEvent(host: GameHost<any>): Promise<PromptEvent> {
|
||||
return new Promise(resolve => {
|
||||
host.commands.on('prompt', resolve);
|
||||
host.context._commands.on('prompt', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ describe('GameHost', () => {
|
||||
it('should create host with initial state', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(host.state.value.winner).toBeNull();
|
||||
expect(host.state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(0);
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(host.context._state.value.winner).toBeNull();
|
||||
expect(host.context._state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(0);
|
||||
});
|
||||
|
||||
it('should have status "created" by default', () => {
|
||||
@@ -59,7 +59,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.schema.name).toBe('play');
|
||||
@@ -81,7 +81,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
|
||||
@@ -106,7 +106,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
const schema = host.activePromptSchema.value;
|
||||
@@ -131,7 +131,7 @@ describe('GameHost', () => {
|
||||
|
||||
// First setup - make one move
|
||||
let promptPromise = waitForPromptEvent(host);
|
||||
let runPromise = host.commands.run('setup');
|
||||
let runPromise = host.context._commands.run('setup');
|
||||
let promptEvent = await promptPromise;
|
||||
|
||||
// Make a move
|
||||
@@ -144,7 +144,7 @@ describe('GameHost', () => {
|
||||
|
||||
let result = await runPromise;
|
||||
expect(result.success).toBe(false); // Cancelled
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(1);
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(1);
|
||||
|
||||
// Setup listener before calling setup
|
||||
const newPromptPromise = waitForPromptEvent(host);
|
||||
@@ -153,10 +153,10 @@ describe('GameHost', () => {
|
||||
await host.setup('setup');
|
||||
|
||||
// State should be back to initial
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(host.state.value.winner).toBeNull();
|
||||
expect(host.state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(0);
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(host.context._state.value.winner).toBeNull();
|
||||
expect(host.context._state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(0);
|
||||
|
||||
// New game should be running and prompting
|
||||
const newPrompt = await newPromptPromise;
|
||||
@@ -168,7 +168,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
await promptPromise;
|
||||
|
||||
@@ -184,8 +184,8 @@ describe('GameHost', () => {
|
||||
}
|
||||
|
||||
// State should be reset
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(host.state.value.turn).toBe(0);
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(host.context._state.value.turn).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw error when disposed', async () => {
|
||||
@@ -208,7 +208,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
await promptPromise;
|
||||
|
||||
@@ -289,12 +289,12 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
// Initial state
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(host.state.value.turn).toBe(0);
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(host.context._state.value.turn).toBe(0);
|
||||
|
||||
// Make a move
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
promptEvent.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
@@ -307,9 +307,9 @@ describe('GameHost', () => {
|
||||
const result = await runPromise;
|
||||
expect(result.success).toBe(false); // Cancelled
|
||||
|
||||
expect(host.state.value.currentPlayer).toBe('O');
|
||||
expect(host.state.value.turn).toBe(1);
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(1);
|
||||
expect(host.context._state.value.currentPlayer).toBe('O');
|
||||
expect(host.context._state.value.turn).toBe(1);
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should update activePromptSchema reactively', async () => {
|
||||
@@ -320,7 +320,7 @@ describe('GameHost', () => {
|
||||
|
||||
// Start a command that triggers prompt
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
await promptPromise;
|
||||
|
||||
@@ -330,7 +330,7 @@ describe('GameHost', () => {
|
||||
|
||||
// Cancel and wait
|
||||
const cancelEvent = host.activePromptSchema.value;
|
||||
host.commands._cancel();
|
||||
host.context._commands._cancel();
|
||||
try {
|
||||
await runPromise;
|
||||
} catch {
|
||||
@@ -347,10 +347,10 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
// Initial state
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(host.state.value.winner).toBeNull();
|
||||
expect(host.state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(0);
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(host.context._state.value.winner).toBeNull();
|
||||
expect(host.context._state.value.turn).toBe(0);
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(0);
|
||||
|
||||
// X wins diagonally: (0,0), (1,1), (2,2)
|
||||
// O plays: (0,1), (2,1)
|
||||
@@ -364,12 +364,12 @@ describe('GameHost', () => {
|
||||
|
||||
// Track prompt events in a queue
|
||||
const promptEvents: PromptEvent[] = [];
|
||||
host.commands.on('prompt', (e) => {
|
||||
host.context._commands.on('prompt', (e) => {
|
||||
promptEvents.push(e);
|
||||
});
|
||||
|
||||
// Start setup command (runs game loop until completion)
|
||||
const setupPromise = host.commands.run('setup');
|
||||
const setupPromise = host.context._commands.run('setup');
|
||||
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
// Wait until the next prompt event arrives
|
||||
@@ -393,12 +393,12 @@ describe('GameHost', () => {
|
||||
}
|
||||
|
||||
// Final state checks
|
||||
expect(host.state.value.winner).toBe('X');
|
||||
expect(host.state.value.currentPlayer).toBe('X');
|
||||
expect(Object.keys(host.state.value.parts).length).toBe(5);
|
||||
expect(host.context._state.value.winner).toBe('X');
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(5);
|
||||
|
||||
// Verify winning diagonal
|
||||
const parts = Object.values(host.state.value.parts);
|
||||
const parts = Object.values(host.context._state.value.parts);
|
||||
const xPieces = parts.filter(p => p.player === 'X');
|
||||
expect(xPieces).toHaveLength(3);
|
||||
expect(xPieces.some(p => JSON.stringify(p.position) === JSON.stringify([0, 0]))).toBe(true);
|
||||
@@ -415,7 +415,7 @@ describe('GameHost', () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.commands.run('setup');
|
||||
const runPromise = host.context._commands.run('setup');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.currentPlayer).toBe('X');
|
||||
@@ -433,7 +433,7 @@ describe('GameHost', () => {
|
||||
|
||||
// First prompt - X's turn
|
||||
let promptPromise = waitForPromptEvent(host);
|
||||
let runPromise = host.commands.run('setup');
|
||||
let runPromise = host.context._commands.run('setup');
|
||||
let promptEvent = await promptPromise;
|
||||
expect(promptEvent.currentPlayer).toBe('X');
|
||||
expect(host.activePromptPlayer.value).toBe('X');
|
||||
|
||||
+39
-42
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createGameContext, createGameCommand, createGameCommandRegistry } from '@/core/game';
|
||||
import type { PromptEvent } from '@/utils/command';
|
||||
import { createGameContext, createGameCommandRegistry, IGameContext } from '@/core/game';
|
||||
import type { PromptEvent, Command } from '@/utils/command';
|
||||
|
||||
type MyState = {
|
||||
score: number;
|
||||
@@ -9,56 +9,55 @@ type MyState = {
|
||||
|
||||
describe('createGameContext', () => {
|
||||
it('should create a game context with state', () => {
|
||||
const { registry } = createGameCommandRegistry();
|
||||
const registry = createGameCommandRegistry();
|
||||
const ctx = createGameContext(registry);
|
||||
|
||||
expect(ctx.state).not.toBeNull();
|
||||
expect(ctx.state.value).toBeDefined();
|
||||
expect(ctx._state).not.toBeNull();
|
||||
expect(ctx._state.value).toBeDefined();
|
||||
});
|
||||
|
||||
it('should wire commands to the context', () => {
|
||||
const { registry } = createGameCommandRegistry();
|
||||
const registry = createGameCommandRegistry();
|
||||
const ctx = createGameContext(registry);
|
||||
|
||||
expect(ctx.commands).not.toBeNull();
|
||||
expect(ctx.commands.registry).toBe(registry);
|
||||
expect(ctx.commands.context).toBe(ctx.state);
|
||||
expect(ctx._commands).not.toBeNull();
|
||||
expect(ctx._commands.registry).toBe(registry);
|
||||
});
|
||||
|
||||
it('should accept initial state as an object', () => {
|
||||
const { registry } = createGameCommandRegistry<MyState>();
|
||||
const registry = createGameCommandRegistry<MyState>();
|
||||
const ctx = createGameContext<MyState>(registry, {
|
||||
score: 0,
|
||||
round: 1,
|
||||
});
|
||||
|
||||
expect(ctx.state.value.score).toBe(0);
|
||||
expect(ctx.state.value.round).toBe(1);
|
||||
expect(ctx._state.value.score).toBe(0);
|
||||
expect(ctx._state.value.round).toBe(1);
|
||||
});
|
||||
|
||||
it('should accept initial state as a factory function', () => {
|
||||
const { registry } = createGameCommandRegistry<MyState>();
|
||||
const registry = createGameCommandRegistry<MyState>();
|
||||
const ctx = createGameContext<MyState>(registry, () => ({
|
||||
score: 10,
|
||||
round: 3,
|
||||
}));
|
||||
|
||||
expect(ctx.state.value.score).toBe(10);
|
||||
expect(ctx.state.value.round).toBe(3);
|
||||
expect(ctx._state.value.score).toBe(10);
|
||||
expect(ctx._state.value.round).toBe(3);
|
||||
});
|
||||
|
||||
it('should forward prompt events via listener', async () => {
|
||||
const { registry } = createGameCommandRegistry();
|
||||
const registry = createGameCommandRegistry();
|
||||
const ctx = createGameContext(registry);
|
||||
|
||||
createGameCommand(registry, 'test <value>', async function () {
|
||||
return this.prompt('prompt <answer>');
|
||||
registry.register('test <value>', async function (_ctx, value) {
|
||||
return this.prompt<string>('prompt <answer>', () => 'ok');
|
||||
});
|
||||
|
||||
const promptPromise = new Promise<PromptEvent>(resolve => {
|
||||
ctx.commands.on('prompt', resolve);
|
||||
ctx._commands.on('prompt', resolve);
|
||||
});
|
||||
const runPromise = ctx.commands.run('test hello');
|
||||
const runPromise = ctx.run('test hello');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent).not.toBeNull();
|
||||
@@ -69,45 +68,43 @@ describe('createGameContext', () => {
|
||||
|
||||
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 run a command with access to game context', async () => {
|
||||
const { registry } = createGameCommandRegistry<{ marker: string }>();
|
||||
const ctx = createGameContext(registry, { marker: '' });
|
||||
|
||||
createGameCommand(registry, 'set-marker <id>', async function (cmd) {
|
||||
const id = cmd.params[0] as string;
|
||||
this.context.produce(state => {
|
||||
const registry = createGameCommandRegistry<{ marker: string }>();
|
||||
|
||||
registry.register('set-marker <id>', async function (ctx, id) {
|
||||
ctx.produce(state => {
|
||||
state.marker = id;
|
||||
});
|
||||
return id;
|
||||
});
|
||||
|
||||
const result = await ctx.commands.run('set-marker board');
|
||||
const ctx = createGameContext(registry, { marker: '' });
|
||||
|
||||
const result = await ctx.run('set-marker board');
|
||||
if (!result.success) {
|
||||
console.error('Error:', result.error);
|
||||
}
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.result).toBe('board');
|
||||
}
|
||||
expect(ctx.state.value.marker).toBe('board');
|
||||
expect(ctx._state.value.marker).toBe('board');
|
||||
});
|
||||
|
||||
it('should run a typed command with extended context', async () => {
|
||||
const { registry } = createGameCommandRegistry<MyState>();
|
||||
const registry = createGameCommandRegistry<MyState>();
|
||||
|
||||
createGameCommand<MyState, number>(
|
||||
registry,
|
||||
registry.register(
|
||||
'add-score <amount:number>',
|
||||
async function (cmd) {
|
||||
const amount = cmd.params[0] as number;
|
||||
this.context.produce(state => {
|
||||
async function (ctx, amount) {
|
||||
ctx.produce(state => {
|
||||
state.score += amount;
|
||||
});
|
||||
return this.context.value.score;
|
||||
return ctx.value.score;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -116,19 +113,19 @@ describe('createGameCommand', () => {
|
||||
round: 1,
|
||||
}));
|
||||
|
||||
const result = await ctx.commands.run('add-score 5');
|
||||
const result = await ctx.run('add-score 5');
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.result).toBe(5);
|
||||
}
|
||||
expect(ctx.state.value.score).toBe(5);
|
||||
expect(ctx._state.value.score).toBe(5);
|
||||
});
|
||||
|
||||
it('should return error for unknown command', async () => {
|
||||
const { registry } = createGameCommandRegistry();
|
||||
const registry = createGameCommandRegistry();
|
||||
const ctx = createGameContext(registry);
|
||||
|
||||
const result = await ctx.commands.run('nonexistent');
|
||||
const result = await ctx.run('nonexistent');
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain('nonexistent');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createRegion, applyAlign, shuffle, moveToRegion, moveToRegionAll, removeFromRegion, type Region, type RegionAxis } from '@/core/region';
|
||||
import { createRegion, applyAlign, shuffle, moveToRegion, type Region, type RegionAxis } from '@/core/region';
|
||||
import { createRNG } from '@/utils/rng';
|
||||
import { type Part } from '@/core/part';
|
||||
|
||||
@@ -303,76 +303,4 @@ describe('Region', () => {
|
||||
expect(part.position).toEqual([3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveToRegionAll', () => {
|
||||
it('should move multiple parts to a target region', () => {
|
||||
const sourceRegion = createRegion('source', [{ name: 'x' }]);
|
||||
const targetRegion = createRegion('target', [{ name: 'x' }]);
|
||||
|
||||
const parts = {
|
||||
p1: { id: 'p1', regionId: 'source', position: [0] } as Part,
|
||||
p2: { id: 'p2', regionId: 'source', position: [1] } as Part,
|
||||
p3: { id: 'p3', regionId: 'source', position: [2] } as Part,
|
||||
};
|
||||
sourceRegion.childIds.push('p1', 'p2', 'p3');
|
||||
sourceRegion.partMap = { '0': 'p1', '1': 'p2', '2': 'p3' };
|
||||
|
||||
moveToRegionAll([parts.p1, parts.p2, parts.p3], sourceRegion, targetRegion, [[0], [1], [2]]);
|
||||
|
||||
expect(sourceRegion.childIds).toHaveLength(0);
|
||||
expect(targetRegion.childIds).toHaveLength(3);
|
||||
expect(parts.p1.position).toEqual([0]);
|
||||
expect(parts.p2.position).toEqual([1]);
|
||||
expect(parts.p3.position).toEqual([2]);
|
||||
});
|
||||
|
||||
it('should keep existing positions if no positions provided', () => {
|
||||
const sourceRegion = createRegion('source', [{ name: 'x' }]);
|
||||
const targetRegion = createRegion('target', [{ name: 'x' }]);
|
||||
|
||||
const parts = {
|
||||
p1: { id: 'p1', regionId: 'source', position: [5] } as Part,
|
||||
p2: { id: 'p2', regionId: 'source', position: [8] } as Part,
|
||||
};
|
||||
sourceRegion.childIds.push('p1', 'p2');
|
||||
sourceRegion.partMap = { '5': 'p1', '8': 'p2' };
|
||||
|
||||
moveToRegionAll([parts.p1, parts.p2], sourceRegion, targetRegion);
|
||||
|
||||
expect(parts.p1.position).toEqual([5]);
|
||||
expect(parts.p2.position).toEqual([8]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeFromRegion', () => {
|
||||
it('should remove a part from its region', () => {
|
||||
const region = createRegion('region1', [{ name: 'x' }]);
|
||||
|
||||
const part: Part = { id: 'p1', regionId: 'region1', position: [2] };
|
||||
const parts: Record<string, Part> = { p1: part };
|
||||
region.childIds.push('p1');
|
||||
region.partMap['2'] = 'p1';
|
||||
|
||||
expect(region.childIds).toHaveLength(1);
|
||||
|
||||
removeFromRegion(part, region);
|
||||
|
||||
expect(region.childIds).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should leave other parts unaffected', () => {
|
||||
const region = createRegion('region1', [{ name: 'x' }]);
|
||||
|
||||
const p1 = { id: 'p1', regionId: 'region1', position: [0] } as Part;
|
||||
const p2 = { id: 'p2', regionId: 'region1', position: [1] } as Part;
|
||||
const p3 = { id: 'p3', regionId: 'region1', position: [2] } as Part;
|
||||
region.childIds.push('p1', 'p2', 'p3');
|
||||
region.partMap = { '0': 'p1', '1': 'p2', '2': 'p3' };
|
||||
|
||||
removeFromRegion(p2, region);
|
||||
|
||||
expect(region.childIds).toHaveLength(2);
|
||||
expect(region.childIds).toEqual(['p1', 'p3']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user