- 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)
160 lines
4.9 KiB
JavaScript
160 lines
4.9 KiB
JavaScript
/**
|
|
* UnitFactory tests — M1.3: deferred spawning
|
|
*
|
|
* Tests:
|
|
* 1. spawnInfantry creates Ukrainian_Rifle for player team + adds to goodGuys
|
|
* 2. spawnTank creates Ukrainian_Tank for player team + adds to goodGuys
|
|
* 3. spawnInfantry creates Russian_Rifle for enemy team + adds to badGuys
|
|
* 4. Multiple spawns don't crash
|
|
*/
|
|
|
|
// ── Module-level mocks (before any imports) ───────────────────────
|
|
|
|
// Use function declarations (hoisted) so jest.mock factories see them
|
|
function mockUnit(scene, tile) {
|
|
return {
|
|
scene,
|
|
tile,
|
|
x: tile.x * 32,
|
|
y: tile.y * 32,
|
|
name: 'unit',
|
|
};
|
|
}
|
|
|
|
jest.mock('Entities/skins/ukrainian-infantry', () => ({
|
|
__esModule: true,
|
|
default: jest.fn().mockImplementation(mockUnit),
|
|
}));
|
|
|
|
jest.mock('Entities/skins/russian-infantry', () => ({
|
|
__esModule: true,
|
|
default: jest.fn().mockImplementation(mockUnit),
|
|
}));
|
|
|
|
jest.mock('Entities/skins/ukrainian-tank', () => ({
|
|
__esModule: true,
|
|
default: jest.fn().mockImplementation(mockUnit),
|
|
}));
|
|
|
|
jest.mock('Entities/skins/russian-tank', () => ({
|
|
__esModule: true,
|
|
default: jest.fn().mockImplementation(mockUnit),
|
|
}));
|
|
|
|
// ── Imports (after mocks) ─────────────────────────────────────────
|
|
|
|
import UnitFactory from 'Systems/UnitFactory';
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
|
|
function buildMockScene() {
|
|
const goodGuysAdd = jest.fn();
|
|
const badGuysAdd = jest.fn();
|
|
|
|
return {
|
|
goodGuys: {
|
|
setName: jest.fn().mockReturnThis(),
|
|
add: goodGuysAdd,
|
|
},
|
|
badGuys: {
|
|
setName: jest.fn().mockReturnThis(),
|
|
add: badGuysAdd,
|
|
},
|
|
_goodGuysAdd: goodGuysAdd,
|
|
_badGuysAdd: badGuysAdd,
|
|
};
|
|
}
|
|
|
|
// ── Tests ─────────────────────────────────────────────────────────
|
|
|
|
describe('UnitFactory', () => {
|
|
let scene;
|
|
let factory;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
scene = buildMockScene();
|
|
factory = new UnitFactory(scene);
|
|
});
|
|
|
|
test('spawnInfantry creates Ukrainian_Rifle for player team', () => {
|
|
const tile = { x: 5, y: 5 };
|
|
const unit = factory.spawnInfantry(tile, 'player');
|
|
|
|
const Ukrainian_Rifle = require('Entities/skins/ukrainian-infantry').default;
|
|
expect(Ukrainian_Rifle).toHaveBeenCalledWith(scene, tile);
|
|
expect(unit).toBeDefined();
|
|
expect(unit.tile).toEqual(tile);
|
|
});
|
|
|
|
test('spawnTank creates Ukrainian_Tank for player team', () => {
|
|
const tile = { x: 3, y: 7 };
|
|
const unit = factory.spawnTank(tile, 'player');
|
|
|
|
const Ukrainian_Tank = require('Entities/skins/ukrainian-tank').default;
|
|
expect(Ukrainian_Tank).toHaveBeenCalledWith(scene, tile);
|
|
expect(unit).toBeDefined();
|
|
expect(unit.tile).toEqual(tile);
|
|
});
|
|
|
|
test('spawnInfantry creates Russian_Rifle for enemy team + adds to badGuys', () => {
|
|
const tile = { x: 10, y: 3 };
|
|
|
|
// Reset mock call counts after previous tests
|
|
jest.clearAllMocks();
|
|
|
|
const unit = factory.spawnInfantry(tile, 'enemy');
|
|
|
|
const Russian_Rifle = require('Entities/skins/russian-infantry').default;
|
|
const Ukrainian_Rifle = require('Entities/skins/ukrainian-infantry').default;
|
|
|
|
// Should use Russian skin, not Ukrainian
|
|
expect(Russian_Rifle).toHaveBeenCalledWith(scene, tile);
|
|
expect(Ukrainian_Rifle).not.toHaveBeenCalled();
|
|
|
|
// Should add to badGuys container
|
|
expect(scene._badGuysAdd).toHaveBeenCalledWith(unit);
|
|
expect(scene._goodGuysAdd).not.toHaveBeenCalled();
|
|
|
|
expect(unit).toBeDefined();
|
|
});
|
|
|
|
test('multiple spawns do not crash', () => {
|
|
const tiles = [
|
|
{ x: 0, y: 0 },
|
|
{ x: 1, y: 1 },
|
|
{ x: 2, y: 2 },
|
|
{ x: 3, y: 3 },
|
|
{ x: 4, y: 4 },
|
|
];
|
|
|
|
const units = [];
|
|
|
|
// Spawn a mix of player and enemy units
|
|
for (const tile of tiles) {
|
|
const unit1 = factory.spawnInfantry(tile, 'player');
|
|
const unit2 = factory.spawnTank(tile, 'player');
|
|
units.push(unit1, unit2);
|
|
}
|
|
|
|
// Spawn some enemy units too
|
|
for (let i = 0; i < 3; i++) {
|
|
const unit = factory.spawnInfantry({ x: 10 + i, y: 10 }, 'enemy');
|
|
units.push(unit);
|
|
}
|
|
|
|
// All units should be defined (no nulls)
|
|
expect(units.every((u) => u !== null && u !== undefined)).toBe(true);
|
|
expect(units.length).toBe(13); // 5 * 2 + 3
|
|
|
|
// Ukrainian skins should have been called (for player team)
|
|
const Ukrainian_Rifle = require('Entities/skins/ukrainian-infantry').default;
|
|
const Ukrainian_Tank = require('Entities/skins/ukrainian-tank').default;
|
|
const Russian_Rifle = require('Entities/skins/russian-infantry').default;
|
|
|
|
expect(Ukrainian_Rifle).toHaveBeenCalledTimes(5);
|
|
expect(Ukrainian_Tank).toHaveBeenCalledTimes(5);
|
|
expect(Russian_Rifle).toHaveBeenCalledTimes(3);
|
|
});
|
|
});
|