phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
1,011 lines (834 loc) • 29.5 kB
JavaScript
var BaseSoundManager = require('../../src/sound/BaseSoundManager');
function createMockGame ()
{
var gameEvents = {
on: function () {},
once: function () {},
off: function () {},
emit: function () {}
};
return {
cache: {
json: {
get: function (key)
{
return {
spritemap: {
markerA: { start: 0, end: 2 },
markerB: { start: 3, end: 5, loop: true }
}
};
}
}
},
events: gameEvents
};
}
function createMockSound (key, isPlaying)
{
return {
key: key,
isPlaying: isPlaying || false,
pendingRemove: false,
spritemap: null,
markers: [],
calculateRateCalled: false,
pauseCalled: false,
resumeCalled: false,
stopCalled: false,
destroyCalled: false,
updateCalled: false,
playArg: null,
addMarkerCalled: false,
onceCalled: false,
destroy: function ()
{
this.destroyCalled = true;
this.pendingRemove = true;
},
pause: function ()
{
this.pauseCalled = true;
},
resume: function ()
{
this.resumeCalled = true;
},
stop: function ()
{
this.stopCalled = true;
return true;
},
play: function (arg)
{
this.playArg = arg;
return true;
},
addMarker: function (marker)
{
this.addMarkerCalled = true;
this.markers.push(marker);
},
update: function (time, delta)
{
this.updateCalled = true;
},
calculateRate: function ()
{
this.calculateRateCalled = true;
},
once: function (event, fn, ctx)
{
this.onceCalled = true;
}
};
}
describe('BaseSoundManager', function ()
{
var game;
var manager;
beforeEach(function ()
{
game = createMockGame();
manager = new BaseSoundManager(game);
});
afterEach(function ()
{
// Only destroy if not already destroyed
if (manager.game !== null)
{
manager.game.events.off = function () {};
manager.destroy();
}
});
describe('Constructor', function ()
{
it('should set game reference', function ()
{
expect(manager.game).toBe(game);
});
it('should set jsonCache reference', function ()
{
expect(manager.jsonCache).toBe(game.cache.json);
});
it('should initialise sounds as empty array', function ()
{
expect(Array.isArray(manager.sounds)).toBe(true);
expect(manager.sounds.length).toBe(0);
});
it('should default mute to false', function ()
{
expect(manager.mute).toBe(false);
});
it('should default volume to 1', function ()
{
expect(manager.volume).toBe(1);
});
it('should default pauseOnBlur to true', function ()
{
expect(manager.pauseOnBlur).toBe(true);
});
it('should default _rate to 1', function ()
{
expect(manager._rate).toBe(1);
});
it('should default _detune to 0', function ()
{
expect(manager._detune).toBe(0);
});
it('should default locked to false', function ()
{
expect(manager.locked).toBe(false);
});
it('should default unlocked to false', function ()
{
expect(manager.unlocked).toBe(false);
});
it('should default gameLostFocus to false', function ()
{
expect(manager.gameLostFocus).toBe(false);
});
it('should initialise listenerPosition as a Vector2 at origin', function ()
{
expect(manager.listenerPosition).not.toBeNull();
expect(manager.listenerPosition.x).toBe(0);
expect(manager.listenerPosition.y).toBe(0);
});
it('should register event listeners on game events', function ()
{
var registered = [];
game = createMockGame();
game.events.on = function (event)
{
registered.push(event);
};
game.events.once = function (event)
{
registered.push(event);
};
var m = new BaseSoundManager(game);
expect(registered).toContain('blur');
expect(registered).toContain('focus');
expect(registered).toContain('prestep');
expect(registered).toContain('destroy');
m.game = null;
});
});
describe('get', function ()
{
it('should return null when sounds array is empty', function ()
{
expect(manager.get('music')).toBeNull();
});
it('should return the first sound matching the key', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('music');
manager.sounds.push(s1, s2);
expect(manager.get('music')).toBe(s1);
});
it('should return null when no sound matches the key', function ()
{
manager.sounds.push(createMockSound('sfx'));
expect(manager.get('music')).toBeNull();
});
});
describe('getAll', function ()
{
it('should return empty array when sounds array is empty', function ()
{
expect(manager.getAll()).toEqual([]);
});
it('should return all sounds when no key is given', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
var result = manager.getAll();
expect(result.length).toBe(2);
});
it('should return only sounds matching the given key', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
var s3 = createMockSound('music');
manager.sounds.push(s1, s2, s3);
var result = manager.getAll('music');
expect(result.length).toBe(2);
expect(result[0]).toBe(s1);
expect(result[1]).toBe(s3);
});
it('should return empty array when no sounds match the key', function ()
{
manager.sounds.push(createMockSound('sfx'));
expect(manager.getAll('music')).toEqual([]);
});
});
describe('getAllPlaying', function ()
{
it('should return empty array when no sounds are playing', function ()
{
manager.sounds.push(createMockSound('music', false));
manager.sounds.push(createMockSound('sfx', false));
expect(manager.getAllPlaying()).toEqual([]);
});
it('should return only playing sounds', function ()
{
var s1 = createMockSound('music', true);
var s2 = createMockSound('sfx', false);
var s3 = createMockSound('ambient', true);
manager.sounds.push(s1, s2, s3);
var result = manager.getAllPlaying();
expect(result.length).toBe(2);
expect(result).toContain(s1);
expect(result).toContain(s3);
});
it('should return empty array when sounds array is empty', function ()
{
expect(manager.getAllPlaying()).toEqual([]);
});
});
describe('remove', function ()
{
it('should return false when sound is not in the manager', function ()
{
var sound = createMockSound('music');
expect(manager.remove(sound)).toBe(false);
});
it('should return true and remove sound from the array', function ()
{
var sound = createMockSound('music');
manager.sounds.push(sound);
var result = manager.remove(sound);
expect(result).toBe(true);
expect(manager.sounds.length).toBe(0);
});
it('should call destroy on the removed sound', function ()
{
var sound = createMockSound('music');
manager.sounds.push(sound);
manager.remove(sound);
expect(sound.destroyCalled).toBe(true);
});
it('should only remove the specified sound, leaving others intact', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.remove(s1);
expect(manager.sounds.length).toBe(1);
expect(manager.sounds[0]).toBe(s2);
});
});
describe('removeAll', function ()
{
it('should destroy all sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.removeAll();
expect(s1.destroyCalled).toBe(true);
expect(s2.destroyCalled).toBe(true);
});
it('should empty the sounds array', function ()
{
manager.sounds.push(createMockSound('music'), createMockSound('sfx'));
manager.removeAll();
expect(manager.sounds.length).toBe(0);
});
it('should work when sounds array is already empty', function ()
{
expect(function () { manager.removeAll(); }).not.toThrow();
});
});
describe('removeByKey', function ()
{
it('should return 0 when no sounds match the key', function ()
{
manager.sounds.push(createMockSound('sfx'));
expect(manager.removeByKey('music')).toBe(0);
});
it('should return the number of removed sounds', function ()
{
manager.sounds.push(createMockSound('music'));
manager.sounds.push(createMockSound('music'));
manager.sounds.push(createMockSound('sfx'));
expect(manager.removeByKey('music')).toBe(2);
});
it('should destroy removed sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.removeByKey('music');
expect(s1.destroyCalled).toBe(true);
expect(s2.destroyCalled).toBe(false);
});
it('should only remove sounds matching the key', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
var s3 = createMockSound('music');
manager.sounds.push(s1, s2, s3);
manager.removeByKey('music');
expect(manager.sounds.length).toBe(1);
expect(manager.sounds[0]).toBe(s2);
});
it('should return 0 when sounds array is empty', function ()
{
expect(manager.removeByKey('music')).toBe(0);
});
});
describe('pauseAll', function ()
{
it('should call pause on all active sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.pauseAll();
expect(s1.pauseCalled).toBe(true);
expect(s2.pauseCalled).toBe(true);
});
it('should not pause sounds marked for removal', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
s2.pendingRemove = true;
manager.sounds.push(s1, s2);
manager.pauseAll();
expect(s1.pauseCalled).toBe(true);
expect(s2.pauseCalled).toBe(false);
});
it('should emit PAUSE_ALL event', function ()
{
var emitted = false;
manager.on('pauseall', function () { emitted = true; });
manager.pauseAll();
expect(emitted).toBe(true);
});
});
describe('resumeAll', function ()
{
it('should call resume on all active sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.resumeAll();
expect(s1.resumeCalled).toBe(true);
expect(s2.resumeCalled).toBe(true);
});
it('should not resume sounds marked for removal', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
s2.pendingRemove = true;
manager.sounds.push(s1, s2);
manager.resumeAll();
expect(s1.resumeCalled).toBe(true);
expect(s2.resumeCalled).toBe(false);
});
it('should emit RESUME_ALL event', function ()
{
var emitted = false;
manager.on('resumeall', function () { emitted = true; });
manager.resumeAll();
expect(emitted).toBe(true);
});
});
describe('stopAll', function ()
{
it('should call stop on all active sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.stopAll();
expect(s1.stopCalled).toBe(true);
expect(s2.stopCalled).toBe(true);
});
it('should not stop sounds marked for removal', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
s2.pendingRemove = true;
manager.sounds.push(s1, s2);
manager.stopAll();
expect(s1.stopCalled).toBe(true);
expect(s2.stopCalled).toBe(false);
});
it('should emit STOP_ALL event', function ()
{
var emitted = false;
manager.on('stopall', function () { emitted = true; });
manager.stopAll();
expect(emitted).toBe(true);
});
});
describe('stopByKey', function ()
{
it('should return 0 when no sounds match the key', function ()
{
manager.sounds.push(createMockSound('sfx'));
expect(manager.stopByKey('music')).toBe(0);
});
it('should stop sounds matching the key and return count', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('music');
var s3 = createMockSound('sfx');
manager.sounds.push(s1, s2, s3);
expect(manager.stopByKey('music')).toBe(2);
expect(s1.stopCalled).toBe(true);
expect(s2.stopCalled).toBe(true);
expect(s3.stopCalled).toBe(false);
});
it('should return 0 when sounds array is empty', function ()
{
expect(manager.stopByKey('music')).toBe(0);
});
});
describe('isPlaying', function ()
{
it('should return false when no sounds are playing and no key given', function ()
{
manager.sounds.push(createMockSound('music', false));
expect(manager.isPlaying(undefined)).toBe(false);
});
it('should return true when any sound is playing and no key given', function ()
{
manager.sounds.push(createMockSound('music', false));
manager.sounds.push(createMockSound('sfx', true));
expect(manager.isPlaying(undefined)).toBe(true);
});
it('should return false when sounds array is empty and no key given', function ()
{
expect(manager.isPlaying(undefined)).toBe(false);
});
it('should return false when no sound with given key is playing', function ()
{
manager.sounds.push(createMockSound('music', false));
expect(manager.isPlaying('music')).toBe(false);
});
it('should return true when a sound with given key is playing', function ()
{
manager.sounds.push(createMockSound('sfx', false));
manager.sounds.push(createMockSound('music', true));
expect(manager.isPlaying('music')).toBe(true);
});
it('should return false when key does not match any sound', function ()
{
manager.sounds.push(createMockSound('sfx', true));
expect(manager.isPlaying('music')).toBe(false);
});
it('should return false when sounds array is empty and key given', function ()
{
expect(manager.isPlaying('music')).toBe(false);
});
});
describe('update', function ()
{
it('should call update on all sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.update(100, 16);
expect(s1.updateCalled).toBe(true);
expect(s2.updateCalled).toBe(true);
});
it('should remove sounds with pendingRemove set', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
s1.pendingRemove = true;
manager.sounds.push(s1, s2);
manager.update(100, 16);
expect(manager.sounds.length).toBe(1);
expect(manager.sounds[0]).toBe(s2);
});
it('should emit UNLOCKED event and clear flags when unlocked is true', function ()
{
var emitted = false;
manager.on('unlocked', function () { emitted = true; });
manager.unlocked = true;
manager.locked = true;
manager.update(100, 16);
expect(emitted).toBe(true);
expect(manager.unlocked).toBe(false);
expect(manager.locked).toBe(false);
});
it('should not emit UNLOCKED event when unlocked is false', function ()
{
var emitted = false;
manager.on('unlocked', function () { emitted = true; });
manager.unlocked = false;
manager.update(100, 16);
expect(emitted).toBe(false);
});
});
describe('destroy', function ()
{
it('should destroy all sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
game.events.off = function () {};
manager.destroy();
expect(s1.destroyCalled).toBe(true);
expect(s2.destroyCalled).toBe(true);
});
it('should set sounds to null', function ()
{
game.events.off = function () {};
manager.destroy();
expect(manager.sounds).toBeNull();
});
it('should set listenerPosition to null', function ()
{
game.events.off = function () {};
manager.destroy();
expect(manager.listenerPosition).toBeNull();
});
it('should set game to null', function ()
{
game.events.off = function () {};
manager.destroy();
expect(manager.game).toBeNull();
});
it('should deregister game event listeners', function ()
{
var removedEvents = [];
game.events.off = function (event)
{
removedEvents.push(event);
};
manager.destroy();
expect(removedEvents).toContain('blur');
expect(removedEvents).toContain('focus');
expect(removedEvents).toContain('prestep');
});
});
describe('setRate', function ()
{
it('should set the rate property', function ()
{
manager.setRate(2);
expect(manager.rate).toBe(2);
});
it('should return the manager for chaining', function ()
{
var result = manager.setRate(1.5);
expect(result).toBe(manager);
});
it('should call calculateRate on all active sounds', function ()
{
var s1 = createMockSound('music');
var s2 = createMockSound('sfx');
manager.sounds.push(s1, s2);
manager.setRate(0.5);
expect(s1.calculateRateCalled).toBe(true);
expect(s2.calculateRateCalled).toBe(true);
});
it('should emit GLOBAL_RATE event with the manager and value', function ()
{
var emittedManager = null;
var emittedValue = null;
manager.on('rate', function (mgr, val)
{
emittedManager = mgr;
emittedValue = val;
});
manager.setRate(2);
expect(emittedManager).toBe(manager);
expect(emittedValue).toBe(2);
});
});
describe('rate getter/setter', function ()
{
it('should default to 1', function ()
{
expect(manager.rate).toBe(1);
});
it('should update _rate when set', function ()
{
manager.rate = 0.5;
expect(manager._rate).toBeCloseTo(0.5);
});
it('should return _rate via getter', function ()
{
manager._rate = 1.5;
expect(manager.rate).toBeCloseTo(1.5);
});
it('should not call calculateRate on sounds with pendingRemove', function ()
{
var s1 = createMockSound('music');
s1.pendingRemove = true;
manager.sounds.push(s1);
manager.rate = 2;
expect(s1.calculateRateCalled).toBe(false);
});
});
describe('setDetune', function ()
{
it('should set the detune property', function ()
{
manager.setDetune(100);
expect(manager.detune).toBe(100);
});
it('should return the manager for chaining', function ()
{
var result = manager.setDetune(50);
expect(result).toBe(manager);
});
it('should call calculateRate on all active sounds', function ()
{
var s1 = createMockSound('music');
manager.sounds.push(s1);
manager.setDetune(200);
expect(s1.calculateRateCalled).toBe(true);
});
it('should emit GLOBAL_DETUNE event with manager and value', function ()
{
var emittedManager = null;
var emittedValue = null;
manager.on('detune', function (mgr, val)
{
emittedManager = mgr;
emittedValue = val;
});
manager.setDetune(-400);
expect(emittedManager).toBe(manager);
expect(emittedValue).toBe(-400);
});
});
describe('detune getter/setter', function ()
{
it('should default to 0', function ()
{
expect(manager.detune).toBe(0);
});
it('should update _detune when set', function ()
{
manager.detune = 300;
expect(manager._detune).toBe(300);
});
it('should return _detune via getter', function ()
{
manager._detune = -100;
expect(manager.detune).toBe(-100);
});
it('should accept negative values', function ()
{
manager.setDetune(-1200);
expect(manager.detune).toBe(-1200);
});
it('should accept positive values', function ()
{
manager.setDetune(1200);
expect(manager.detune).toBe(1200);
});
});
describe('onBlur / onFocus (NOOP stubs)', function ()
{
it('should expose onBlur as a callable method', function ()
{
expect(typeof manager.onBlur).toBe('function');
expect(function () { manager.onBlur(); }).not.toThrow();
});
it('should expose onFocus as a callable method', function ()
{
expect(typeof manager.onFocus).toBe('function');
expect(function () { manager.onFocus(); }).not.toThrow();
});
});
describe('unlock (NOOP stub)', function ()
{
it('should expose unlock as a callable method', function ()
{
expect(typeof manager.unlock).toBe('function');
expect(function () { manager.unlock(); }).not.toThrow();
});
});
describe('setListenerPosition (NOOP stub)', function ()
{
it('should expose setListenerPosition as a callable method', function ()
{
expect(typeof manager.setListenerPosition).toBe('function');
expect(function () { manager.setListenerPosition(100, 200); }).not.toThrow();
});
});
describe('play', function ()
{
it('should call add and play the returned sound', function ()
{
var mockSound = createMockSound('music');
manager.add = function (key)
{
return mockSound;
};
var result = manager.play('music');
expect(result).toBe(true);
expect(mockSound.onceCalled).toBe(true);
});
it('should play with extra config when no name is provided', function ()
{
var mockSound = createMockSound('music');
var extraConfig = { volume: 0.5 };
manager.add = function (key)
{
return mockSound;
};
manager.play('music', extraConfig);
expect(mockSound.playArg).toBe(extraConfig);
});
it('should addMarker and play by name when extra has a name property', function ()
{
var mockSound = createMockSound('music');
var extra = { name: 'intro', start: 0, duration: 2 };
manager.add = function (key)
{
return mockSound;
};
manager.play('music', extra);
expect(mockSound.addMarkerCalled).toBe(true);
expect(mockSound.playArg).toBe('intro');
});
});
describe('playAudioSprite', function ()
{
it('should call addAudioSprite, register complete listener, and play the sprite name', function ()
{
var mockSound = createMockSound('sfx');
manager.addAudioSprite = function (key)
{
return mockSound;
};
var result = manager.playAudioSprite('sfx', 'explosion');
expect(result).toBe(true);
expect(mockSound.onceCalled).toBe(true);
expect(mockSound.playArg).toBe('explosion');
});
});
describe('addAudioSprite', function ()
{
it('should call add and attach spritemap from jsonCache', function ()
{
var mockSound = createMockSound('sfx');
mockSound.spritemap = null;
manager.add = function (key, config)
{
return mockSound;
};
manager.addAudioSprite('sfx');
expect(mockSound.spritemap).not.toBeNull();
expect(typeof mockSound.spritemap).toBe('object');
});
it('should call addMarker for each spritemap entry', function ()
{
var mockSound = createMockSound('sfx');
manager.add = function (key, config)
{
return mockSound;
};
manager.addAudioSprite('sfx');
// jsonCache returns markerA and markerB
expect(mockSound.markers.length).toBe(2);
});
it('should set loop from spritemap marker when loop property exists', function ()
{
var mockSound = createMockSound('sfx');
manager.add = function (key, config)
{
return mockSound;
};
manager.addAudioSprite('sfx');
var markerB = mockSound.markers.find(function (m) { return m.name === 'markerB'; });
expect(markerB.config.loop).toBe(true);
});
it('should default loop to false when marker has no loop property', function ()
{
var mockSound = createMockSound('sfx');
manager.add = function (key, config)
{
return mockSound;
};
manager.addAudioSprite('sfx');
var markerA = mockSound.markers.find(function (m) { return m.name === 'markerA'; });
expect(markerA.config.loop).toBe(false);
});
it('should return the sound instance', function ()
{
var mockSound = createMockSound('sfx');
manager.add = function (key, config)
{
return mockSound;
};
var result = manager.addAudioSprite('sfx');
expect(result).toBe(mockSound);
});
});
});