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