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