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