- 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)
161 lines
5.1 KiB
JavaScript
161 lines
5.1 KiB
JavaScript
/**
|
|
* HealthBarSystem.test.js — M2.2: Health bar overlay
|
|
*
|
|
* Tests:
|
|
* 1. drawBar — bar draws with correct width proportional to sprite displayWidth
|
|
* 2. Color transitions at thresholds — green (100%), yellow (50%), red (<25%)
|
|
* 3. Auto-hide at full HP, show on damage
|
|
* 4. destroy(unit) cleans up graphics objects
|
|
*/
|
|
|
|
import HealthBarSystem from 'Systems/HealthBarSystem';
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
|
|
function buildMockScene() {
|
|
const added = [];
|
|
return {
|
|
add: {
|
|
graphics: jest.fn(() => {
|
|
const g = {
|
|
clear: jest.fn(),
|
|
fillStyle: jest.fn().mockReturnThis(),
|
|
fillRect: jest.fn().mockReturnThis(),
|
|
lineStyle: jest.fn().mockReturnThis(),
|
|
strokeRect: jest.fn().mockReturnThis(),
|
|
setDepth: jest.fn().mockReturnThis(),
|
|
setPosition: jest.fn().mockReturnThis(),
|
|
setAlpha: jest.fn().mockReturnThis(),
|
|
setVisible: jest.fn().mockReturnThis(),
|
|
destroy: jest.fn(),
|
|
active: true,
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
added.push(g);
|
|
return g;
|
|
}),
|
|
},
|
|
_graphicsAdded: added,
|
|
};
|
|
}
|
|
|
|
function buildMockUnit(overrides = {}) {
|
|
const hp = overrides.hp ?? 100;
|
|
const maxHp = overrides.maxHp ?? 100;
|
|
|
|
return {
|
|
x: 100,
|
|
y: 200,
|
|
displayWidth: 48,
|
|
displayHeight: 48,
|
|
active: true,
|
|
_data: { health: hp, maxHp },
|
|
getData: jest.fn((key) => {
|
|
if (key === 'health') return hp;
|
|
if (key === 'maxHp') return maxHp;
|
|
return undefined;
|
|
}),
|
|
setData: jest.fn(),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
// ── Tests ─────────────────────────────────────────────────────────
|
|
|
|
describe('HealthBarSystem', () => {
|
|
let scene;
|
|
let system;
|
|
|
|
beforeEach(() => {
|
|
scene = buildMockScene();
|
|
system = new HealthBarSystem(scene);
|
|
});
|
|
|
|
afterEach(() => {
|
|
system?.destroy?.();
|
|
});
|
|
|
|
// ── 1. drawBar — correct width proportional to displayWidth ─────
|
|
test('drawBar creates a bar whose width matches unit displayWidth', () => {
|
|
const unit = buildMockUnit({ hp: 75, maxHp: 100 });
|
|
system.drawBar(unit);
|
|
|
|
expect(scene.add.graphics).toHaveBeenCalled();
|
|
const graphics = system._bars.get(unit);
|
|
expect(graphics).toBeDefined();
|
|
expect(graphics.active).toBe(true);
|
|
|
|
// drawBar should call fillRect twice: background + fill bar
|
|
expect(graphics.fillRect).toHaveBeenCalled();
|
|
|
|
// The second fillRect call (foreground bar) should have width based on health fraction
|
|
const calls = graphics.fillRect.mock.calls;
|
|
expect(calls.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
// ── 2. Color transitions at thresholds ────────────────────────
|
|
test('getColor returns green at 100%, yellow at 50%, red below 25%', () => {
|
|
expect(system.getColor(1.0)).toBe(0x00ff00);
|
|
expect(system.getColor(0.75)).toBe(0x00ff00);
|
|
expect(system.getColor(0.5)).toBe(0xffff00);
|
|
expect(system.getColor(0.25)).toBe(0xffff00);
|
|
expect(system.getColor(0.24)).toBe(0xff0000);
|
|
expect(system.getColor(0.0)).toBe(0xff0000);
|
|
});
|
|
|
|
// ── 3. Auto-hide at full HP, show on damage ───────────────────
|
|
test('bar is hidden at full HP and shown when damaged', () => {
|
|
const unit = buildMockUnit({ hp: 100, maxHp: 100 });
|
|
system.drawBar(unit);
|
|
system.update(unit);
|
|
|
|
const graphics = system._bars.get(unit);
|
|
expect(graphics).toBeDefined();
|
|
|
|
// At full HP, bar should be hidden
|
|
expect(graphics.setVisible).toHaveBeenCalledWith(false);
|
|
|
|
// Simulate damage
|
|
unit.getData = jest.fn((key) => {
|
|
if (key === 'health') return 80;
|
|
if (key === 'maxHp') return 100;
|
|
return undefined;
|
|
});
|
|
|
|
// Reset mock
|
|
graphics.setVisible.mockClear();
|
|
system.update(unit);
|
|
|
|
// After damage, bar should be visible
|
|
expect(graphics.setVisible).toHaveBeenCalledWith(true);
|
|
});
|
|
|
|
// ── 4. destroy(unit) cleans up graphics ──────────────────────
|
|
test('destroy(unit) destroys the health bar graphics', () => {
|
|
const unit = buildMockUnit({ hp: 60, maxHp: 100 });
|
|
system.drawBar(unit);
|
|
const graphics = system._bars.get(unit);
|
|
|
|
expect(graphics).toBeDefined();
|
|
const destroySpy = graphics.destroy;
|
|
|
|
system.destroy(unit);
|
|
|
|
expect(destroySpy).toHaveBeenCalled();
|
|
expect(system._bars.has(unit)).toBe(false);
|
|
});
|
|
|
|
// ── flash ─────────────────────────────────────────────────────
|
|
test('flash sets tint color briefly', () => {
|
|
const unit = buildMockUnit({ hp: 60, maxHp: 100 });
|
|
system.drawBar(unit);
|
|
const graphics = system._bars.get(unit);
|
|
|
|
system.flash(unit, 0xff0000);
|
|
|
|
// Flash should temporarily change fill style
|
|
expect(graphics.fillStyle).toHaveBeenCalled();
|
|
});
|
|
});
|