phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
451 lines (397 loc) • 14.2 kB
JavaScript
var EventEmitter = require('eventemitter3');
var UpdateList = require('../../src/gameobjects/UpdateList');
function createMockScene ()
{
var events = new EventEmitter();
return {
sys: {
events: events
}
};
}
function createMockGameObject (active)
{
return {
active: (active === undefined) ? true : active,
preUpdate: vi.fn(),
destroy: vi.fn()
};
}
describe('UpdateList', function ()
{
var scene;
var updateList;
beforeEach(function ()
{
scene = createMockScene();
updateList = new UpdateList(scene);
});
afterEach(function ()
{
vi.restoreAllMocks();
});
describe('constructor', function ()
{
it('should set checkQueue to true', function ()
{
expect(updateList.checkQueue).toBe(true);
});
it('should store a reference to the scene', function ()
{
expect(updateList.scene).toBe(scene);
});
it('should store a reference to scene.sys as systems', function ()
{
expect(updateList.systems).toBe(scene.sys);
});
it('should initialise with an empty active list', function ()
{
expect(updateList.getActive()).toEqual([]);
});
it('should initialise with length zero', function ()
{
expect(updateList.length).toBe(0);
});
it('should register a boot listener on scene.sys.events', function ()
{
// After emitting boot, the destroy listener should be registered on sys events
scene.sys.events.emit('boot');
var hasDestroyListener = scene.sys.events.listenerCount('destroy') > 0;
expect(hasDestroyListener).toBe(true);
});
it('should register a start listener on scene.sys.events', function ()
{
var listeners = scene.sys.events.listenerCount('start');
expect(listeners).toBeGreaterThan(0);
});
});
describe('add', function ()
{
it('should add an item to the pending list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
expect(updateList._pending).toContain(obj);
});
it('should return the added item', function ()
{
var obj = createMockGameObject();
var result = updateList.add(obj);
expect(result).toBe(obj);
});
it('should move item from pending to active after update', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
expect(updateList.getActive()).toContain(obj);
});
it('should not add a duplicate item when already active (checkQueue is true)', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.add(obj);
// _toProcess should remain 0 as duplicate was blocked
expect(updateList._toProcess).toBe(0);
});
it('should not add a duplicate item when already pending', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.add(obj);
expect(updateList._pending.length).toBe(1);
});
it('should increase length after update', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
expect(updateList.length).toBe(1);
});
});
describe('remove', function ()
{
it('should return the removed item', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
var result = updateList.remove(obj);
expect(result).toBe(obj);
});
it('should queue an active item for destruction', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.remove(obj);
expect(updateList._destroy).toContain(obj);
});
it('should remove item from active list after update', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.remove(obj);
updateList.update();
expect(updateList.getActive()).not.toContain(obj);
});
it('should remove a pending item directly without waiting for update', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.remove(obj);
expect(updateList._pending).not.toContain(obj);
});
});
describe('removeAll', function ()
{
it('should return the UpdateList instance', function ()
{
var result = updateList.removeAll();
expect(result).toBe(updateList);
});
it('should mark all active items for destruction', function ()
{
var obj1 = createMockGameObject();
var obj2 = createMockGameObject();
updateList.add(obj1);
updateList.add(obj2);
updateList.update();
updateList.removeAll();
expect(updateList._destroy).toContain(obj1);
expect(updateList._destroy).toContain(obj2);
});
it('should result in an empty active list after update', function ()
{
var obj1 = createMockGameObject();
var obj2 = createMockGameObject();
updateList.add(obj1);
updateList.add(obj2);
updateList.update();
updateList.removeAll();
updateList.update();
expect(updateList.getActive().length).toBe(0);
});
});
describe('update', function ()
{
it('should return the active list', function ()
{
var result = updateList.update();
expect(Array.isArray(result)).toBe(true);
});
it('should return the same active array reference', function ()
{
var active = updateList.getActive();
var result = updateList.update();
expect(result).toBe(active);
});
it('should move pending items to active', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
var active = updateList.update();
expect(active).toContain(obj);
});
it('should reset _toProcess to zero after processing', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
expect(updateList._toProcess).toBe(0);
});
it('should clear the pending list after processing', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
expect(updateList._pending.length).toBe(0);
});
it('should remove destroyed items from active list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.remove(obj);
updateList.update();
expect(updateList.getActive()).not.toContain(obj);
});
it('should bail early when _toProcess is zero', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
// No changes - should return same active list without processing
var active = updateList.update();
expect(active).toContain(obj);
});
});
describe('getActive', function ()
{
it('should return an empty array initially', function ()
{
expect(updateList.getActive()).toEqual([]);
});
it('should return active items after update', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
expect(updateList.getActive()).toContain(obj);
});
it('should return a reference to the internal active array', function ()
{
var ref = updateList.getActive();
expect(ref).toBe(updateList._active);
});
});
describe('sceneUpdate', function ()
{
it('should call preUpdate on active game objects with active=true', function ()
{
var obj = createMockGameObject(true);
updateList.add(obj);
updateList.update();
updateList.sceneUpdate(100, 16);
expect(obj.preUpdate).toHaveBeenCalledWith(100, 16);
});
it('should not call preUpdate on inactive game objects', function ()
{
var obj = createMockGameObject(false);
updateList.add(obj);
updateList.update();
updateList.sceneUpdate(100, 16);
expect(obj.preUpdate).not.toHaveBeenCalled();
});
it('should pass time and delta to preUpdate', function ()
{
var obj = createMockGameObject(true);
updateList.add(obj);
updateList.update();
updateList.sceneUpdate(9999, 32.5);
expect(obj.preUpdate).toHaveBeenCalledWith(9999, 32.5);
});
it('should call preUpdate on multiple active objects', function ()
{
var obj1 = createMockGameObject(true);
var obj2 = createMockGameObject(true);
updateList.add(obj1);
updateList.add(obj2);
updateList.update();
updateList.sceneUpdate(0, 16);
expect(obj1.preUpdate).toHaveBeenCalledTimes(1);
expect(obj2.preUpdate).toHaveBeenCalledTimes(1);
});
it('should do nothing when active list is empty', function ()
{
// Should not throw
expect(function ()
{
updateList.sceneUpdate(0, 16);
}).not.toThrow();
});
it('should skip objects with active=false but process active=true ones', function ()
{
var activeObj = createMockGameObject(true);
var inactiveObj = createMockGameObject(false);
updateList.add(activeObj);
updateList.add(inactiveObj);
updateList.update();
updateList.sceneUpdate(0, 16);
expect(activeObj.preUpdate).toHaveBeenCalledTimes(1);
expect(inactiveObj.preUpdate).not.toHaveBeenCalled();
});
});
describe('shutdown', function ()
{
it('should call destroy on all active objects', function ()
{
var obj1 = createMockGameObject();
var obj2 = createMockGameObject();
updateList.add(obj1);
updateList.add(obj2);
updateList.update();
updateList.shutdown();
expect(obj1.destroy).toHaveBeenCalledWith(true);
expect(obj2.destroy).toHaveBeenCalledWith(true);
});
it('should call destroy on all pending objects', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
// Do NOT call update so obj stays pending
updateList.shutdown();
expect(obj.destroy).toHaveBeenCalledWith(true);
});
it('should reset _toProcess to zero', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.shutdown();
expect(updateList._toProcess).toBe(0);
});
it('should clear the active list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.shutdown();
expect(updateList._active).toEqual([]);
});
it('should clear the pending list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.shutdown();
expect(updateList._pending).toEqual([]);
});
it('should clear the destroy list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.remove(obj);
updateList.shutdown();
expect(updateList._destroy).toEqual([]);
});
it('should remove PRE_UPDATE and UPDATE scene event listeners', function ()
{
// Trigger start to register listeners
scene.sys.events.emit('start');
updateList.shutdown();
expect(scene.sys.events.listenerCount('preupdate')).toBe(0);
expect(scene.sys.events.listenerCount('update')).toBe(0);
});
});
describe('destroy', function ()
{
it('should null the scene reference', function ()
{
updateList.destroy();
expect(updateList.scene).toBeNull();
});
it('should null the systems reference', function ()
{
updateList.destroy();
expect(updateList.systems).toBeNull();
});
it('should clear the active list', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.destroy();
expect(updateList._active).toEqual([]);
});
it('should call destroy on active objects as part of shutdown', function ()
{
var obj = createMockGameObject();
updateList.add(obj);
updateList.update();
updateList.destroy();
expect(obj.destroy).toHaveBeenCalledWith(true);
});
});
});