+ test('Serpentity should include the Node class', () => {
+
+ expect(typeof Node).to.not.be.undefined();
+ });
+
+ test('Serpentity should include the NodeCollection class', () => {
+
+ expect(typeof NodeCollection).to.not.be.undefined();
+ });
+});
+
+experiment('Engine Tests', () => {
+
+ beforeEach(() => {
+
+ internals.context.engine = new Serpentity();
+
+ internals.context.regularSystem = new internals.system();
+ internals.context.highPrioritySystem = new internals.system();
+ internals.context.lowPrioritySystem = new internals.system();
+
+ internals.context.firstEntity = new Entity();
+ internals.context.firstEntity.addComponent(new internals.component());
+ internals.context.secondEntity = new Entity();
+ internals.context.secondEntity.addComponent(new internals.component());
+ internals.context.emptyEntity = new Entity();
+
+ // Add entity before the systems
+ internals.context.engine.addEntity(internals.context.firstEntity);
+
+ internals.context.engine.addSystem(internals.context.regularSystem, 100);
+ internals.context.engine.addSystem(internals.context.highPrioritySystem, 0);
+ internals.context.engine.addSystem(internals.context.lowPrioritySystem, 1000);
+
+ // Add entity after the systems
+ internals.context.engine.addEntity(internals.context.secondEntity);
+ internals.context.engine.addEntity(internals.context.emptyEntity);
+ });
+
+ test('Engine should call added callback on added systems', () => {
+
+ // Ensure the added callback is being called
+ expect(internals.context.regularSystem.addedCalled).to.be.true();
+ expect(internals.context.highPrioritySystem.addedCalled).to.be.true();
+ expect(internals.context.lowPrioritySystem.addedCalled).to.be.true();
+ });
+
+ test('Engine should send the engine instance in added callback', () => {
+
+ // Ensure the added callback is sending the engine
+ expect(internals.context.regularSystem.addedEngine instanceof Serpentity).to.be.true();
+ expect(internals.context.highPrioritySystem.addedEngine instanceof Serpentity).to.be.true();
+ expect(internals.context.lowPrioritySystem.addedEngine instanceof Serpentity).to.be.true();
+ });
+
+ test('Engine should not add duplicate systems', () => {
+
+ const originalSystemsLength = internals.context.engine.systems.length;
+ const added = internals.context.engine.addSystem(internals.context.regularSystem, 0);
+ const newSystemsLength = internals.context.engine.systems.length;
+
+ // Ensure we don't add the same system twice
+ expect(added).to.be.false();
+ expect(originalSystemsLength).to.be.equal(newSystemsLength);
+ });
+
+ test('Engine should call update callback on added systems', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ // Ensure update function called
+ expect(!!internals.context.regularSystem.updateCalled).to.be.true();
+ expect(!!internals.context.highPrioritySystem.updateCalled).to.be.true();
+ expect(!!internals.context.lowPrioritySystem.updateCalled).to.be.true();
+ });
+
+ test('Engine should call update callback in the order of priorities', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ // Ensure order of priorities
+ expect(internals.context.regularSystem.updateCalled).to.be.lessThan(internals.context.lowPrioritySystem.updateCalled);
+ expect(internals.context.regularSystem.updateCalled).to.be.greaterThan(internals.context.highPrioritySystem.updateCalled);
+ });
+
+ test('Engine should send the delta in the update callback', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ // Ensure delta is being sent
+ expect(internals.context.regularSystem.updateDt).to.be.equal(internals.delta);
+ expect(internals.context.highPrioritySystem.updateDt).to.be.equal(internals.delta);
+ expect(internals.context.lowPrioritySystem.updateDt).to.be.equal(internals.delta);
+ });
+
+ test('System remove callback', () => {
+
+ const originalSystemLength = internals.context.engine.systems.length;
+ const originalRemoved = internals.context.engine.removeSystem(internals.context.lowPrioritySystem);
+ const intermediateSystemLength = internals.context.engine.systems.length;
+ const finalRemoved = internals.context.engine.removeSystem(internals.context.lowPrioritySystem);
+ const finalSystemLength = internals.context.engine.systems.length;
+ internals.context.engine.update(internals.delta);
+
+ // Check for return value
+ expect(originalRemoved).to.be.true();
+ expect(finalRemoved).to.be.false();
+
+ // Confirm that only removed if found by checking length of systems
+ // array
+ expect(originalSystemLength).to.be.above(intermediateSystemLength);
+ expect(finalSystemLength).to.be.equal(intermediateSystemLength);
+
+ // Ensure callback is sent
+ expect(!!internals.context.regularSystem.removedCalled).to.be.false();
+ expect(!!internals.context.highPrioritySystem.removedCalled).to.be.false();
+ expect(!!internals.context.lowPrioritySystem.removedCalled).to.be.true();
+
+ // Ensure update is no longer sent
+ expect(!!internals.context.regularSystem.updateCalled).to.be.true();
+ expect(!!internals.context.highPrioritySystem.updateCalled).to.be.true();
+ expect(!!internals.context.lowPrioritySystem.updateCalled).to.be.false();
+ });
+
+ test('Entity node selection', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ // Ensure component is called for each entity
+ expect(!!internals.context.firstEntity._components[0].called).to.be.true();
+ expect(!!internals.context.secondEntity._components[0].called).to.be.true();
+
+ // Ensure entity not in node collection not called
+ expect(!!internals.context.firstEntity.called).to.be.true();
+ expect(!!internals.context.secondEntity.called).to.be.true();
+ expect(!!internals.context.emptyEntity.called).to.be.false();
+ });
+
+ test('Entity node removal', () => {
+
+ internals.context.engine.removeEntity(internals.context.secondEntity);
+ internals.context.engine.update(internals.delta);
+
+ expect(!!internals.context.firstEntity._components[0].called).to.be.true();
+ expect(!!internals.context.secondEntity._components[0].called).to.be.false();
+
+ expect(!!internals.context.firstEntity.called).to.be.true();
+ expect(!!internals.context.secondEntity.called).to.be.false();
+ expect(!!internals.context.emptyEntity.called).to.be.false();
+ });
+
+ test('Entity should not add duplicate components', () => {
+
+ const originalComponentsLength = internals.context.secondEntity._components.length;
+ const result = internals.context.secondEntity.addComponent(new internals.component());
+ const newComponentsLength = internals.context.secondEntity._components.length;
+
+ expect(result).to.be.false();
+ expect(originalComponentsLength).to.be.equal(newComponentsLength);
+ });
+
+ test('Entity should allow access to components by class', () => {
+
+ const firstComponent = internals.context.firstEntity.getComponent(internals.component);
+ const emptyComponent = internals.context.emptyEntity.getComponent(internals.component);
+
+ expect(firstComponent instanceof internals.component).to.be.true();
+ expect(emptyComponent).to.be.equal(undefined);
+ });
+
+ test('Engine should not add duplicate entities', () => {
+
+ const originalEntitiesLength = internals.context.engine.entities.length;
+ const added = internals.context.engine.addEntity(internals.context.firstEntity);
+ const finalEntitiesLength = internals.context.engine.entities.length;
+
+ expect(added).to.be.false();
+
+ expect(originalEntitiesLength).to.be.equal(finalEntitiesLength);
+ });
+
+ test('Engine should remove entities', () => {
+
+ const originalEntityLength = internals.context.engine.entities.length;
+ const originalRemoved = internals.context.engine.removeEntity(internals.context.firstEntity);
+ const intermediateEntityLength = internals.context.engine.entities.length;
+ const finalRemoved = internals.context.engine.removeEntity(internals.context.firstEntity);
+ const finalEntityLength = internals.context.engine.entities.length;
+ internals.context.engine.update(internals.delta);
+
+ // Check for return value
+ expect(originalRemoved).to.be.true();
+ expect(finalRemoved).to.be.false();
+
+ // Confirm that only removed if found by checking length of systems
+ // array
+ expect(originalEntityLength).to.be.above(intermediateEntityLength);
+ expect(finalEntityLength).to.be.equal(intermediateEntityLength);