phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
1,102 lines (813 loc) • 38.3 kB
JavaScript
var PluginManager = require('../../src/plugins/PluginManager');
var PluginCache = require('../../src/plugins/PluginCache');
var GameObjectFactory = require('../../src/gameobjects/GameObjectFactory');
var GameObjectCreator = require('../../src/gameobjects/GameObjectCreator');
var FileTypesManager = require('../../src/loader/FileTypesManager');
describe('PluginManager', function ()
{
var manager;
var mockGame;
function createMockGame ()
{
return {
isBooted: false,
config: {
renderType: 0,
installGlobalPlugins: [],
installScenePlugins: [],
defaultPlugins: [ 'corePlugin' ]
},
events: {
once: vi.fn(),
emit: vi.fn(),
on: vi.fn(),
off: vi.fn()
},
noReturn: false
};
}
function createMockPluginClass ()
{
var MockPlugin = function MockPlugin (pluginManager)
{
this.pluginManager = pluginManager;
};
MockPlugin.prototype.init = vi.fn(function (data) { this.data = data; });
MockPlugin.prototype.start = vi.fn();
MockPlugin.prototype.stop = vi.fn();
MockPlugin.prototype.destroy = vi.fn();
return MockPlugin;
}
beforeEach(function ()
{
mockGame = createMockGame();
manager = new PluginManager(mockGame);
});
afterEach(function ()
{
vi.restoreAllMocks();
});
describe('constructor', function ()
{
it('should set the game reference', function ()
{
expect(manager.game).toBe(mockGame);
});
it('should initialise plugins as an empty array', function ()
{
expect(manager.plugins).toEqual([]);
});
it('should initialise scenePlugins as an empty array', function ()
{
expect(manager.scenePlugins).toEqual([]);
});
it('should initialise _pendingGlobal as an empty array', function ()
{
expect(manager._pendingGlobal).toEqual([]);
});
it('should initialise _pendingScene as an empty array', function ()
{
expect(manager._pendingScene).toEqual([]);
});
it('should register for BOOT event when game is not booted', function ()
{
expect(mockGame.events.once).toHaveBeenCalled();
});
it('should call boot immediately when game is already booted', function ()
{
var bootedGame = createMockGame();
bootedGame.isBooted = true;
var bootSpy = vi.spyOn(PluginManager.prototype, 'boot').mockImplementation(function () {});
new PluginManager(bootedGame);
expect(bootSpy).toHaveBeenCalled();
});
});
describe('getIndex', function ()
{
it('should return -1 when plugins array is empty', function ()
{
expect(manager.getIndex('test')).toBe(-1);
});
it('should return 0 for the first plugin', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: true });
expect(manager.getIndex('testPlugin')).toBe(0);
});
it('should return -1 for a key that does not exist', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: true });
expect(manager.getIndex('nonExistent')).toBe(-1);
});
it('should return the correct index when multiple plugins exist', function ()
{
manager.plugins.push({ key: 'plugin1', plugin: {}, active: true });
manager.plugins.push({ key: 'plugin2', plugin: {}, active: true });
manager.plugins.push({ key: 'plugin3', plugin: {}, active: true });
expect(manager.getIndex('plugin2')).toBe(1);
expect(manager.getIndex('plugin3')).toBe(2);
});
it('should return -1 when searching an empty string key', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: true });
expect(manager.getIndex('')).toBe(-1);
});
});
describe('getEntry', function ()
{
it('should return undefined when plugins array is empty', function ()
{
expect(manager.getEntry('test')).toBeUndefined();
});
it('should return the plugin entry when it exists', function ()
{
var entry = { key: 'testPlugin', plugin: {}, active: true };
manager.plugins.push(entry);
expect(manager.getEntry('testPlugin')).toBe(entry);
});
it('should return undefined for a non-existent key', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: true });
expect(manager.getEntry('missing')).toBeUndefined();
});
it('should return the correct entry when multiple plugins exist', function ()
{
var entry1 = { key: 'plugin1', plugin: {}, active: true };
var entry2 = { key: 'plugin2', plugin: {}, active: false };
manager.plugins.push(entry1);
manager.plugins.push(entry2);
expect(manager.getEntry('plugin2')).toBe(entry2);
});
});
describe('isActive', function ()
{
it('should return falsy when plugin does not exist', function ()
{
expect(manager.isActive('nonExistent')).toBeFalsy();
});
it('should return true when plugin is active', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: true });
expect(manager.isActive('testPlugin')).toBe(true);
});
it('should return falsy when plugin is inactive', function ()
{
manager.plugins.push({ key: 'testPlugin', plugin: {}, active: false });
expect(manager.isActive('testPlugin')).toBeFalsy();
});
it('should return falsy when plugins array is empty', function ()
{
expect(manager.isActive('any')).toBeFalsy();
});
});
describe('stop', function ()
{
it('should return this for method chaining', function ()
{
var result = manager.stop('nonExistent');
expect(result).toBe(manager);
});
it('should set plugin active to false', function ()
{
var mockPlugin = { stop: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPlugin, active: true });
manager.stop('testPlugin');
expect(manager.plugins[0].active).toBe(false);
});
it('should call the plugin stop method', function ()
{
var mockPlugin = { stop: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPlugin, active: true });
manager.stop('testPlugin');
expect(mockPlugin.stop).toHaveBeenCalled();
});
it('should not call stop if plugin is already inactive', function ()
{
var mockPlugin = { stop: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPlugin, active: false });
manager.stop('testPlugin');
expect(mockPlugin.stop).not.toHaveBeenCalled();
});
it('should not throw when key does not exist', function ()
{
expect(function () { manager.stop('nonExistent'); }).not.toThrow();
});
it('should still return this when key does not exist', function ()
{
expect(manager.stop('nonExistent')).toBe(manager);
});
});
describe('getDefaultScenePlugins', function ()
{
it('should return an array containing config defaultPlugins', function ()
{
var result = manager.getDefaultScenePlugins();
expect(result).toContain('corePlugin');
});
it('should merge scenePlugins into the result', function ()
{
manager.scenePlugins.push('customPlugin');
var result = manager.getDefaultScenePlugins();
expect(result).toContain('corePlugin');
expect(result).toContain('customPlugin');
});
it('should return just config defaults when scenePlugins is empty', function ()
{
var result = manager.getDefaultScenePlugins();
expect(result).toEqual([ 'corePlugin' ]);
});
it('should not mutate the original config.defaultPlugins array', function ()
{
var original = mockGame.config.defaultPlugins.slice();
manager.scenePlugins.push('extra');
manager.getDefaultScenePlugins();
expect(mockGame.config.defaultPlugins).toEqual(original);
});
it('should return all scenePlugins when config defaultPlugins is empty', function ()
{
mockGame.config.defaultPlugins = [];
manager.scenePlugins.push('onlyCustom');
var result = manager.getDefaultScenePlugins();
expect(result).toEqual([ 'onlyCustom' ]);
});
});
describe('install', function ()
{
beforeEach(function ()
{
vi.spyOn(PluginCache, 'hasCustom').mockReturnValue(false);
});
it('should return null for a non-function plugin', function ()
{
var result = manager.install('key', 'not a function');
expect(result).toBeNull();
});
it('should return null for a null plugin', function ()
{
var result = manager.install('key', null);
expect(result).toBeNull();
});
it('should add to _pendingGlobal when game is not booted', function ()
{
var MockPlugin = createMockPluginClass();
manager.install('testKey', MockPlugin, false);
expect(manager._pendingGlobal.length).toBe(1);
expect(manager._pendingGlobal[0].key).toBe('testKey');
});
it('should store the plugin class in _pendingGlobal entry', function ()
{
var MockPlugin = createMockPluginClass();
manager.install('testKey', MockPlugin, false);
expect(manager._pendingGlobal[0].plugin).toBe(MockPlugin);
});
it('should store start flag in _pendingGlobal entry', function ()
{
var MockPlugin = createMockPluginClass();
manager.install('testKey', MockPlugin, true);
expect(manager._pendingGlobal[0].start).toBe(true);
});
it('should store mapping in _pendingGlobal entry', function ()
{
var MockPlugin = createMockPluginClass();
manager.install('testKey', MockPlugin, false, 'testMapping');
expect(manager._pendingGlobal[0].mapping).toBe('testMapping');
});
it('should force start to true when mapping is provided', function ()
{
var MockPlugin = createMockPluginClass();
manager.install('testKey', MockPlugin, false, 'testMapping');
expect(manager._pendingGlobal[0].start).toBe(true);
});
it('should store data in _pendingGlobal entry', function ()
{
var MockPlugin = createMockPluginClass();
var data = { msg: 'hello' };
manager.install('testKey', MockPlugin, false, null, data);
expect(manager._pendingGlobal[0].data).toBe(data);
});
it('should warn and return null when plugin key is already in use', function ()
{
var warnSpy = vi.spyOn(console, 'warn').mockImplementation(function () {});
vi.spyOn(PluginCache, 'hasCustom').mockReturnValue(true);
var result = manager.install('existingKey', createMockPluginClass());
expect(warnSpy).toHaveBeenCalled();
expect(result).toBeNull();
});
it('should return null when game is not booted even if start is true', function ()
{
var result = manager.install('testKey', createMockPluginClass(), true);
expect(result).toBeNull();
});
});
describe('installScenePlugin', function ()
{
it('should warn and return for a non-function plugin', function ()
{
var warnSpy = vi.spyOn(console, 'warn').mockImplementation(function () {});
manager.installScenePlugin('key', 'not a function');
expect(warnSpy).toHaveBeenCalled();
});
it('should add the key to scenePlugins array', function ()
{
vi.spyOn(PluginCache, 'hasCore').mockReturnValue(false);
vi.spyOn(PluginCache, 'register').mockImplementation(function () {});
manager.installScenePlugin('testScenePlugin', createMockPluginClass(), 'mapping');
expect(manager.scenePlugins).toContain('testScenePlugin');
});
it('should not add a duplicate key to scenePlugins', function ()
{
vi.spyOn(PluginCache, 'hasCore').mockReturnValue(false);
vi.spyOn(PluginCache, 'register').mockImplementation(function () {});
manager.installScenePlugin('testScenePlugin', createMockPluginClass(), 'mapping');
manager.installScenePlugin('testScenePlugin', createMockPluginClass(), 'mapping');
var count = manager.scenePlugins.filter(function (k) { return k === 'testScenePlugin'; }).length;
expect(count).toBe(1);
});
it('should call PluginCache.register when key is not already in core cache', function ()
{
vi.spyOn(PluginCache, 'hasCore').mockReturnValue(false);
var registerSpy = vi.spyOn(PluginCache, 'register').mockImplementation(function () {});
var MockPlugin = createMockPluginClass();
manager.installScenePlugin('newPlugin', MockPlugin, 'mapping');
expect(registerSpy).toHaveBeenCalledWith('newPlugin', MockPlugin, 'mapping', true);
});
it('should warn when key is already in use and not from loader', function ()
{
var warnSpy = vi.spyOn(console, 'warn').mockImplementation(function () {});
vi.spyOn(PluginCache, 'hasCore').mockReturnValueOnce(false).mockReturnValue(true);
vi.spyOn(PluginCache, 'register').mockImplementation(function () {});
manager.scenePlugins.push('existingPlugin');
manager.installScenePlugin('existingPlugin', createMockPluginClass(), 'mapping', undefined, false);
expect(warnSpy).toHaveBeenCalled();
});
});
describe('removeGlobalPlugin', function ()
{
it('should remove the plugin entry from plugins array', function ()
{
var mockPlugin = { destroy: vi.fn() };
var entry = { key: 'testPlugin', plugin: mockPlugin, active: true };
manager.plugins.push(entry);
vi.spyOn(PluginCache, 'removeCustom').mockImplementation(function () {});
manager.removeGlobalPlugin('testPlugin');
expect(manager.plugins.length).toBe(0);
});
it('should call PluginCache.removeCustom with the key', function ()
{
var removeCustomSpy = vi.spyOn(PluginCache, 'removeCustom').mockImplementation(function () {});
manager.removeGlobalPlugin('someKey');
expect(removeCustomSpy).toHaveBeenCalledWith('someKey');
});
it('should not throw when the key does not exist', function ()
{
vi.spyOn(PluginCache, 'removeCustom').mockImplementation(function () {});
expect(function () { manager.removeGlobalPlugin('nonExistent'); }).not.toThrow();
});
it('should not remove other plugins when removing one', function ()
{
var entry1 = { key: 'plugin1', plugin: { destroy: vi.fn() }, active: true };
var entry2 = { key: 'plugin2', plugin: { destroy: vi.fn() }, active: true };
manager.plugins.push(entry1);
manager.plugins.push(entry2);
vi.spyOn(PluginCache, 'removeCustom').mockImplementation(function () {});
manager.removeGlobalPlugin('plugin1');
expect(manager.plugins.length).toBe(1);
expect(manager.plugins[0].key).toBe('plugin2');
});
});
describe('removeScenePlugin', function ()
{
it('should remove the key from scenePlugins array', function ()
{
manager.scenePlugins.push('testScenePlugin');
vi.spyOn(PluginCache, 'remove').mockImplementation(function () {});
manager.removeScenePlugin('testScenePlugin');
expect(manager.scenePlugins).not.toContain('testScenePlugin');
});
it('should call PluginCache.remove with the key', function ()
{
var removeSpy = vi.spyOn(PluginCache, 'remove').mockImplementation(function () {});
manager.removeScenePlugin('testScenePlugin');
expect(removeSpy).toHaveBeenCalledWith('testScenePlugin');
});
it('should not remove other keys from scenePlugins', function ()
{
manager.scenePlugins.push('plugin1');
manager.scenePlugins.push('plugin2');
vi.spyOn(PluginCache, 'remove').mockImplementation(function () {});
manager.removeScenePlugin('plugin1');
expect(manager.scenePlugins).toContain('plugin2');
expect(manager.scenePlugins.length).toBe(1);
});
it('should not throw when key is not in scenePlugins', function ()
{
vi.spyOn(PluginCache, 'remove').mockImplementation(function () {});
expect(function () { manager.removeScenePlugin('nonExistent'); }).not.toThrow();
});
});
describe('registerGameObject', function ()
{
it('should return this for method chaining', function ()
{
var result = manager.registerGameObject('testObject', function () {});
expect(result).toBe(manager);
});
it('should call GameObjectFactory.register when factoryCallback is provided', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'register');
var factoryCb = function () {};
manager.registerGameObject('testObject', factoryCb);
expect(factorySpy).toHaveBeenCalledWith('testObject', factoryCb);
});
it('should call GameObjectCreator.register when creatorCallback is provided', function ()
{
var creatorSpy = vi.spyOn(GameObjectCreator, 'register');
var creatorCb = function () {};
manager.registerGameObject('testObject', null, creatorCb);
expect(creatorSpy).toHaveBeenCalledWith('testObject', creatorCb);
});
it('should register both factory and creator callbacks when both are provided', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'register');
var creatorSpy = vi.spyOn(GameObjectCreator, 'register');
var factoryCb = function () {};
var creatorCb = function () {};
manager.registerGameObject('testObject', factoryCb, creatorCb);
expect(factorySpy).toHaveBeenCalledWith('testObject', factoryCb);
expect(creatorSpy).toHaveBeenCalledWith('testObject', creatorCb);
});
it('should not call GameObjectFactory.register when factoryCallback is not provided', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'register');
manager.registerGameObject('testObject', null, function () {});
expect(factorySpy).not.toHaveBeenCalled();
});
it('should not call GameObjectCreator.register when creatorCallback is not provided', function ()
{
var creatorSpy = vi.spyOn(GameObjectCreator, 'register');
manager.registerGameObject('testObject', function () {}, null);
expect(creatorSpy).not.toHaveBeenCalled();
});
});
describe('removeGameObject', function ()
{
it('should return this for method chaining', function ()
{
vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
var result = manager.removeGameObject('testObject');
expect(result).toBe(manager);
});
it('should remove from both factory and creator by default', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
var creatorSpy = vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
manager.removeGameObject('testObject');
expect(factorySpy).toHaveBeenCalledWith('testObject');
expect(creatorSpy).toHaveBeenCalledWith('testObject');
});
it('should call GameObjectFactory.remove when removeFromFactory is true', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
manager.removeGameObject('testObject', true, false);
expect(factorySpy).toHaveBeenCalledWith('testObject');
});
it('should call GameObjectCreator.remove when removeFromCreator is true', function ()
{
vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
var creatorSpy = vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
manager.removeGameObject('testObject', false, true);
expect(creatorSpy).toHaveBeenCalledWith('testObject');
});
it('should not call GameObjectFactory.remove when removeFromFactory is false', function ()
{
var factorySpy = vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
manager.removeGameObject('testObject', false, true);
expect(factorySpy).not.toHaveBeenCalled();
});
it('should not call GameObjectCreator.remove when removeFromCreator is false', function ()
{
vi.spyOn(GameObjectFactory, 'remove').mockImplementation(function () {});
var creatorSpy = vi.spyOn(GameObjectCreator, 'remove').mockImplementation(function () {});
manager.removeGameObject('testObject', true, false);
expect(creatorSpy).not.toHaveBeenCalled();
});
});
describe('registerFileType', function ()
{
it('should call FileTypesManager.register with key and callback', function ()
{
var ftSpy = vi.spyOn(FileTypesManager, 'register').mockImplementation(function () {});
var callback = function () {};
manager.registerFileType('testType', callback);
expect(ftSpy).toHaveBeenCalledWith('testType', callback);
});
it('should inject the callback into scene loader when addToScene is provided', function ()
{
vi.spyOn(FileTypesManager, 'register').mockImplementation(function () {});
var callback = function () {};
var mockScene = { sys: { load: {} } };
manager.registerFileType('testType', callback, mockScene);
expect(mockScene.sys.load['testType']).toBe(callback);
});
it('should not throw when addToScene is not provided', function ()
{
vi.spyOn(FileTypesManager, 'register').mockImplementation(function () {});
expect(function () { manager.registerFileType('testType', function () {}); }).not.toThrow();
});
it('should not inject into scene when scene has no load plugin', function ()
{
vi.spyOn(FileTypesManager, 'register').mockImplementation(function () {});
var mockScene = { sys: {} };
expect(function () { manager.registerFileType('testType', function () {}, mockScene); }).not.toThrow();
});
});
describe('getClass', function ()
{
it('should return null for an unknown plugin key', function ()
{
vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(null);
expect(manager.getClass('unknown')).toBeNull();
});
it('should return the plugin class from PluginCache', function ()
{
var MockPlugin = createMockPluginClass();
vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(MockPlugin);
expect(manager.getClass('testPlugin')).toBe(MockPlugin);
});
it('should delegate to PluginCache.getCustomClass with the correct key', function ()
{
var spy = vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(null);
manager.getClass('someKey');
expect(spy).toHaveBeenCalledWith('someKey');
});
});
describe('get', function ()
{
it('should return null when plugin key does not exist anywhere', function ()
{
vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(null);
var result = manager.get('nonExistent');
expect(result).toBeNull();
});
it('should return the plugin instance when entry is already in plugins array', function ()
{
var mockPluginInstance = { stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: true });
var result = manager.get('testPlugin');
expect(result).toBe(mockPluginInstance);
});
it('should return the class without starting when autoStart is false', function ()
{
var MockPlugin = createMockPluginClass();
vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(MockPlugin);
var result = manager.get('testPlugin', false);
expect(result).toBe(MockPlugin);
});
it('should return null when autoStart is true but entry cannot be created', function ()
{
vi.spyOn(PluginCache, 'getCustomClass').mockReturnValue(createMockPluginClass());
vi.spyOn(PluginCache, 'getCustom').mockReturnValue(null);
var result = manager.get('testPlugin', true);
expect(result).toBeNull();
});
});
describe('start', function ()
{
it('should reactivate an inactive plugin entry', function ()
{
var mockPluginInstance = { init: vi.fn(), start: vi.fn(), stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: false });
manager.start('testPlugin');
expect(manager.plugins[0].active).toBe(true);
});
it('should call start on the plugin instance when reactivating', function ()
{
var mockPluginInstance = { init: vi.fn(), start: vi.fn(), stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: false });
manager.start('testPlugin');
expect(mockPluginInstance.start).toHaveBeenCalled();
});
it('should return the plugin instance when reactivating', function ()
{
var mockPluginInstance = { init: vi.fn(), start: vi.fn(), stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: false });
var result = manager.start('testPlugin');
expect(result).toBe(mockPluginInstance);
});
it('should return the existing plugin instance when already active', function ()
{
var mockPluginInstance = { init: vi.fn(), start: vi.fn(), stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: true });
var result = manager.start('testPlugin');
expect(result).toBe(mockPluginInstance);
});
it('should not call start again on an already active plugin', function ()
{
var mockPluginInstance = { init: vi.fn(), start: vi.fn(), stop: vi.fn(), destroy: vi.fn() };
manager.plugins.push({ key: 'testPlugin', plugin: mockPluginInstance, active: true });
manager.start('testPlugin');
expect(mockPluginInstance.start).not.toHaveBeenCalled();
});
it('should return null when plugin cannot be found in cache', function ()
{
vi.spyOn(PluginCache, 'getCustom').mockReturnValue(null);
var result = manager.start('nonExistent');
expect(result).toBeNull();
});
it('should use runAs key when provided', function ()
{
var MockPlugin = createMockPluginClass();
var mockInstance = new MockPlugin(manager);
vi.spyOn(PluginCache, 'getCustom').mockReturnValue({
plugin: MockPlugin,
mapping: null,
data: null
});
manager.start('sourceKey', 'aliasKey');
var entry = manager.getEntry('aliasKey');
expect(entry).toBeDefined();
expect(entry.key).toBe('aliasKey');
});
});
describe('addToScene', function ()
{
it('should inject global plugin references from game into scene systems', function ()
{
var mockPlugin = { someMethod: vi.fn() };
mockGame.testPlugin = mockPlugin;
var mockSys = {
scene: {},
settings: {
map: {},
isBooted: false
}
};
manager.addToScene(mockSys, [ 'testPlugin' ], []);
expect(mockSys.testPlugin).toBe(mockPlugin);
});
it('should map global plugin into scene when map contains the key', function ()
{
var mockPlugin = { someMethod: vi.fn() };
mockGame.testPlugin = mockPlugin;
var mockSys = {
scene: {},
settings: {
map: { testPlugin: 'mappedKey' },
isBooted: false
}
};
manager.addToScene(mockSys, [ 'testPlugin' ], []);
expect(mockSys.scene.mappedKey).toBe(mockPlugin);
});
it('should inject game reference into scene when plugin key is game and map has game', function ()
{
var mockSys = {
scene: {},
settings: {
map: { game: 'gameRef' },
isBooted: false
}
};
manager.addToScene(mockSys, [ 'game' ], []);
expect(mockSys.scene.gameRef).toBe(mockGame);
});
it('should inject plugin entries with mapping directly into scene', function ()
{
var mockPluginInstance = { destroy: vi.fn() };
manager.plugins.push({ key: 'myPlugin', plugin: mockPluginInstance, active: true, mapping: 'myPluginRef' });
var mockSys = {
scene: {},
settings: { map: {}, isBooted: false }
};
manager.addToScene(mockSys, [], [ [] ]);
expect(mockSys.scene.myPluginRef).toBe(mockPluginInstance);
});
it('should not inject plugin entries that have no mapping', function ()
{
var mockPluginInstance = { destroy: vi.fn() };
manager.plugins.push({ key: 'myPlugin', plugin: mockPluginInstance, active: true, mapping: null });
var mockSys = {
scene: {},
settings: { map: {}, isBooted: false }
};
manager.addToScene(mockSys, [], [ [] ]);
expect(mockSys.scene['myPlugin']).toBeUndefined();
});
});
describe('boot', function ()
{
it('should clear _pendingGlobal after booting', function ()
{
var MockPlugin = createMockPluginClass();
manager._pendingGlobal = [ { key: 'testPlugin', plugin: MockPlugin, start: false, mapping: null, data: null } ];
vi.spyOn(manager, 'install').mockImplementation(function () {});
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager.boot();
expect(manager._pendingGlobal).toEqual([]);
});
it('should clear _pendingScene after booting', function ()
{
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager.boot();
expect(manager._pendingScene).toEqual([]);
});
it('should call install for each pending global plugin with a key and plugin', function ()
{
var MockPlugin = createMockPluginClass();
var installSpy = vi.spyOn(manager, 'install').mockImplementation(function () {});
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager._pendingGlobal = [ { key: 'testPlugin', plugin: MockPlugin, start: false, mapping: null, data: null } ];
manager.boot();
expect(installSpy).toHaveBeenCalledWith('testPlugin', MockPlugin, false, null, null);
});
it('should warn when pending global plugin is missing plugin class', function ()
{
var warnSpy = vi.spyOn(console, 'warn').mockImplementation(function () {});
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager._pendingGlobal = [ { key: 'brokenPlugin', plugin: null } ];
manager.boot();
expect(warnSpy).toHaveBeenCalled();
});
it('should call installScenePlugin for each pending scene plugin with key and plugin', function ()
{
var MockPlugin = createMockPluginClass();
var installSceneSpy = vi.spyOn(manager, 'installScenePlugin').mockImplementation(function () {});
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager._pendingScene = [ { key: 'scenePlugin', plugin: MockPlugin, mapping: 'sp' } ];
manager.boot();
expect(installSceneSpy).toHaveBeenCalledWith('scenePlugin', MockPlugin, 'sp');
});
it('should register for DESTROY event after booting', function ()
{
mockGame.config.installGlobalPlugins = [];
mockGame.config.installScenePlugins = [];
manager.boot();
var calls = mockGame.events.once.mock.calls;
var destroyCalled = calls.some(function (call) { return call[0] === 'destroy'; });
expect(destroyCalled).toBe(true);
});
});
describe('destroy', function ()
{
it('should call destroy on all plugin instances', function ()
{
var mockPlugin1 = { destroy: vi.fn() };
var mockPlugin2 = { destroy: vi.fn() };
manager.plugins.push({ key: 'plugin1', plugin: mockPlugin1, active: true });
manager.plugins.push({ key: 'plugin2', plugin: mockPlugin2, active: true });
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
manager.destroy();
expect(mockPlugin1.destroy).toHaveBeenCalled();
expect(mockPlugin2.destroy).toHaveBeenCalled();
});
it('should set game to null after destroy', function ()
{
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
manager.destroy();
expect(manager.game).toBeNull();
});
it('should clear the plugins array after destroy', function ()
{
manager.plugins.push({ key: 'plugin1', plugin: { destroy: vi.fn() }, active: true });
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
manager.destroy();
expect(manager.plugins).toEqual([]);
});
it('should clear the scenePlugins array after destroy', function ()
{
manager.scenePlugins.push('somePlugin');
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
manager.destroy();
expect(manager.scenePlugins).toEqual([]);
});
it('should call PluginCache.destroyCustomPlugins', function ()
{
var destroySpy = vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
manager.destroy();
expect(destroySpy).toHaveBeenCalled();
});
it('should call PluginCache.destroyCorePlugins when game.noReturn is true', function ()
{
mockGame.noReturn = true;
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
var coreDestroySpy = vi.spyOn(PluginCache, 'destroyCorePlugins').mockImplementation(function () {});
manager.destroy();
expect(coreDestroySpy).toHaveBeenCalled();
});
it('should not call PluginCache.destroyCorePlugins when game.noReturn is false', function ()
{
mockGame.noReturn = false;
vi.spyOn(PluginCache, 'destroyCustomPlugins').mockImplementation(function () {});
var coreDestroySpy = vi.spyOn(PluginCache, 'destroyCorePlugins').mockImplementation(function () {});
manager.destroy();
expect(coreDestroySpy).not.toHaveBeenCalled();
});
});
});