Files
restitution/tests/VictoryScene.test.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

229 lines
7.3 KiB
JavaScript

/**
* VictoryScene Unit Tests
*/
// Minimal Phaser Scene mock
jest.mock('phaser', () => ({
Scene: class MockScene {
constructor(config) {
this.key = config?.key;
this.scene = {
start: jest.fn(),
stop: jest.fn(),
};
this.events = {
emit: jest.fn(),
on: jest.fn(),
};
this.add = {
text: jest.fn(() => mockTextObj()),
rectangle: jest.fn(() => mockRectObj()),
container: jest.fn(() => mockContainerObj()),
graphics: jest.fn(() => mockGraphicsObj()),
image: jest.fn(() => mockImageObj()),
};
this.cameras = {
main: { width: 1920, height: 1080, centerX: 960, centerY: 540 },
};
this.input = {
on: jest.fn(),
off: jest.fn(),
};
this.scale = {
baseSize: { width: 1920, height: 1080 },
};
this.game = {
loop: { delta: 1000 / 60 },
};
}
},
GameObjects: {
Text: class {},
Rectangle: class {},
Container: class {},
Graphics: class {},
},
Display: {
Color: {
GetColor: jest.fn(() => 0xffffff),
},
},
}));
function mockTextObj() {
const obj = {
setOrigin: jest.fn(() => obj),
setPosition: jest.fn(() => obj),
setScale: jest.fn(() => obj),
setAlpha: jest.fn(() => obj),
setInteractive: jest.fn(() => obj),
setDepth: jest.fn(() => obj),
setStyle: jest.fn(() => obj),
setText: jest.fn(() => obj),
setVisible: jest.fn(() => obj),
destroy: jest.fn(),
on: jest.fn(() => obj),
once: jest.fn(() => obj),
off: jest.fn(() => obj),
x: 0, y: 0,
active: true, visible: true,
};
return obj;
}
function mockRectObj() {
const obj = {
setOrigin: jest.fn(() => obj),
setPosition: jest.fn(() => obj),
setFillStyle: jest.fn(() => obj),
setAlpha: jest.fn(() => obj),
setDepth: jest.fn(() => obj),
setInteractive: jest.fn(() => obj),
destroy: jest.fn(),
on: jest.fn(() => obj),
off: jest.fn(() => obj),
x: 0, y: 0,
active: true,
width: 1920, height: 1080,
};
return obj;
}
function mockContainerObj() {
const obj = {
add: jest.fn(() => obj),
setPosition: jest.fn(() => obj),
setOrigin: jest.fn(() => obj),
setAlpha: jest.fn(() => obj),
setDepth: jest.fn(() => obj),
setVisible: jest.fn(() => obj),
destroy: jest.fn(),
x: 0, y: 0,
active: true, visible: true,
list: [],
};
return obj;
}
function mockGraphicsObj() {
const obj = {
fillStyle: jest.fn(() => obj),
fillRect: jest.fn(() => obj),
setAlpha: jest.fn(() => obj),
setDepth: jest.fn(() => obj),
clear: jest.fn(() => obj),
destroy: jest.fn(),
};
return obj;
}
function mockImageObj() {
const obj = {
setOrigin:
jest.fn(() => obj),
setPosition: jest.fn(() => obj),
setScale: jest.fn(() => obj),
setAlpha: jest.fn(() => obj),
setDepth: jest.fn(() => obj),
setInteractive: jest.fn(() => obj),
destroy: jest.fn(),
on: jest.fn(() => obj),
x: 0, y: 0,
active: true,
};
return obj;
}
import VictoryScene from '../src/scenes/VictoryScene';
describe('VictoryScene', () => {
let scene;
beforeEach(() => {
scene = new VictoryScene();
});
describe('create', () => {
it('should create dark overlay background', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
expect(scene.add.rectangle).toHaveBeenCalledWith(960, 540, 1920, 1080, 0x000000);
});
it('should show VICTORY when local player is winner', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasVictory = textCalls.some((c) => typeof c[2] === 'string' && c[2].includes('VICTORY'));
expect(hasVictory).toBe(true);
});
it('should show DEFEAT when local player is not winner', () => {
scene.create({ winnerPlayerId: 'player2', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasDefeat = textCalls.some((c) => typeof c[2] === 'string' && c[2].includes('DEFEAT'));
expect(hasDefeat).toBe(true);
});
it('should display elapsed time formatted as mm:ss', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 125000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasTime = textCalls.some((c) => {
const text = typeof c[2] === 'string' ? c[2] : c[2]?.text;
return typeof text === 'string' && /02:05/.test(text);
});
expect(hasTime).toBe(true);
});
it('should display unit kill count', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 7, buildingsBuilt: 2, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasKills = textCalls.some((c) => {
const text = typeof c[2] === 'string' ? c[2] : c[2]?.text;
return typeof text === 'string' && text.includes('7');
});
expect(hasKills).toBe(true);
});
it('should display buildings built count', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 4, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasBuildings = textCalls.some((c) => {
const text = typeof c[2] === 'string' ? c[2] : c[2]?.text;
return typeof text === 'string' && text.includes('4');
});
expect(hasBuildings).toBe(true);
});
it('should display CP captured count', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
const textCalls = scene.add.text.mock.calls;
const hasCp = textCalls.some((c) => {
const text = typeof c[2] === 'string' ? c[2] : c[2]?.text;
return typeof text === 'string' && text.includes('100');
});
expect(hasCp).toBe(true);
});
it('should create a Play Again button', () => {
scene.create({ winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } });
expect(scene.add.text).toHaveBeenCalled();
const textCalls = scene.add.text.mock.calls;
const hasPlayAgain = textCalls.some((c) => {
const text = typeof c[2] === 'string' ? c[2] : c[2]?.text;
return typeof text === 'string' && text.toLowerCase().includes('play again');
});
expect(hasPlayAgain).toBe(true);
});
});
describe('interaction', () => {
it('should launch Server_Connector scene on play again click', () => {
const data = { winnerPlayerId: 'player1', localPlayerId: 'player1', stats: { elapsedMs: 120000, unitsKilled: 5, buildingsBuilt: 2, cpCaptured: 100 } };
scene.create(data);
const startSpy = jest.spyOn(scene.scene, 'start');
scene._onPlayAgain();
expect(startSpy).toHaveBeenCalledWith('Server_Connector');
});
});
});