Files
restitution/tests/setup.js
kaykayyali 8fc45968b5 M2.3 + M2.4 + TeamManager integration: projectile sprites, death handling, build menu, production panel, building placer
- ProjectileSprite.js: physics arcade sprite, faction tint, off-screen culling
- CombatSystem: refactored enemy selection to use TeamManager instead of legacy containers
- Death handling: DYING alpha tween (500ms), smoke puff (300ms), unit:killed event, cleanup
- TeamManager: centralized team registry replacing goodGuys/badGuys containers
- HealthBarSystem, ResourceBar, CaptureProgressUI, BuildMenu, BuildingPlacer, BuildingRenderer, ProductionPanel
- Map_Player: wired new subsystems, removed legacy container creation
- Tests: ProjectileSprite (4), DeathHandling (13), CombatSystem updated

47 tests passed at dev time (M2.3), 158/158 at dev time (M2.4)
2026-06-01 05:18:33 +00:00

206 lines
5.5 KiB
JavaScript

/**
* Jest Setup - Mock Phaser and browser APIs
*/
// Mock Phaser BEFORE it's imported
jest.mock('phaser', () => ({
Scene: class MockScene {
constructor(config) {
this.key = config?.key || '';
this.sys = { events: new (require('events').EventEmitter)() };
}
},
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();
// Stateful setData/getData so tests can read back what they wrote
this._data = {};
this.displayWidth = 32;
this.displayHeight = 32;
this.setData = jest.fn((key, value) => { this._data[key] = value; });
this.getData = jest.fn((key) => this._data[key] ?? null);
this.pulse = null;
}
destroy() {} // no-op so Unit.destroy() can safely call super.destroy()
static enable(scene, object) {
object.body = { allowGravity: false };
}
}
}
},
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 => {
const tween = {
getValue: () => 200,
stop: () => {}
};
// Fire onUpdate immediately so selection tests see setTint called
if (config.onUpdate) config.onUpdate(tween);
return tween;
}
},
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() {}
},
Graphics: class MockGraphics {
constructor() {
this.clear = jest.fn();
this.fillStyle = jest.fn().mockReturnThis();
this.fillRect = jest.fn().mockReturnThis();
this.lineStyle = jest.fn().mockReturnThis();
this.strokeRect = jest.fn().mockReturnThis();
this.setDepth = jest.fn().mockReturnThis();
this.setPosition = jest.fn().mockReturnThis();
this.setVisible = jest.fn().mockReturnThis();
this.setAlpha = jest.fn().mockReturnThis();
this.active = true;
}
destroy() {}
}
},
Input: {
Keyboard: {
KeyCodes: {
A: 65, D: 68, W: 87, S: 83, SHIFT: 16, F: 70, CTRL: 17,
},
},
Events: {
POINTER_DOWN: 'pointerdown',
POINTER_MOVE: 'pointermove',
POINTER_UP: 'pointerup',
POINTER_WHEEL: 'wheel',
},
},
Cameras: {
Controls: {
SmoothedKeyControl: class MockSmoothedKeyControl {
constructor(config) {
this.config = config;
}
update() {}
}
}
},
Geom: {
Rectangle: class MockRectangle {
constructor(x, y, w, h) {
this.x = x; this.y = y; this.width = w; this.height = h;
}
},
Circle: class MockCircle {
constructor(x, y, r) {
this.x = x; this.y = y; this.radius = r;
}
}
},
Math: {
Vector2: class MockVector2 {
constructor(x, y) { this.x = x; this.y = y; }
},
Angle: {
BetweenPoints: (a, b) => Math.atan2(b.y - a.y, b.x - a.x)
},
RadToDeg: rad => {
let deg = rad * (180 / Math.PI);
while (deg < 0) deg += 360;
return deg % 360;
},
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))
},
}));
// 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', () => ({
__esModule: true,
default: {
js: jest.fn().mockImplementation(function () {
this.setGrid = jest.fn();
this.setIterationsPerCalculation = jest.fn();
this.findPath = jest.fn((x, y, toX, toY, callback) => {
setImmediate(() => callback([{ x, y }, { x: toX, y: toY }]));
});
this.setTileAtXY = jest.fn();
this.enableDiagonals = jest.fn();
this.enableCornerCutting = jest.fn();
this.setAcceptableTiles = jest.fn();
this.setTileCost = jest.fn();
this.setAdditionalPointCost = jest.fn();
this.calculate = jest.fn();
}),
},
}));
// Suppress console errors during tests
console.error = jest.fn();