- Implemented 10 sub-systems (Economy, Pathfinding, Combat, Selection, Network, Map, Entity/Building/ControlPoint state machines, Orchestrator) - Refactored Custom_Entity.js → Unit.js with 5 components (health, owner, inventory, movement, combat) - Added Jest test suite with 100+ tests (EconomySystem 100%, EntityStateMachine 100%, PathfindingSystem 99%, Unit.js 72%) - All webpack builds pass (0 errors) - BMAD-auto team-respawn flow: 10 parallel sub-agents implemented systems Architecture: Phaser 3 + XState + socket.io + EasyStar Mode: team-respawn Model: custom/ollama-cloud-pro
124 lines
3.0 KiB
JavaScript
124 lines
3.0 KiB
JavaScript
/**
|
|
* Jest Setup - Mock Phaser and browser APIs
|
|
*/
|
|
|
|
// Mock Phaser BEFORE it's imported
|
|
jest.mock('phaser', () => ({
|
|
Physics: {
|
|
Arcade: {
|
|
DYNAMIC_BODY: 0,
|
|
Sprite: class MockSprite {
|
|
constructor(scene, x, y, texture) {
|
|
this.scene = scene;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.texture = texture;
|
|
this.body = {
|
|
allowGravity: false,
|
|
setSize: jest.fn(),
|
|
setOffset: jest.fn()
|
|
};
|
|
this.setScale = jest.fn();
|
|
this.setInteractive = jest.fn();
|
|
this.on = jest.fn();
|
|
this.setPosition = jest.fn(() => true);
|
|
this.setFlipX = jest.fn();
|
|
this.setTint = jest.fn();
|
|
this.clearTint = jest.fn();
|
|
this.setData = jest.fn();
|
|
this.getData = jest.fn(() => null);
|
|
this.pulse = null;
|
|
}
|
|
static enable(scene, object) {
|
|
object.body = { allowGravity: false };
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Math: {
|
|
Angle: {
|
|
BetweenPoints: (a, b) => Math.atan2(b.y - a.y, b.x - a.x)
|
|
},
|
|
RadToDeg: rad => rad * (180 / Math.PI),
|
|
Distance: {
|
|
BetweenPoints: (a, b) => Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2))
|
|
},
|
|
Clamp: (v, min, max) => Math.max(min, Math.min(max, v))
|
|
},
|
|
Display: {
|
|
Color: {
|
|
GetColor32: (r, g, b, a) => (r << 24) | (g << 16) | (b << 8) | a
|
|
}
|
|
},
|
|
Tweens: {
|
|
Tween: class MockTween {
|
|
constructor(config) {
|
|
this.config = config;
|
|
}
|
|
getValue() { return 200; }
|
|
stop() {}
|
|
},
|
|
addCounter: config => ({
|
|
getValue: () => 200,
|
|
stop: () => {}
|
|
})
|
|
},
|
|
Events: {
|
|
EventEmitter: class MockEventEmitter {
|
|
constructor() {
|
|
this.listeners = {};
|
|
}
|
|
on(event, fn) {
|
|
if (!this.listeners[event]) this.listeners[event] = [];
|
|
this.listeners[event].push(fn);
|
|
}
|
|
emit(event, ...args) {
|
|
if (this.listeners[event]) {
|
|
this.listeners[event].forEach(fn => fn(...args));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
GameObjects: {
|
|
Zone: class MockZone {
|
|
constructor(scene, x, y, width, height) {
|
|
this.scene = scene;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.body = { setCircle: jest.fn(), checkCollision: { none: false } };
|
|
}
|
|
destroy() {}
|
|
}
|
|
}
|
|
}));
|
|
|
|
// Mock XState
|
|
jest.mock('xstate', () => ({
|
|
createMachine: jest.fn(config => ({ config })),
|
|
interpret: jest.fn(machine => ({
|
|
machine,
|
|
start: jest.fn(),
|
|
send: jest.fn(),
|
|
stop: jest.fn(),
|
|
state: { value: 'IDLING' }
|
|
})),
|
|
assign: jest.fn(fn => fn)
|
|
}));
|
|
|
|
// Mock EasyStar
|
|
jest.mock('easystarjs', () => {
|
|
return jest.fn().mockImplementation(() => ({
|
|
setGrid: jest.fn(),
|
|
setIterationsPerCalculation: jest.fn(),
|
|
findPath: jest.fn((x, y, toX, toY, callback) => {
|
|
setTimeout(() => callback([{ x, y }, { x: toX, y: toY }]), 0);
|
|
}),
|
|
setTileAtXY: jest.fn()
|
|
}));
|
|
});
|
|
|
|
// Suppress console errors during tests
|
|
console.error = jest.fn();
|