bard-legends-framework 1.11.2 → 1.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -324,10 +324,67 @@ Resolve via constructor injection or `Service.get(X)`:
324
324
 
325
325
  ## Testing
326
326
 
327
- - Call `BardLegendsHardReset.hardReset()` in `beforeEach` (alongside `actions-lib`'s
328
- `ActionLib.hardReset()`) to clear entity/singleton/DI registries between tests.
329
- - Drive frames with `UpdateCycle.triggerUpdateTick(delta)`; use fake timers for `wait`/animations.
330
- - Because delivery is synchronous (`actions-lib`), most assertions need no awaiting.
327
+ All testing utilities live on `BardLegendsTestingHelper`.
328
+
329
+ ### Concepts
330
+
331
+ - **`BardLegendsTestingHelper.hardReset()`** — call it first thing in `beforeEach`. It calls
332
+ `ActionLib.hardReset()` itself (no separate call needed) and resets all per-test state: DI
333
+ service instances, entity instances, singleton creation latches, and mock gateway bindings.
334
+ **Import-time registrations survive the reset** — `@ServiceDecorator`/`@EntityDecorator`
335
+ metadata and real controller→gateway bindings stay valid, so modules imported once at the top
336
+ of a test file keep working across tests.
337
+ - **Module-level (gateway) tests** — a module's public surface is its gateway. Test through
338
+ `Service.get(TheGateway)`: side-effect-import the module's own `*.controller.ts` (this registers
339
+ the real controller and pulls in its services/entities), and do **not** import any `*.view.ts`
340
+ (unimported views never activate, so entities render nothing).
341
+ - **`BardLegendsTestingHelper.bindMockControllerToGateway(Gateway, mock)`** — fills a foreign
342
+ module's gateway with a mock controller, exactly like `@ControllerDecorator` does for a real
343
+ one. Since the foreign module's controller file is never imported in the test, its gateway
344
+ prototype is empty; the mock's prototype methods fill it. Colliding with an already registered
345
+ method throws. All mock bindings are removed on the next `hardReset()`, so bind them in every
346
+ test's setup, after the reset.
347
+ - Mocks are plain classes matching the foreign controller's shape (`implements
348
+ Partial<TheController>`): mutable public fields for state, regular class methods reading them.
349
+ Methods must live on the prototype — arrow-function fields are not bound.
350
+
351
+ ### Sample (module test with a mocked neighbor)
352
+
353
+ ```ts
354
+ import './sample.controller'; // registers the module under test
355
+
356
+ describe('SampleController', () => {
357
+ let otherGatewayMock: OtherGatewayMock;
358
+ let sampleGateway: InstanceType<typeof SampleGateway>;
359
+
360
+ beforeEach(() => {
361
+ BardLegendsTestingHelper.hardReset(); // always first
362
+ otherGatewayMock = new OtherGatewayMock();
363
+ BardLegendsTestingHelper.bindMockControllerToGateway(OtherGateway, otherGatewayMock);
364
+ sampleGateway = Service.get(SampleGateway);
365
+ });
366
+
367
+ test('does the thing', () => {
368
+ otherGatewayMock.value = 42; // steer the mock through its state fields
369
+ expect(sampleGateway.doTheThing()).toEqual(42);
370
+ });
371
+ });
372
+ ```
373
+
374
+ ### Do / Don't
375
+
376
+ ```ts
377
+ // DO: hardReset() first in beforeEach, then bind mocks, then Service.get(...) fresh handles.
378
+ // DO: test a module only through its gateway; mock every foreign gateway it reaches.
379
+ // DO: drive frames with UpdateCycle.triggerUpdateTick(delta); use fake timers for wait/animations.
380
+ // DO: assert synchronously — actions-lib delivery is synchronous, most flows need no awaiting.
381
+
382
+ // DON'T: import another module's *.controller.ts or any *.view.ts in a module test.
383
+ // DON'T: use arrow-function fields as mock methods — only prototype methods are bound.
384
+ // DON'T: hold gateway/service references across tests — re-fetch after each hardReset().
385
+ // DON'T: put state in services — controllers/services persist across hardReset by design;
386
+ // per-test state belongs in entities, which do reset.
387
+ ```
331
388
 
332
389
  ---
333
390
 
package/dist/index.d.mts CHANGED
@@ -79,11 +79,6 @@ declare abstract class SingletonEntity extends Entity {
79
79
  */
80
80
  declare function Gateway<TController>(): new () => TController;
81
81
 
82
- declare class BardLegendsHardReset {
83
- static readonly onHardReset: actions_lib.Notifier<void>;
84
- static hardReset(): void;
85
- }
86
-
87
82
  type SceneClassType = new (...services: any[]) => Scene<any, any>;
88
83
  type SceneInput<T> = T extends Scene<infer I, any> ? I : never;
89
84
  declare function SceneDecorator(): (SceneClass: SceneClassType) => any;
@@ -113,6 +108,23 @@ declare class Service {
113
108
  static get<T extends Service>(ServiceClass: new (...args: any[]) => T): T;
114
109
  }
115
110
 
111
+ declare class BardLegendsTestingHelper {
112
+ static readonly onHardReset: actions_lib.Notifier<void>;
113
+ static hardReset(): void;
114
+ /**
115
+ * Binds a partial mock controller to a gateway, exactly like `@ControllerDecorator` does for a
116
+ * real controller — its prototype methods are installed onto the gateway prototype. Intended for
117
+ * tests where the module behind a gateway is not loaded: the real controller never registers
118
+ * (controllers load via the eager `*.controller.ts` glob, which tests skip), so the mock's
119
+ * methods fill the gateway.
120
+ *
121
+ * The mock's methods must be prototype methods (regular class methods, not arrow-function
122
+ * fields). Colliding with an already registered method throws. All mock bindings are removed on
123
+ * `hardReset()`, so bind mocks in each test's setup after the reset.
124
+ */
125
+ static bindMockControllerToGateway<TController>(GatewayClass: new () => TController, mock: Partial<TController>): void;
126
+ }
127
+
116
128
  declare class UpdateCycle {
117
129
  static readonly beforeSceneUpdateAction: actions_lib.Notifier<{
118
130
  time: number;
@@ -962,7 +974,7 @@ declare class PathFinder {
962
974
  }
963
975
 
964
976
  declare class VectorFieldPathFinder {
965
- constructor(_targetArea: Rectangle, _availabilityGrid: Grid<boolean>);
977
+ constructor(_targetArea: Rectangle, availabilityGrid: Grid<boolean>);
966
978
  getDirectionToTarget(startingPosition: Vector): Radian | undefined;
967
979
  }
968
980
 
@@ -1330,4 +1342,4 @@ declare abstract class MovablePhysicsEntity extends PhysicsEntity {
1330
1342
  convertToDTO(): PhysicsBodyDTO;
1331
1343
  }
1332
1344
 
1333
- export { AdvancedSound, type AdvancedSoundOptions, AnimationFlicker, AnimationInterpolationFunctions, type AnimationOptions, Animations, AnimationsCompletionHandlingType, Animator, type AnimatorAnimation, type AssetDefinition, type AudioAppearTransitionType, BORDER_MATERIAL_NAME, BardLegendsHardReset, type BlendMode, type BorderProperties, Camera, CameraGateway, type CircleShapeData, ClosestAvailableSpaceHelper, type CollisionDetails, type CollisionReport, Container, ContainerEventType, ControllerDecorator, type CreateDashedLineOptions, type CreatePhysicsWorldRequestDTO, Cursor, DEFAULT_SHADER_RESOLUTION, DeltaTime, DisplayObjectArray, type DisplayObjectArrayOptions, type DropShadowOptions, Entity, EntityDecorator, type EntityID, type ExplosionHit, FadeInContent, type FadeInContentOptions, FocusingAnimation, Game, Gateway, type GlowEffectOptions, type GlowOptions, type GlowingShapeDefinition, Graphics, type HitTestOptions, ImmovablePhysicsEntity, type InteractingBodyGroups, type KeyboardKeyInfo, KeyboardService, type MapSizeDTO, type MaterialContactDefinition, type MaterialDefinition, MenuEntity, type MenuOptions, MenuUI, MenuView, MouseService, MouseTargetFocusService, MovableEntity, MovablePhysicsEntity, Music, MusicPlayer, type MusicPlayerOptions, type PartialTextOptions, PathFinder, type PathFinderResult, type PhysicsBodyDTO, PhysicsEntity, type PhysicsEntityDefinition, type PhysicsExplosionOptions, PhysicsGateway, PhysicsShapeType, type PhysicsWorldID, Placeholder, type PolygonDefinition, type PolygonShapeData, ROTATIONAL_SPEED_LIMIT, type RayCast, type RayCastHit, ReAnimateHandlingType, type RectangleShapeData, RichText, type RichTextOptions, type RichTextRectangleCut, type RichTextStyles, SOUND_TRANSITION_DURATION, SPEED_LIMIT, Scene, SceneDecorator, ScrollAreaUI, ScrollDirection, type ScrollInContentOptions, ScrollMaskUI, Service, ServiceDecorator, type SetParentOptions, type ShapeDefinition, SingletonEntity, SlideInContent, SlideInContentByIndex, SlideStateAnimation, SlideStateAnimationState, Sound, type SoundDefinition, type SoundID, type SoundIDRegistry, type SoundOptions, Sprite, type SpriteDefinition, type SpriteID, type SpriteIDRegistry, StateAnimation, type StateAnimationOptions, type StateAnimationState, Text, type TextAlignment, type TextOptions, ThroughEmptyStateAnimation, UpdatableContainer, UpdateCycle, VectorFieldPathFinder, VectorSet, View, ViewDecorator, type ViewDecoratorMeta };
1345
+ export { AdvancedSound, type AdvancedSoundOptions, AnimationFlicker, AnimationInterpolationFunctions, type AnimationOptions, Animations, AnimationsCompletionHandlingType, Animator, type AnimatorAnimation, type AssetDefinition, type AudioAppearTransitionType, BORDER_MATERIAL_NAME, BardLegendsTestingHelper, type BlendMode, type BorderProperties, Camera, CameraGateway, type CircleShapeData, ClosestAvailableSpaceHelper, type CollisionDetails, type CollisionReport, Container, ContainerEventType, ControllerDecorator, type CreateDashedLineOptions, type CreatePhysicsWorldRequestDTO, Cursor, DEFAULT_SHADER_RESOLUTION, DeltaTime, DisplayObjectArray, type DisplayObjectArrayOptions, type DropShadowOptions, Entity, EntityDecorator, type EntityID, type ExplosionHit, FadeInContent, type FadeInContentOptions, FocusingAnimation, Game, Gateway, type GlowEffectOptions, type GlowOptions, type GlowingShapeDefinition, Graphics, type HitTestOptions, ImmovablePhysicsEntity, type InteractingBodyGroups, type KeyboardKeyInfo, KeyboardService, type MapSizeDTO, type MaterialContactDefinition, type MaterialDefinition, MenuEntity, type MenuOptions, MenuUI, MenuView, MouseService, MouseTargetFocusService, MovableEntity, MovablePhysicsEntity, Music, MusicPlayer, type MusicPlayerOptions, type PartialTextOptions, PathFinder, type PathFinderResult, type PhysicsBodyDTO, PhysicsEntity, type PhysicsEntityDefinition, type PhysicsExplosionOptions, PhysicsGateway, PhysicsShapeType, type PhysicsWorldID, Placeholder, type PolygonDefinition, type PolygonShapeData, ROTATIONAL_SPEED_LIMIT, type RayCast, type RayCastHit, ReAnimateHandlingType, type RectangleShapeData, RichText, type RichTextOptions, type RichTextRectangleCut, type RichTextStyles, SOUND_TRANSITION_DURATION, SPEED_LIMIT, Scene, SceneDecorator, ScrollAreaUI, ScrollDirection, type ScrollInContentOptions, ScrollMaskUI, Service, ServiceDecorator, type SetParentOptions, type ShapeDefinition, SingletonEntity, SlideInContent, SlideInContentByIndex, SlideStateAnimation, SlideStateAnimationState, Sound, type SoundDefinition, type SoundID, type SoundIDRegistry, type SoundOptions, Sprite, type SpriteDefinition, type SpriteID, type SpriteIDRegistry, StateAnimation, type StateAnimationOptions, type StateAnimationState, Text, type TextAlignment, type TextOptions, ThroughEmptyStateAnimation, UpdatableContainer, UpdateCycle, VectorFieldPathFinder, VectorSet, View, ViewDecorator, type ViewDecoratorMeta };
package/dist/index.d.ts CHANGED
@@ -79,11 +79,6 @@ declare abstract class SingletonEntity extends Entity {
79
79
  */
80
80
  declare function Gateway<TController>(): new () => TController;
81
81
 
82
- declare class BardLegendsHardReset {
83
- static readonly onHardReset: actions_lib.Notifier<void>;
84
- static hardReset(): void;
85
- }
86
-
87
82
  type SceneClassType = new (...services: any[]) => Scene<any, any>;
88
83
  type SceneInput<T> = T extends Scene<infer I, any> ? I : never;
89
84
  declare function SceneDecorator(): (SceneClass: SceneClassType) => any;
@@ -113,6 +108,23 @@ declare class Service {
113
108
  static get<T extends Service>(ServiceClass: new (...args: any[]) => T): T;
114
109
  }
115
110
 
111
+ declare class BardLegendsTestingHelper {
112
+ static readonly onHardReset: actions_lib.Notifier<void>;
113
+ static hardReset(): void;
114
+ /**
115
+ * Binds a partial mock controller to a gateway, exactly like `@ControllerDecorator` does for a
116
+ * real controller — its prototype methods are installed onto the gateway prototype. Intended for
117
+ * tests where the module behind a gateway is not loaded: the real controller never registers
118
+ * (controllers load via the eager `*.controller.ts` glob, which tests skip), so the mock's
119
+ * methods fill the gateway.
120
+ *
121
+ * The mock's methods must be prototype methods (regular class methods, not arrow-function
122
+ * fields). Colliding with an already registered method throws. All mock bindings are removed on
123
+ * `hardReset()`, so bind mocks in each test's setup after the reset.
124
+ */
125
+ static bindMockControllerToGateway<TController>(GatewayClass: new () => TController, mock: Partial<TController>): void;
126
+ }
127
+
116
128
  declare class UpdateCycle {
117
129
  static readonly beforeSceneUpdateAction: actions_lib.Notifier<{
118
130
  time: number;
@@ -962,7 +974,7 @@ declare class PathFinder {
962
974
  }
963
975
 
964
976
  declare class VectorFieldPathFinder {
965
- constructor(_targetArea: Rectangle, _availabilityGrid: Grid<boolean>);
977
+ constructor(_targetArea: Rectangle, availabilityGrid: Grid<boolean>);
966
978
  getDirectionToTarget(startingPosition: Vector): Radian | undefined;
967
979
  }
968
980
 
@@ -1330,4 +1342,4 @@ declare abstract class MovablePhysicsEntity extends PhysicsEntity {
1330
1342
  convertToDTO(): PhysicsBodyDTO;
1331
1343
  }
1332
1344
 
1333
- export { AdvancedSound, type AdvancedSoundOptions, AnimationFlicker, AnimationInterpolationFunctions, type AnimationOptions, Animations, AnimationsCompletionHandlingType, Animator, type AnimatorAnimation, type AssetDefinition, type AudioAppearTransitionType, BORDER_MATERIAL_NAME, BardLegendsHardReset, type BlendMode, type BorderProperties, Camera, CameraGateway, type CircleShapeData, ClosestAvailableSpaceHelper, type CollisionDetails, type CollisionReport, Container, ContainerEventType, ControllerDecorator, type CreateDashedLineOptions, type CreatePhysicsWorldRequestDTO, Cursor, DEFAULT_SHADER_RESOLUTION, DeltaTime, DisplayObjectArray, type DisplayObjectArrayOptions, type DropShadowOptions, Entity, EntityDecorator, type EntityID, type ExplosionHit, FadeInContent, type FadeInContentOptions, FocusingAnimation, Game, Gateway, type GlowEffectOptions, type GlowOptions, type GlowingShapeDefinition, Graphics, type HitTestOptions, ImmovablePhysicsEntity, type InteractingBodyGroups, type KeyboardKeyInfo, KeyboardService, type MapSizeDTO, type MaterialContactDefinition, type MaterialDefinition, MenuEntity, type MenuOptions, MenuUI, MenuView, MouseService, MouseTargetFocusService, MovableEntity, MovablePhysicsEntity, Music, MusicPlayer, type MusicPlayerOptions, type PartialTextOptions, PathFinder, type PathFinderResult, type PhysicsBodyDTO, PhysicsEntity, type PhysicsEntityDefinition, type PhysicsExplosionOptions, PhysicsGateway, PhysicsShapeType, type PhysicsWorldID, Placeholder, type PolygonDefinition, type PolygonShapeData, ROTATIONAL_SPEED_LIMIT, type RayCast, type RayCastHit, ReAnimateHandlingType, type RectangleShapeData, RichText, type RichTextOptions, type RichTextRectangleCut, type RichTextStyles, SOUND_TRANSITION_DURATION, SPEED_LIMIT, Scene, SceneDecorator, ScrollAreaUI, ScrollDirection, type ScrollInContentOptions, ScrollMaskUI, Service, ServiceDecorator, type SetParentOptions, type ShapeDefinition, SingletonEntity, SlideInContent, SlideInContentByIndex, SlideStateAnimation, SlideStateAnimationState, Sound, type SoundDefinition, type SoundID, type SoundIDRegistry, type SoundOptions, Sprite, type SpriteDefinition, type SpriteID, type SpriteIDRegistry, StateAnimation, type StateAnimationOptions, type StateAnimationState, Text, type TextAlignment, type TextOptions, ThroughEmptyStateAnimation, UpdatableContainer, UpdateCycle, VectorFieldPathFinder, VectorSet, View, ViewDecorator, type ViewDecoratorMeta };
1345
+ export { AdvancedSound, type AdvancedSoundOptions, AnimationFlicker, AnimationInterpolationFunctions, type AnimationOptions, Animations, AnimationsCompletionHandlingType, Animator, type AnimatorAnimation, type AssetDefinition, type AudioAppearTransitionType, BORDER_MATERIAL_NAME, BardLegendsTestingHelper, type BlendMode, type BorderProperties, Camera, CameraGateway, type CircleShapeData, ClosestAvailableSpaceHelper, type CollisionDetails, type CollisionReport, Container, ContainerEventType, ControllerDecorator, type CreateDashedLineOptions, type CreatePhysicsWorldRequestDTO, Cursor, DEFAULT_SHADER_RESOLUTION, DeltaTime, DisplayObjectArray, type DisplayObjectArrayOptions, type DropShadowOptions, Entity, EntityDecorator, type EntityID, type ExplosionHit, FadeInContent, type FadeInContentOptions, FocusingAnimation, Game, Gateway, type GlowEffectOptions, type GlowOptions, type GlowingShapeDefinition, Graphics, type HitTestOptions, ImmovablePhysicsEntity, type InteractingBodyGroups, type KeyboardKeyInfo, KeyboardService, type MapSizeDTO, type MaterialContactDefinition, type MaterialDefinition, MenuEntity, type MenuOptions, MenuUI, MenuView, MouseService, MouseTargetFocusService, MovableEntity, MovablePhysicsEntity, Music, MusicPlayer, type MusicPlayerOptions, type PartialTextOptions, PathFinder, type PathFinderResult, type PhysicsBodyDTO, PhysicsEntity, type PhysicsEntityDefinition, type PhysicsExplosionOptions, PhysicsGateway, PhysicsShapeType, type PhysicsWorldID, Placeholder, type PolygonDefinition, type PolygonShapeData, ROTATIONAL_SPEED_LIMIT, type RayCast, type RayCastHit, ReAnimateHandlingType, type RectangleShapeData, RichText, type RichTextOptions, type RichTextRectangleCut, type RichTextStyles, SOUND_TRANSITION_DURATION, SPEED_LIMIT, Scene, SceneDecorator, ScrollAreaUI, ScrollDirection, type ScrollInContentOptions, ScrollMaskUI, Service, ServiceDecorator, type SetParentOptions, type ShapeDefinition, SingletonEntity, SlideInContent, SlideInContentByIndex, SlideStateAnimation, SlideStateAnimationState, Sound, type SoundDefinition, type SoundID, type SoundIDRegistry, type SoundOptions, Sprite, type SpriteDefinition, type SpriteID, type SpriteIDRegistry, StateAnimation, type StateAnimationOptions, type StateAnimationState, Text, type TextAlignment, type TextOptions, ThroughEmptyStateAnimation, UpdatableContainer, UpdateCycle, VectorFieldPathFinder, VectorSet, View, ViewDecorator, type ViewDecoratorMeta };