-import Code from '@hapi/code'; // assertion library
-import Lab from '@hapi/lab';
+import { describe, it, beforeEach, mock } from 'node:test';
+import assert from 'node:assert';
import Serpentity, { Component, Entity, Node, System } from '../lib/serpentity.js';
const internals = {
this.testNodes = engine.getNodes(internals.node);
this.addedCalled = true;
this.addedEngine = engine;
+ this.emittedEvents = [];
+ this.beforeCall = null;
+ this.afterCall = null;
+
+ this.changeObserver = (event) => {
+ this.emittedEvents.push(event)
+ };
+
+ this.nodeAddedObserver = ({ node }) => {
+ node.test.addEventListener('change', this.changeObserver);
+ };
+
+ this.nodeRemovedObserver = ({ node }) => {
+ node.test.removeEventListener('change', this.changeObserver);
+ };
+
+ for (const node of this.testNodes) {
+ node.test.addEventListener('change', this.changeObserver);
+ }
+
+ this.testNodes.addEventListener('nodeAdded', this.nodeAddedObserver);
+ this.testNodes.addEventListener('nodeRemoved', this.nodeRemovedObserver);
+
+ super.added(); // not needed, but takes care of coverage :P
}
removed(engine) {
+ this.testNodes.removeEventListener('nodeAdded', this.nodeAddedObserver);
+ this.nodeAddedObserver = null;
+
+ this.testNodes.removeEventListener('nodeRemoved', this.nodeRemovedObserver);
+ this.nodeRemovedObserver = null;
+
+ for (const node of this.testNodes) {
+ node.test.removeEventListener('change', this.changeObserver);
+ }
+ this.changeObserver = null;
+
this.testNodes = null;
this.removedCalled = true;
this.removedEngine = engine;
+ this.emittedEvents = null;
+ this.beforeCall = null;
+ this.afterCall = null;
+ super.removed(); // not needed, but takes care of coverage :P
}
update(dt) {
this.updateDt = dt;
for (const node of this.testNodes) {
+ this.beforeCall = this.beforeCall === null ? node.test.called : this.beforeCall;
node.test.called = true;
+ this.afterCall = this.afterCall === null ? node.test.called : this.afterCall;
node.entity.called = true;
}
while (Date.now() === this.updateCalled) { /* pass some time */ }
+ super.update(); // not needed, but takes care of coverage :P
}
},
component: class TestComponent extends Component {
test: internals.component
};
-const lab = Lab.script({
- schedule: false
-});
-
-const { beforeEach, experiment, test } = lab;
-const { expect } = Code;
-
-experiment('loading', () => {
-
- test('Serpentity should be exported', () => {
-
- expect(typeof Serpentity).to.not.be.undefined();
- });
+describe('Loading', () => {
- test('Serpentity should include the Entity class', () => {
+ it('Should export the main class', () => {
- expect(typeof Entity).to.not.be.undefined();
+ assert(typeof Serpentity === 'function');
});
- test('Serpentity should include the Component class', () => {
+ it('Should export the Entity class', () => {
- expect(typeof Component).to.not.be.undefined();
+ assert(typeof Entity === 'function');
});
- test('Serpentity should include the System class', () => {
+ it('Should export the Component class', () => {
- expect(typeof System).to.not.be.undefined();
+ assert(typeof Component === 'function');
});
- test('Serpentity should include the Node class', () => {
+ it('Should export the System class', () => {
- expect(typeof Node).to.not.be.undefined();
+ assert(typeof System === 'function');
});
- test('Serpentity should include the NodeCollection class', () => {
+ it('Should export the Node class', () => {
- expect(typeof NodeCollection).to.not.be.undefined();
+ assert(typeof Node === 'function');
});
});
-experiment('Engine Tests', () => {
+describe('Engine', () => {
beforeEach(() => {
internals.context.engine.addEntity(internals.context.emptyEntity);
});
- test('Engine should call added callback on added systems', () => {
+ it('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();
+ assert(internals.context.regularSystem.addedCalled);
+ assert(internals.context.highPrioritySystem.addedCalled);
+ assert(internals.context.lowPrioritySystem.addedCalled);
});
- test('Engine should send the engine instance in added callback', () => {
+ it('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();
+ assert(internals.context.regularSystem.addedEngine instanceof Serpentity);
+ assert(internals.context.highPrioritySystem.addedEngine instanceof Serpentity);
+ assert(internals.context.lowPrioritySystem.addedEngine instanceof Serpentity);
});
- test('Engine should not add duplicate systems', () => {
+ it('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);
+ assert(!added);
+ assert.deepEqual(newSystemsLength, originalSystemsLength);
});
- test('Engine should call update callback on added systems', () => {
+ it('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();
+ assert(!!internals.context.regularSystem.updateCalled);
+ assert(!!internals.context.highPrioritySystem.updateCalled);
+ assert(!!internals.context.lowPrioritySystem.updateCalled);
});
- test('Engine should call update callback in the order of priorities', () => {
+ it('should keep proxied object behavior as expected', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ assert(internals.context.highPrioritySystem.beforeCall === false);
+ assert(internals.context.highPrioritySystem.afterCall === true);
+ });
+
+ it('should emit an event for every changed property', () => {
+
+ internals.context.engine.update(internals.delta);
+
+ assert(internals.context.regularSystem.emittedEvents[0].property === 'called');
+ assert(internals.context.regularSystem.emittedEvents[0].from === false);
+ assert(internals.context.regularSystem.emittedEvents[0].to === true);
+ // 3 systems x 2 entities.
+ assert(internals.context.regularSystem.emittedEvents.length === 6);
+ });
+
+ it('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);
+ assert(internals.context.regularSystem.updateCalled < internals.context.lowPrioritySystem.updateCalled);
+ assert(internals.context.regularSystem.updateCalled > internals.context.highPrioritySystem.updateCalled);
});
- test('Engine should send the delta in the update callback', () => {
+ it('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);
+ assert.deepEqual(internals.context.regularSystem.updateDt, internals.delta);
+ assert.deepEqual(internals.context.highPrioritySystem.updateDt, internals.delta);
+ assert.deepEqual(internals.context.lowPrioritySystem.updateDt, internals.delta);
});
- test('System remove callback', () => {
+ it('should no longer call removed systems', () => {
const originalSystemLength = internals.context.engine.systems.length;
const originalRemoved = internals.context.engine.removeSystem(internals.context.lowPrioritySystem);
internals.context.engine.update(internals.delta);
// Check for return value
- expect(originalRemoved).to.be.true();
- expect(finalRemoved).to.be.false();
+ assert(originalRemoved);
+ assert(!finalRemoved);
// Confirm that only removed if found by checking length of systems
// array
- expect(originalSystemLength).to.be.above(intermediateSystemLength);
- expect(finalSystemLength).to.be.equal(intermediateSystemLength);
+ assert(originalSystemLength > intermediateSystemLength);
+ assert.deepEqual(finalSystemLength, 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();
+ assert(!internals.context.regularSystem.removedCalled);
+ assert(!internals.context.highPrioritySystem.removedCalled);
+ assert(!!internals.context.lowPrioritySystem.removedCalled);
// 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();
+ assert(!!internals.context.regularSystem.updateCalled);
+ assert(!!internals.context.highPrioritySystem.updateCalled);
+ assert(!internals.context.lowPrioritySystem.updateCalled);
});
- test('Entity node selection', () => {
+ it('should only call nodes in selected node collections', () => {
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();
+ assert(!!internals.context.firstEntity._components[0].called);
+ assert(!!internals.context.secondEntity._components[0].called);
// 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();
+ assert(!!internals.context.firstEntity.called);
+ assert(!!internals.context.secondEntity.called);
+ assert(!internals.context.emptyEntity.called);
});
- test('Entity node removal', () => {
+ it('should stop showing removed entities', () => {
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();
+ assert(!!internals.context.firstEntity._components[0].called);
+ assert(!internals.context.secondEntity._components[0].called);
- expect(!!internals.context.firstEntity.called).to.be.true();
- expect(!!internals.context.secondEntity.called).to.be.false();
- expect(!!internals.context.emptyEntity.called).to.be.false();
+ assert(!!internals.context.firstEntity.called);
+ assert(!internals.context.secondEntity.called);
+ assert(!internals.context.emptyEntity.called);
});
- test('Entity should not add duplicate components', () => {
+ it('should not add duplicate components to entities', () => {
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);
+ assert(!result);
+ assert.deepEqual(newComponentsLength, originalComponentsLength);
});
- test('Entity should allow access to components by class', () => {
+ it('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);
+ assert(firstComponent instanceof internals.component);
+ assert.deepEqual(emptyComponent, undefined);
});
- test('Engine should not add duplicate entities', () => {
+ it('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();
+ assert(!added);
- expect(originalEntitiesLength).to.be.equal(finalEntitiesLength);
+ assert.deepEqual(finalEntitiesLength, originalEntitiesLength);
});
- test('Engine should remove entities', () => {
+ it('should remove entities', () => {
const originalEntityLength = internals.context.engine.entities.length;
const originalRemoved = internals.context.engine.removeEntity(internals.context.firstEntity);
internals.context.engine.update(internals.delta);
// Check for return value
- expect(originalRemoved).to.be.true();
- expect(finalRemoved).to.be.false();
+ assert(originalRemoved);
+ assert(!finalRemoved);
// Confirm that only removed if found by checking length of systems
// array
- expect(originalEntityLength).to.be.above(intermediateEntityLength);
- expect(finalEntityLength).to.be.equal(intermediateEntityLength);
+ assert(originalEntityLength > intermediateEntityLength);
+ assert.deepEqual(finalEntityLength, intermediateEntityLength);
// Ensure callback is sent
- expect(!!internals.context.firstEntity.called).to.be.false();
- expect(!!internals.context.secondEntity.called).to.be.true();
+ assert(!internals.context.firstEntity.called);
+ assert(!!internals.context.secondEntity.called);
});
-});
-Lab.report(lab).then((result) => process.exit(result.code));
+ it('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;
+
+ assert(!added);
+
+ assert.deepEqual(finalEntitiesLength, originalEntitiesLength);
+ });
+});