]> git.r.bdr.sh - rbdr/serpentity/blame - test/integration.js
Merge branch 'release/3.0.0'
[rbdr/serpentity] / test / integration.js
CommitLineData
4d421669
BB
1import Code from '@hapi/code'; // assertion library
2import Lab from '@hapi/lab';
3import Serpentity, { Component, Entity, Node, System } from '../lib/serpentity.js';
3db7d198 4
b3b840f8 5const internals = {
4d421669
BB
6 context: {},
7 system: class TestSystem extends System {
b3b840f8 8 added(engine) {
3db7d198 9
b3b840f8
RBR
10 this.testNodes = engine.getNodes(internals.node);
11 this.addedCalled = true;
12 this.addedEngine = engine;
3db7d198
BB
13 }
14
b3b840f8
RBR
15 removed(engine) {
16
3db7d198 17 this.testNodes = null;
b3b840f8
RBR
18 this.removedCalled = true;
19 this.removedEngine = engine;
3db7d198
BB
20 }
21
b3b840f8 22 update(dt) {
3db7d198 23
b3b840f8
RBR
24 this.updateCalled = Date.now();
25 this.updateDt = dt;
3db7d198 26
b3b840f8
RBR
27 for (const node of this.testNodes) {
28 node.test.called = true;
29 node.entity.called = true;
30 }
3db7d198 31
b3b840f8
RBR
32 while (Date.now() === this.updateCalled) { /* pass some time */ }
33 }
34 },
4d421669 35 component: class TestComponent extends Component {
b3b840f8 36 constructor(config) {
3db7d198 37
3db7d198
BB
38 super(config);
39
b3b840f8 40 this.called = false;
3db7d198 41 }
b3b840f8 42 },
4d421669 43 node: class TestNode extends Node {},
b3b840f8
RBR
44 delta: 10
45};
3db7d198 46
b3b840f8
RBR
47// adds a component to the node
48internals.node.types = {
49 test: internals.component
50};
3db7d198 51
4d421669
BB
52const lab = Lab.script({
53 schedule: false
54});
55
56const { beforeEach, experiment, test } = lab;
57const { expect } = Code;
3db7d198 58
4d421669 59experiment('loading', () => {
3db7d198 60
4d421669 61 test('Serpentity should be exported', () => {
3db7d198 62
4d421669 63 expect(typeof Serpentity).to.not.be.undefined();
b3b840f8 64 });
3db7d198 65
4d421669 66 test('Serpentity should include the Entity class', () => {
3db7d198 67
4d421669 68 expect(typeof Entity).to.not.be.undefined();
b3b840f8 69 });
3db7d198 70
4d421669 71 test('Serpentity should include the Component class', () => {
3db7d198 72
4d421669 73 expect(typeof Component).to.not.be.undefined();
b3b840f8 74 });
3db7d198 75
4d421669 76 test('Serpentity should include the System class', () => {
3db7d198 77
4d421669 78 expect(typeof System).to.not.be.undefined();
b3b840f8 79 });
3db7d198 80
4d421669 81 test('Serpentity should include the Node class', () => {
3db7d198 82
4d421669 83 expect(typeof Node).to.not.be.undefined();
b3b840f8
RBR
84 });
85
4d421669 86 test('Serpentity should include the NodeCollection class', () => {
b3b840f8 87
4d421669 88 expect(typeof NodeCollection).to.not.be.undefined();
b3b840f8
RBR
89 });
90});
91
4d421669 92experiment('Engine Tests', () => {
b3b840f8 93
4d421669 94 beforeEach(() => {
b3b840f8 95
4d421669 96 internals.context.engine = new Serpentity();
b3b840f8 97
4d421669
BB
98 internals.context.regularSystem = new internals.system();
99 internals.context.highPrioritySystem = new internals.system();
100 internals.context.lowPrioritySystem = new internals.system();
b3b840f8 101
4d421669
BB
102 internals.context.firstEntity = new Entity();
103 internals.context.firstEntity.addComponent(new internals.component());
104 internals.context.secondEntity = new Entity();
105 internals.context.secondEntity.addComponent(new internals.component());
106 internals.context.emptyEntity = new Entity();
b3b840f8
RBR
107
108 // Add entity before the systems
4d421669 109 internals.context.engine.addEntity(internals.context.firstEntity);
b3b840f8 110
4d421669
BB
111 internals.context.engine.addSystem(internals.context.regularSystem, 100);
112 internals.context.engine.addSystem(internals.context.highPrioritySystem, 0);
113 internals.context.engine.addSystem(internals.context.lowPrioritySystem, 1000);
b3b840f8
RBR
114
115 // Add entity after the systems
4d421669
BB
116 internals.context.engine.addEntity(internals.context.secondEntity);
117 internals.context.engine.addEntity(internals.context.emptyEntity);
b3b840f8
RBR
118 });
119
4d421669 120 test('Engine should call added callback on added systems', () => {
b3b840f8
RBR
121
122 // Ensure the added callback is being called
4d421669
BB
123 expect(internals.context.regularSystem.addedCalled).to.be.true();
124 expect(internals.context.highPrioritySystem.addedCalled).to.be.true();
125 expect(internals.context.lowPrioritySystem.addedCalled).to.be.true();
b3b840f8
RBR
126 });
127
4d421669 128 test('Engine should send the engine instance in added callback', () => {
b3b840f8
RBR
129
130 // Ensure the added callback is sending the engine
4d421669
BB
131 expect(internals.context.regularSystem.addedEngine instanceof Serpentity).to.be.true();
132 expect(internals.context.highPrioritySystem.addedEngine instanceof Serpentity).to.be.true();
133 expect(internals.context.lowPrioritySystem.addedEngine instanceof Serpentity).to.be.true();
b3b840f8
RBR
134 });
135
4d421669 136 test('Engine should not add duplicate systems', () => {
b3b840f8 137
4d421669
BB
138 const originalSystemsLength = internals.context.engine.systems.length;
139 const added = internals.context.engine.addSystem(internals.context.regularSystem, 0);
140 const newSystemsLength = internals.context.engine.systems.length;
b3b840f8
RBR
141
142 // Ensure we don't add the same system twice
4d421669
BB
143 expect(added).to.be.false();
144 expect(originalSystemsLength).to.be.equal(newSystemsLength);
b3b840f8
RBR
145 });
146
4d421669 147 test('Engine should call update callback on added systems', () => {
b3b840f8 148
4d421669 149 internals.context.engine.update(internals.delta);
b3b840f8
RBR
150
151 // Ensure update function called
4d421669
BB
152 expect(!!internals.context.regularSystem.updateCalled).to.be.true();
153 expect(!!internals.context.highPrioritySystem.updateCalled).to.be.true();
154 expect(!!internals.context.lowPrioritySystem.updateCalled).to.be.true();
b3b840f8
RBR
155 });
156
4d421669 157 test('Engine should call update callback in the order of priorities', () => {
b3b840f8 158
4d421669 159 internals.context.engine.update(internals.delta);
b3b840f8
RBR
160
161 // Ensure order of priorities
4d421669
BB
162 expect(internals.context.regularSystem.updateCalled).to.be.lessThan(internals.context.lowPrioritySystem.updateCalled);
163 expect(internals.context.regularSystem.updateCalled).to.be.greaterThan(internals.context.highPrioritySystem.updateCalled);
b3b840f8
RBR
164 });
165
4d421669 166 test('Engine should send the delta in the update callback', () => {
b3b840f8 167
4d421669 168 internals.context.engine.update(internals.delta);
b3b840f8
RBR
169
170 // Ensure delta is being sent
4d421669
BB
171 expect(internals.context.regularSystem.updateDt).to.be.equal(internals.delta);
172 expect(internals.context.highPrioritySystem.updateDt).to.be.equal(internals.delta);
173 expect(internals.context.lowPrioritySystem.updateDt).to.be.equal(internals.delta);
b3b840f8
RBR
174 });
175
4d421669 176 test('System remove callback', () => {
b3b840f8 177
4d421669
BB
178 const originalSystemLength = internals.context.engine.systems.length;
179 const originalRemoved = internals.context.engine.removeSystem(internals.context.lowPrioritySystem);
180 const intermediateSystemLength = internals.context.engine.systems.length;
181 const finalRemoved = internals.context.engine.removeSystem(internals.context.lowPrioritySystem);
182 const finalSystemLength = internals.context.engine.systems.length;
183 internals.context.engine.update(internals.delta);
b3b840f8 184
4d421669
BB
185 // Check for return value
186 expect(originalRemoved).to.be.true();
187 expect(finalRemoved).to.be.false();
b3b840f8
RBR
188
189 // Confirm that only removed if found by checking length of systems
190 // array
4d421669
BB
191 expect(originalSystemLength).to.be.above(intermediateSystemLength);
192 expect(finalSystemLength).to.be.equal(intermediateSystemLength);
b3b840f8
RBR
193
194 // Ensure callback is sent
4d421669
BB
195 expect(!!internals.context.regularSystem.removedCalled).to.be.false();
196 expect(!!internals.context.highPrioritySystem.removedCalled).to.be.false();
197 expect(!!internals.context.lowPrioritySystem.removedCalled).to.be.true();
b3b840f8
RBR
198
199 // Ensure update is no longer sent
4d421669
BB
200 expect(!!internals.context.regularSystem.updateCalled).to.be.true();
201 expect(!!internals.context.highPrioritySystem.updateCalled).to.be.true();
202 expect(!!internals.context.lowPrioritySystem.updateCalled).to.be.false();
b3b840f8
RBR
203 });
204
4d421669 205 test('Entity node selection', () => {
b3b840f8 206
4d421669 207 internals.context.engine.update(internals.delta);
b3b840f8
RBR
208
209 // Ensure component is called for each entity
4d421669
BB
210 expect(!!internals.context.firstEntity._components[0].called).to.be.true();
211 expect(!!internals.context.secondEntity._components[0].called).to.be.true();
b3b840f8
RBR
212
213 // Ensure entity not in node collection not called
4d421669
BB
214 expect(!!internals.context.firstEntity.called).to.be.true();
215 expect(!!internals.context.secondEntity.called).to.be.true();
216 expect(!!internals.context.emptyEntity.called).to.be.false();
b3b840f8
RBR
217 });
218
4d421669 219 test('Entity node removal', () => {
b3b840f8 220
4d421669
BB
221 internals.context.engine.removeEntity(internals.context.secondEntity);
222 internals.context.engine.update(internals.delta);
b3b840f8 223
4d421669
BB
224 expect(!!internals.context.firstEntity._components[0].called).to.be.true();
225 expect(!!internals.context.secondEntity._components[0].called).to.be.false();
b3b840f8 226
4d421669
BB
227 expect(!!internals.context.firstEntity.called).to.be.true();
228 expect(!!internals.context.secondEntity.called).to.be.false();
229 expect(!!internals.context.emptyEntity.called).to.be.false();
b3b840f8
RBR
230 });
231
4d421669 232 test('Entity should not add duplicate components', () => {
b3b840f8 233
4d421669
BB
234 const originalComponentsLength = internals.context.secondEntity._components.length;
235 const result = internals.context.secondEntity.addComponent(new internals.component());
236 const newComponentsLength = internals.context.secondEntity._components.length;
b3b840f8 237
4d421669
BB
238 expect(result).to.be.false();
239 expect(originalComponentsLength).to.be.equal(newComponentsLength);
b3b840f8
RBR
240 });
241
4d421669 242 test('Entity should allow access to components by class', () => {
b3b840f8 243
4d421669
BB
244 const firstComponent = internals.context.firstEntity.getComponent(internals.component);
245 const emptyComponent = internals.context.emptyEntity.getComponent(internals.component);
b3b840f8 246
4d421669
BB
247 expect(firstComponent instanceof internals.component).to.be.true();
248 expect(emptyComponent).to.be.equal(undefined);
b3b840f8
RBR
249 });
250
4d421669
BB
251 test('Engine should not add duplicate entities', () => {
252
253 const originalEntitiesLength = internals.context.engine.entities.length;
254 const added = internals.context.engine.addEntity(internals.context.firstEntity);
255 const finalEntitiesLength = internals.context.engine.entities.length;
b3b840f8 256
4d421669 257 expect(added).to.be.false();
b3b840f8 258
4d421669 259 expect(originalEntitiesLength).to.be.equal(finalEntitiesLength);
b3b840f8
RBR
260 });
261
4d421669 262 test('Engine should remove entities', () => {
b3b840f8 263
4d421669
BB
264 const originalEntityLength = internals.context.engine.entities.length;
265 const originalRemoved = internals.context.engine.removeEntity(internals.context.firstEntity);
266 const intermediateEntityLength = internals.context.engine.entities.length;
267 const finalRemoved = internals.context.engine.removeEntity(internals.context.firstEntity);
268 const finalEntityLength = internals.context.engine.entities.length;
269 internals.context.engine.update(internals.delta);
b3b840f8 270
4d421669
BB
271 // Check for return value
272 expect(originalRemoved).to.be.true();
273 expect(finalRemoved).to.be.false();
b3b840f8
RBR
274
275 // Confirm that only removed if found by checking length of systems
276 // array
4d421669
BB
277 expect(originalEntityLength).to.be.above(intermediateEntityLength);
278 expect(finalEntityLength).to.be.equal(intermediateEntityLength);
b3b840f8
RBR
279
280 // Ensure callback is sent
4d421669
BB
281 expect(!!internals.context.firstEntity.called).to.be.false();
282 expect(!!internals.context.secondEntity.called).to.be.true();
3db7d198 283 });
b3b840f8 284});
4d421669
BB
285
286Lab.report(lab).then((result) => process.exit(result.code));