- 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)
697 lines
30 KiB
Plaintext
697 lines
30 KiB
Plaintext
FAIL tests/unit/ProjectileSprite.test.js
|
||
ProjectileSprite
|
||
✕ sets velocity toward target angle (16 ms)
|
||
✕ destroys itself when off-screen (1 ms)
|
||
✕ tints blue for player faction and red for enemy faction (2 ms)
|
||
✕ applies damage and destroys on hit (1 ms)
|
||
|
||
● ProjectileSprite › sets velocity toward target angle
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:78:15)
|
||
|
||
● ProjectileSprite › destroys itself when off-screen
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:90:15)
|
||
|
||
● ProjectileSprite › tints blue for player faction and red for enemy faction
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:98:21)
|
||
|
||
● ProjectileSprite › applies damage and destroys on hit
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:116:15)
|
||
|
||
PASS tests/unit/EconomySystem.test.js
|
||
EconomySystem
|
||
initPlayer
|
||
✓ registers a player with default resources (8 ms)
|
||
✓ registers a player with custom starting resources (3 ms)
|
||
✓ emits economy:updated on registration (3 ms)
|
||
✓ partial defaults fill missing keys (1 ms)
|
||
getResources
|
||
✓ returns undefined for unregistered player (2 ms)
|
||
✓ returns a snapshot of the resource object (2 ms)
|
||
canAfford
|
||
✓ returns true when player has enough resources (1 ms)
|
||
✓ returns false when player lacks fuel (1 ms)
|
||
✓ returns false when player lacks ammo (7 ms)
|
||
✓ returns false for unknown player (2 ms)
|
||
✓ null/undefined cost keys are treated as free (1 ms)
|
||
✓ emits economy:purchaseFailed on failure (5 ms)
|
||
deduct
|
||
✓ deducts fuel and ammo correctly (22 ms)
|
||
✓ does not change resources on insufficient funds (2 ms)
|
||
✓ emits economy:updated on success (1 ms)
|
||
✓ partial deduct works (only fuel) (1 ms)
|
||
addIncome
|
||
✓ adds income to existing player (5 ms)
|
||
✓ auto-initialises player if not yet registered (13 ms)
|
||
✓ emits economy:incomeReceived and economy:updated (2 ms)
|
||
✓ skips null fields gracefully (3 ms)
|
||
update
|
||
✓ does not throw when called (2 ms)
|
||
✓ _lastTick advances after enough time (1 ms)
|
||
✓ _lastTick does not advance within same tick interval (1 ms)
|
||
destroy
|
||
✓ clears all players and event listeners (3 ms)
|
||
|
||
PASS tests/CombatSystem.test.js
|
||
CombatSystem
|
||
acquireTarget
|
||
✓ should return null when no enemies in range (9 ms)
|
||
✓ should return closest enemy when multiple in range (3 ms)
|
||
✓ should filter out dead enemies (3 ms)
|
||
canHit
|
||
✓ should return false for friendly fire (2 ms)
|
||
✓ should return false for dead target (5 ms)
|
||
✓ should return false when out of range (5 ms)
|
||
✓ should return true when all conditions met (1 ms)
|
||
applyDamage
|
||
✓ should apply damage with armor reduction (9 ms)
|
||
✓ should apply minimum 1 damage (2 ms)
|
||
✓ should apply critical hit multiplier (1 ms)
|
||
fireProjectile
|
||
✓ creates a ProjectileSprite and adds it to the group (6 ms)
|
||
✓ tints enemy projectiles red (2 ms)
|
||
✓ emits combat:projectileHit on _onHit (3 ms)
|
||
|
||
PASS tests/unit/BuildingRenderer.test.js
|
||
BuildingRenderer
|
||
✓ constructor creates buildings container with correct depth (8 ms)
|
||
✓ render() creates rectangle with Barracks color #4a90d9 (3 ms)
|
||
✓ render() creates rectangle with VehicleDepot color #8b4513 (1 ms)
|
||
✓ render() creates rectangle with Logistics color #d4a017 (4 ms)
|
||
✓ render() creates rectangle with AmmoFactory color #d94a4a (2 ms)
|
||
✓ render() creates rectangle with CommandCenter color #ffd700 (6 ms)
|
||
✓ render() calls orchestrator.registerBuilding() (6 ms)
|
||
✓ render() wires pointerdown for selection (2 ms)
|
||
✓ update() sets CONSTRUCTING alpha to 0.4 (2 ms)
|
||
✓ update() sets ACTIVE alpha to 1.0 (2 ms)
|
||
✓ update() pulses PRODUCING alpha between 0.7 and 1.0 (6 ms)
|
||
✓ select() shows selection highlight around building (3 ms)
|
||
✓ deselect() hides selection highlight (27 ms)
|
||
✓ destroyBuilding() unregisters from orchestrator and destroys graphics (1 ms)
|
||
✓ destroy() cleans up all buildings and container (4 ms)
|
||
|
||
PASS tests/unit/DeathHandling.test.js
|
||
Death Handling
|
||
DYING state — opacity tween
|
||
✓ infantry DYING onEnter adds opacity tween via scene.tweens.add (15 ms)
|
||
✓ infantry DYING onEnter sets dead flag and stops movement (4 ms)
|
||
✓ tank DYING onEnter adds opacity tween with same parameters (7 ms)
|
||
Corpse / smoke-puff effect
|
||
✓ infantry death spawns a Graphics smoke puff at unit position (6 ms)
|
||
✓ tank death spawns a Graphics smoke puff at tank position (6 ms)
|
||
✓ smoke puff tweens scale and alpha over 300ms (2 ms)
|
||
✓ smoke puff is destroyed after its tween completes (3 ms)
|
||
Kill event
|
||
✓ infantry DYING emits unit:killed after 500ms (4 ms)
|
||
✓ tank DYING emits unit:killed after 500ms (8 ms)
|
||
✓ unit:killed payload includes entity reference (18 ms)
|
||
Cleanup
|
||
✓ DYING onEnter removes unit from its parent container (3 ms)
|
||
✓ unit is destroyed after kill event (via tween onComplete) (5 ms)
|
||
✓ scene shutdown destroys all tracked effects (2 ms)
|
||
|
||
PASS tests/unit/CombatSystem.test.js
|
||
CombatSystem
|
||
constructor
|
||
✓ initializes projectiles group and damage modifiers (9 ms)
|
||
✓ teamManager is stored (1 ms)
|
||
acquireTarget
|
||
✓ returns null when enemy container has no units (3 ms)
|
||
✓ returns null when all enemies are dead (2 ms)
|
||
✓ uses per-entity weaponRange from components.combat (2 ms)
|
||
✓ falls back to components.combat.range when weaponRange absent (3 ms)
|
||
✓ finds closest enemy within range (2 ms)
|
||
✓ filters by fov cone (1 ms)
|
||
✓ prioritizes weakest when specified (6 ms)
|
||
✓ returns null for null enemy container (2 ms)
|
||
canHit
|
||
✓ returns false for null entities (2 ms)
|
||
✓ returns false for friendly fire (same container) (3 ms)
|
||
✓ returns false for dead target (2 ms)
|
||
✓ returns false when target is out of range (1 ms)
|
||
applyDamage
|
||
✓ deals damage reducing health (2 ms)
|
||
✓ returns 0 for dead entity (1 ms)
|
||
✓ armor reduces damage taken (1 ms)
|
||
✓ deals at least 1 damage (1 ms)
|
||
✓ calls handleDeath when health drops to 0 (1 ms)
|
||
✓ emits combat:unitDamaged on scene (1 ms)
|
||
fireProjectile
|
||
✓ returns null for invalid entities (1 ms)
|
||
✓ creates a ProjectileSprite and stashes data (5 ms)
|
||
✓ sets projectile data (damage, damageType, attacker, target) (5 ms)
|
||
update
|
||
✓ handles empty projectile group (1 ms)
|
||
✓ destroys expired projectiles (8 ms)
|
||
✓ destroys inactive projectiles (11 ms)
|
||
✓ auto-engage finds target and fires projectile (2 ms)
|
||
✓ auto-engage no-op when container is empty
|
||
✓ auto-engage respects per-entity range config (1 ms)
|
||
hasLineOfSight
|
||
✓ returns true when no rockLayer (3 ms)
|
||
✓ returns true when worldToTileXY returns null (1 ms)
|
||
|
||
PASS tests/VictoryScene.test.js
|
||
VictoryScene
|
||
create
|
||
✓ should create dark overlay background (7 ms)
|
||
✓ should show VICTORY when local player is winner (3 ms)
|
||
✓ should show DEFEAT when local player is not winner (4 ms)
|
||
✓ should display elapsed time formatted as mm:ss (2 ms)
|
||
✓ should display unit kill count (2 ms)
|
||
✓ should display buildings built count (5 ms)
|
||
✓ should display CP captured count (16 ms)
|
||
✓ should create a Play Again button (2 ms)
|
||
interaction
|
||
✓ should launch Server_Connector scene on play again click (3 ms)
|
||
|
||
/root/restitution/src/systems/PathfindingSystem.js:199
|
||
const sx = clamp(Math.round(startTileX), this.grid[0].length - 1);
|
||
^
|
||
|
||
[TypeError: Cannot read properties of undefined (reading 'length')]
|
||
|
||
Node.js v22.22.3
|
||
PASS tests/WinCondition.test.js
|
||
WinCondition
|
||
initialization
|
||
✓ should set threshold to 100 by default (3 ms)
|
||
✓ should accept custom threshold (1 ms)
|
||
✓ should track game start time (1 ms)
|
||
✓ should register combat:unitDamaged listener on economy events (1 ms)
|
||
victory detection
|
||
✓ should emit game:victory when a player reaches threshold (1 ms)
|
||
✓ should emit victory only once per game (1 ms)
|
||
✓ should not emit victory when no player has reached threshold (1 ms)
|
||
✓ should detect victory for the correct player when multiple exist (1 ms)
|
||
stats tracking
|
||
✓ should increment unitsKilled on combat:unitDamaged when target dies (1 ms)
|
||
✓ should not increment unitsKilled when target does not die (2 ms)
|
||
✓ should increment buildingsBuilt on building:spawned (1 ms)
|
||
✓ should increment cpCaptured on economy:incomeReceived with capturePoints (4 ms)
|
||
elapsed time
|
||
✓ should calculate elapsed time from game start to victory (4 ms)
|
||
cleanup
|
||
✓ should remove listeners on destroy (1 ms)
|
||
|
||
PASS tests/unit/EntityStateMachine.test.js
|
||
EntityStateMachine
|
||
constructor
|
||
✓ stores entity and machine config (2 ms)
|
||
✓ default initial state is IDLING
|
||
send
|
||
✓ sends event to service when available (1 ms)
|
||
✓ sends event with context (1 ms)
|
||
✓ does not throw when service is null (1 ms)
|
||
✓ does not throw when service has no send method (1 ms)
|
||
getState
|
||
✓ returns service state value when available (4 ms)
|
||
✓ falls back to _currentState when service is null (1 ms)
|
||
✓ falls back to _currentState when service.state is null (4 ms)
|
||
state transitions
|
||
✓ starts in IDLING (1 ms)
|
||
✓ can simulate state changes via send (1 ms)
|
||
tick
|
||
✓ does not throw when called
|
||
✓ can be called multiple times without side effects (1 ms)
|
||
✓ auto-engage sends ATTACK when combat finds a target (1 ms)
|
||
✓ auto-engage no-op when no target found (1 ms)
|
||
✓ auto-engage no-op when state is not IDLING (2 ms)
|
||
destroy
|
||
✓ stops service if it has a stop method (1 ms)
|
||
✓ handles service without stop method gracefully (1 ms)
|
||
✓ handles null service gracefully
|
||
edge cases
|
||
✓ handles empty machineConfig (4 ms)
|
||
✓ handles rapid send calls (1 ms)
|
||
|
||
PASS tests/Unit.test.js
|
||
Unit
|
||
Component Access
|
||
✓ should have health component (18 ms)
|
||
✓ should have owner component (2 ms)
|
||
✓ should have combat component (6 ms)
|
||
✓ should update component with setComponent (5 ms)
|
||
Damage System
|
||
✓ should apply damage with armor reduction (2 ms)
|
||
✓ should apply minimum 1 damage (2 ms)
|
||
✓ should emit unit:damaged event (2 ms)
|
||
✓ should mark unit as dead when health reaches 0 (1 ms)
|
||
✓ should not damage if already dead (5 ms)
|
||
Heal System
|
||
✓ should heal unit (2 ms)
|
||
✓ should not exceed max HP (5 ms)
|
||
Combat
|
||
✓ should return true when target in range (2 ms)
|
||
✓ should return false when target out of range (12 ms)
|
||
✓ should return false when target is dead (1 ms)
|
||
✓ should attack target when in range (11 ms)
|
||
✓ should not attack when target out of range (5 ms)
|
||
✓ should respect fire rate (2 ms)
|
||
Selection
|
||
✓ should select unit (1 ms)
|
||
✓ should unselect unit (9 ms)
|
||
✓ should tint based on team (1 ms)
|
||
Movement
|
||
✓ should move to tile (1 ms)
|
||
✓ should set target tile data (1 ms)
|
||
✓ should orient to target (5 ms)
|
||
State Machine
|
||
✓ should initialize state machine (1 ms)
|
||
✓ should tick state machine in preUpdate (1 ms)
|
||
Death
|
||
✓ should trigger death when health reaches 0 (3 ms)
|
||
✓ should cleanup on destroy (1 ms)
|
||
|
||
PASS tests/unit/BuildMenu.test.js
|
||
BuildMenu
|
||
✓ constructor creates a container fixed to camera (4 ms)
|
||
✓ container has 4 building buttons (2 ms)
|
||
✓ each button has a text label (3 ms)
|
||
✓ each button shows its resource cost (2 ms)
|
||
✓ clicking a button calls onSelect with building type (2 ms)
|
||
✓ updateAffordability disables buttons player cannot afford (3 ms)
|
||
✓ updateAffordability enables affordable buttons (2 ms)
|
||
✓ destroy cleans up container and buttons (2 ms)
|
||
|
||
PASS tests/Map_Player.test.js
|
||
Map_Player — F-key spawn
|
||
✓ F-key spawns unit in scene (17 ms)
|
||
✓ spawned unit is selectable (5 ms)
|
||
Interface — init(false)
|
||
✓ init(false) does NOT wire pointer DOWN/MOVE/UP or create pathfinder (2 ms)
|
||
|
||
PASS tests/unit/ControlPointManager.test.js
|
||
ControlPointManager
|
||
constructor
|
||
✓ creates 4 control points (4 ms)
|
||
✓ accepts optional teamManager parameter (2 ms)
|
||
✓ falls back to scene.teamManager when no teamManager passed (2 ms)
|
||
✓ CPs are at clearing centers converted to world coords (2 ms)
|
||
✓ tileToWorldXY is called for each CP (2 ms)
|
||
✓ each CP has type=controlPoint, captureTime=60000, radius=5 tiles (2 ms)
|
||
update
|
||
✓ ticks every CP (3 ms)
|
||
CP income
|
||
✓ each CP is wired with the economy system on construction (2 ms)
|
||
✓ does NOT call addIncome when state is not CAPTURED (2 ms)
|
||
✓ does NOT call addIncome when owner is null (1 ms)
|
||
destroy
|
||
✓ destroys all CPs and clears the array (5 ms)
|
||
|
||
PASS test/systems/TeamManager.test.js
|
||
TeamManager
|
||
✓ createTeam returns Team with id/color/name, duplicate returns same object (2 ms)
|
||
✓ getTeam returns undefined for unknown teamId (1 ms)
|
||
✓ setPlayerTeam maps playerId to teamId (1 ms)
|
||
✓ getTeamPlayers returns set of players in team (1 ms)
|
||
✓ addUnit sets unit data, adds to Team.units, appears in getTeamUnits (5 ms)
|
||
✓ removeUnit removes from one team, re-add to another works (1 ms)
|
||
✓ getUnitTeam returns null for unregistered unit (15 ms)
|
||
✓ getAllUnits returns flat array of all units (4 ms)
|
||
✓ getAllUnitsGrouped returns Map keyed by teamId (2 ms)
|
||
✓ addBuilding/removeBuilding/getBuildingTeam follow same pattern (1 ms)
|
||
✓ isEnemy/isSameTeam: same team=false, different=true, null=not enemy (2 ms)
|
||
✓ getEnemyUnits returns all units NOT in given team (1 ms)
|
||
|
||
PASS tests/unit/ProductionPanel.test.js
|
||
ProductionPanel
|
||
✓ constructor creates container fixed to camera at bottom-right (4 ms)
|
||
✓ constructor starts hidden (2 ms)
|
||
✓ show() renders building name and state (5 ms)
|
||
✓ show() renders Add Unit buttons for production building (19 ms)
|
||
✓ show() with COMMAND_CENTER has no unit buttons (1 ms)
|
||
✓ clicking Add Unit deducts cost and adds to queue (6 ms)
|
||
✓ clicking Add Unit when unaffordable does not deduct or queue (4 ms)
|
||
✓ Add Unit disabled when queue is full (5 ms)
|
||
✓ hide() hides container and clears selection (6 ms)
|
||
✓ destroy() cleans up container and buttons (2 ms)
|
||
✓ update() sets progress bar width based on production time (2 ms)
|
||
✓ show() with new building clears previous unit buttons (5 ms)
|
||
✓ scene pointerdown outside panel hides it (7 ms)
|
||
|
||
PASS tests/unit/BuildingPlacer.test.js
|
||
BuildingPlacer
|
||
✓ constructor creates a hidden ghost sprite (5 ms)
|
||
✓ constructor wires pointermove and pointerdown (3 ms)
|
||
✓ startPlacement shows ghost and remembers building type (4 ms)
|
||
✓ updateGhost snaps ghost to tile grid (5 ms)
|
||
✓ isValidPlacement returns true for open ground (1 ms)
|
||
✓ isValidPlacement returns false for collision tiles (rock) (8 ms)
|
||
✓ isValidPlacement returns false for water tiles (1 ms)
|
||
✓ isValidPlacement returns false when overlapping existing buildings (1 ms)
|
||
✓ ghost tint is green when placement is valid (7 ms)
|
||
✓ ghost tint is red when placement is invalid (1 ms)
|
||
✓ tryPlace deducts resources via economy (1 ms)
|
||
✓ tryPlace calls orchestrator.registerBuilding (1 ms)
|
||
✓ tryPlace emits building:placed event (1 ms)
|
||
✓ tryPlace hides ghost after placement (1 ms)
|
||
✓ tryPlace returns false when player cannot afford (1 ms)
|
||
✓ cancel hides ghost and resets state (1 ms)
|
||
✓ destroy removes ghost and unregisters listeners (2 ms)
|
||
|
||
PASS test/scenes/Map_Player.test.js
|
||
Map_Player — TeamManager rewiring
|
||
✓ does NOT create goodGuys / badGuys Phaser containers (16 ms)
|
||
✓ creates TeamManager with three teams for FFA (9 ms)
|
||
✓ UnitFactory receives teamManager as second arg (8 ms)
|
||
✓ spawn calls use teamId strings (team-A, team-B) (7 ms)
|
||
✓ 3-team spawn: each team gets different color (10 ms)
|
||
✓ ProductionPanel onProductionComplete passes teamId to UnitFactory (6 ms)
|
||
✓ CombatSystem and ControlPointManager receive teamManager (7 ms)
|
||
✓ combat resolves correctly across all 3 teams (18 ms)
|
||
✓ control point captures correctly with 3 teams (8 ms)
|
||
✓ UI shows correct team colors for all 3 teams (5 ms)
|
||
|
||
PASS tests/unit/CaptureProgressUI.test.js
|
||
CaptureProgressUI
|
||
✓ update lazily draws a bar for a new CP (3 ms)
|
||
✓ bar fill reflects capture progress at 50% (1 ms)
|
||
✓ bar fill width scales with progress fraction (1 ms)
|
||
✓ getColor returns grey for NEUTRAL (8 ms)
|
||
✓ getColor returns yellow for CONTESTED (1 ms)
|
||
✓ getColor returns green when captured by player (4 ms)
|
||
✓ getColor returns red when captured by enemy (1 ms)
|
||
✓ destroy(cp) removes bar from Map and destroys graphics (10 ms)
|
||
✓ shutdown destroys all bars and clears Map (2 ms)
|
||
|
||
PASS test/systems/ControlPointStateMachine.test.js
|
||
ControlPointStateMachine (multi-team)
|
||
✓ registerUnitContainers is NOT a method on the instance (2 ms)
|
||
✓ getUnitsInRadius counts units per teamId via TeamManager (2 ms)
|
||
✓ NEUTRAL → CONTESTED when units from multiple teams present (2 ms)
|
||
✓ CONTESTED → CAPTURED when one team reaches majority (progress hits 100) (2 ms)
|
||
✓ owner stored as teamId string after capture (2 ms)
|
||
✓ constructor accepts teamManager via config (1 ms)
|
||
✓ falls back to scene.teamManager if no teamManager in config (3 ms)
|
||
|
||
PASS test/systems/CombatSystem.test.js
|
||
CombatSystem (multi-team)
|
||
✓ constructor accepts TeamManager, not containers (3 ms)
|
||
✓ registerUnitContainers removed from public API (6 ms)
|
||
✓ _processCombatGroup iterates all team groups (4 ms)
|
||
✓ _checkOverlap checks all teams (2 ms)
|
||
✓ acquireTarget returns enemy unit from any team (2 ms)
|
||
✓ friendly fire prevented by team check in canHit (1 ms)
|
||
✓ projectile from team-A hits team-B and team-C units (2 ms)
|
||
✓ projectile from team-A does NOT hit team-A units (1 ms)
|
||
|
||
PASS test/entities/Unit.test.js
|
||
Unit (team-aware)
|
||
✓ getEnemyContainer removed — no longer exists on prototype (3 ms)
|
||
✓ getFriendlyContainer removed — no longer exists on prototype (1 ms)
|
||
✓ select() uses teamId for tint color via TeamManager (2 ms)
|
||
✓ isEnemyOf delegates to TeamManager (1 ms)
|
||
✓ Unit without teamId has null team data (1 ms)
|
||
|
||
PASS tests/EconomySystem.test.js
|
||
EconomySystem
|
||
initPlayer
|
||
✓ should initialize player with default resources (1 ms)
|
||
✓ should initialize player with custom resources (1 ms)
|
||
canAfford
|
||
✓ should return true when player has enough resources (1 ms)
|
||
✓ should return false when player lacks fuel
|
||
✓ should return false when player lacks ammo (1 ms)
|
||
✓ should return false for non-existent player
|
||
deduct
|
||
✓ should deduct resources and return true (1 ms)
|
||
✓ should not deduct and return false when insufficient resources (1 ms)
|
||
✓ should emit economy:purchaseFailed on insufficient resources (1 ms)
|
||
addIncome
|
||
✓ should add income to player resources (1 ms)
|
||
✓ should auto-initialize player if not exists (1 ms)
|
||
✓ should emit economy:incomeReceived and economy:updated (1 ms)
|
||
update
|
||
✓ should track elapsed time for income tick guard (1 ms)
|
||
✓ should not fire tick before 1000ms
|
||
|
||
PASS tests/unit/HealthBarSystem.test.js
|
||
HealthBarSystem
|
||
✓ drawBar creates a bar whose width matches unit displayWidth (2 ms)
|
||
✓ getColor returns green at 100%, yellow at 50%, red below 25% (1 ms)
|
||
✓ bar is hidden at full HP and shown when damaged (4 ms)
|
||
✓ destroy(unit) destroys the health bar graphics (1 ms)
|
||
✓ flash sets tint color briefly (1 ms)
|
||
|
||
PASS tests/unit/ResourceBar.test.js
|
||
ResourceBar
|
||
✓ constructor creates a Phaser Text HUD element (4 ms)
|
||
✓ HUD text is fixed to camera (scrollFactor 0) (8 ms)
|
||
✓ HUD text is at top-left by default (1 ms)
|
||
✓ updateFromResources shows fuel / ammo / CP (2 ms)
|
||
✓ format includes emoji/color icons (12 ms)
|
||
✓ setEconomySystem wires economy:updated listener (3 ms)
|
||
✓ economy:updated auto-updates the bar (1 ms)
|
||
✓ ignores economy:updated for other players (1 ms)
|
||
✓ bar text shows default starting resources on creation (5 ms)
|
||
✓ destroy removes the Phaser Text object (1 ms)
|
||
|
||
PASS tests/unit/BuildingIncome.test.js
|
||
BuildingStateMachine income tick
|
||
✓ tick returns null when no income configured (1 ms)
|
||
✓ tick returns income when ACTIVE and first tick (1 ms)
|
||
✓ tick returns null when CONSTRUCTING even with income config (1 ms)
|
||
✓ income is rate-limited to once per 1000ms (15 ms)
|
||
✓ startActive flag sets state to ACTIVE immediately (1 ms)
|
||
✓ playerId is stored and accessible (4 ms)
|
||
✓ income with both fuel and ammo (1 ms)
|
||
SystemOrchestrator building income wiring
|
||
✓ simulated update loop adds income for ACTIVE buildings only (1 ms)
|
||
✓ simulated update loop skips CONSTRUCTING and no-income buildings (1 ms)
|
||
|
||
PASS test/systems/UnitFactory.test.js
|
||
UnitFactory (with TeamManager)
|
||
✓ constructor stores scene and teamManager (2 ms)
|
||
✓ spawnInfantry adds unit to correct team via TeamManager (1 ms)
|
||
✓ spawnTank adds unit to correct team via TeamManager
|
||
✓ no references to scene.goodGuys or scene.badGuys remain
|
||
✓ skin selection: team index 0 -> Ukrainian infantry (1 ms)
|
||
✓ skin selection: team index 0 -> Ukrainian tank
|
||
✓ skin selection: team index 1 -> Russian infantry (1 ms)
|
||
✓ skin selection: team index 1 -> Russian tank (1 ms)
|
||
✓ skin selection: team index 2+ -> Russian fallback infantry
|
||
✓ skin selection: team index 2+ -> Russian fallback tank (6 ms)
|
||
|
||
PASS test/systems/ControlPointManager.test.js
|
||
ControlPointManager
|
||
✓ constructor accepts teamManager parameter (6 ms)
|
||
✓ update delegates tick to each CP using teamManager for unit counts (2 ms)
|
||
✓ each CP has a reference to teamManager on construction (2 ms)
|
||
✓ falls back to scene.teamManager when no teamManager passed (1 ms)
|
||
|
||
PASS test/entities/components/OwnerComponent.test.js
|
||
OwnerComponent (team-aware)
|
||
✓ teamId replaces good/enemy string (1 ms)
|
||
✓ isEnemy delegates to TeamManager (1 ms)
|
||
✓ isSameTeam delegates to TeamManager
|
||
|
||
FAIL tests/App.test.js
|
||
● Test suite failed to run
|
||
|
||
Cannot find module 'bufferutil' from 'node_modules/colyseus.js/dist/colyseus.js'
|
||
|
||
Require stack:
|
||
node_modules/colyseus.js/dist/colyseus.js
|
||
src/systems/ColyseusClient.js
|
||
src/components/app.jsx
|
||
tests/App.test.js
|
||
|
||
> 1 | import { Client } from "colyseus.js";
|
||
| ^
|
||
2 |
|
||
3 | class ColyseusClient {
|
||
4 | constructor() {
|
||
|
||
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/index.js:895:11)
|
||
at node_modules/colyseus.js/dist/colyseus.js:3:242
|
||
at Object.<anonymous> (node_modules/colyseus.js/dist/colyseus.js:6:3)
|
||
at Object.require (src/systems/ColyseusClient.js:1:1)
|
||
at Object.require (src/components/app.jsx:5:1)
|
||
at Object.require (tests/App.test.js:38:1)
|
||
|
||
PASS tests/LobbyScreen.test.js
|
||
LobbyScreen
|
||
✓ renders Create Game and Join Game buttons on initial load (38 ms)
|
||
✓ calls createGame and shows CircularProgress when Create is clicked (45 ms)
|
||
✓ displays the invite code and player count after creation (27 ms)
|
||
✓ shows Join mode with TextField when Join Game is clicked (26 ms)
|
||
✓ disables Join button until exactly 4 characters are entered (20 ms)
|
||
✓ shows Alert error when joinGame rejects (38 ms)
|
||
✓ calls onGameStart when 2+ players connect after creating (11 ms)
|
||
|
||
/root/restitution/src/systems/PathfindingSystem.js:199
|
||
const sx = clamp(Math.round(startTileX), this.grid[0].length - 1);
|
||
^
|
||
|
||
[TypeError: Cannot read properties of undefined (reading 'length')]
|
||
|
||
Node.js v22.22.3
|
||
/root/restitution/src/systems/PathfindingSystem.js:199
|
||
const sx = clamp(Math.round(startTileX), this.grid[0].length - 1);
|
||
^
|
||
|
||
[TypeError: Cannot read properties of undefined (reading 'length')]
|
||
|
||
Node.js v22.22.3
|
||
/root/restitution/src/systems/PathfindingSystem.js:199
|
||
const sx = clamp(Math.round(startTileX), this.grid[0].length - 1);
|
||
^
|
||
|
||
[TypeError: Cannot read properties of undefined (reading 'length')]
|
||
|
||
Node.js v22.22.3
|
||
FAIL tests/unit/PathfindingSystem.test.js
|
||
● Test suite failed to run
|
||
|
||
Jest worker encountered 4 child process exceptions, exceeding retry limit
|
||
|
||
at ChildProcessWorker.initialize (node_modules/jest-runner/node_modules/jest-worker/build/index.js:801:21)
|
||
|
||
Summary of all failing tests
|
||
FAIL tests/unit/ProjectileSprite.test.js
|
||
● ProjectileSprite › sets velocity toward target angle
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:78:15)
|
||
|
||
● ProjectileSprite › destroys itself when off-screen
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:90:15)
|
||
|
||
● ProjectileSprite › tints blue for player faction and red for enemy faction
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:98:21)
|
||
|
||
● ProjectileSprite › applies damage and destroys on hit
|
||
|
||
TypeError: this.body.setVelocity is not a function
|
||
|
||
22 | // Velocity from angle (set directly — velocityFromAngle in constructor
|
||
23 | // fires before body is fully initialized, leaving velocity at 0,0)
|
||
> 24 | this.body.setVelocity(
|
||
| ^
|
||
25 | Math.cos(angle) * speed,
|
||
26 | Math.sin(angle) * speed
|
||
27 | );
|
||
|
||
at new setVelocity (src/systems/ProjectileSprite.js:24:15)
|
||
at Object.<anonymous> (tests/unit/ProjectileSprite.test.js:116:15)
|
||
|
||
FAIL tests/App.test.js
|
||
● Test suite failed to run
|
||
|
||
Cannot find module 'bufferutil' from 'node_modules/colyseus.js/dist/colyseus.js'
|
||
|
||
Require stack:
|
||
node_modules/colyseus.js/dist/colyseus.js
|
||
src/systems/ColyseusClient.js
|
||
src/components/app.jsx
|
||
tests/App.test.js
|
||
|
||
> 1 | import { Client } from "colyseus.js";
|
||
| ^
|
||
2 |
|
||
3 | class ColyseusClient {
|
||
4 | constructor() {
|
||
|
||
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/index.js:895:11)
|
||
at node_modules/colyseus.js/dist/colyseus.js:3:242
|
||
at Object.<anonymous> (node_modules/colyseus.js/dist/colyseus.js:6:3)
|
||
at Object.require (src/systems/ColyseusClient.js:1:1)
|
||
at Object.require (src/components/app.jsx:5:1)
|
||
at Object.require (tests/App.test.js:38:1)
|
||
|
||
FAIL tests/unit/PathfindingSystem.test.js
|
||
● Test suite failed to run
|
||
|
||
Jest worker encountered 4 child process exceptions, exceeding retry limit
|
||
|
||
at ChildProcessWorker.initialize (node_modules/jest-runner/node_modules/jest-worker/build/index.js:801:21)
|
||
|
||
|
||
Test Suites: 3 failed, 28 passed, 31 total
|
||
Tests: 4 failed, 332 passed, 336 total
|
||
Snapshots: 0 total
|
||
Time: 4.307 s
|
||
Ran all test suites.
|