phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
594 lines (514 loc) • 19.6 kB
JavaScript
var AnimationManager = require('../../src/animations/AnimationManager');
function createMockGame ()
{
return {
events: {
once: function () {},
on: function () {},
off: function () {},
emit: function () {}
},
textures: {
exists: function () { return false; },
get: function () { return null; }
},
cache: {
json: { get: function () { return null; } }
}
};
}
function createMockAnim (key)
{
return {
key: key,
frames: [],
toJSON: function ()
{
return { key: key, type: 'frame', frames: [], frameRate: 24, duration: 0, repeat: 0 };
}
};
}
describe('AnimationManager', function ()
{
var manager;
beforeEach(function ()
{
manager = new AnimationManager(createMockGame());
});
afterEach(function ()
{
manager.anims.clear();
manager.mixes.clear();
});
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
describe('constructor', function ()
{
it('should set globalTimeScale to 1 by default', function ()
{
expect(manager.globalTimeScale).toBe(1);
});
it('should set paused to false by default', function ()
{
expect(manager.paused).toBe(false);
});
it('should have the name AnimationManager', function ()
{
expect(manager.name).toBe('AnimationManager');
});
it('should initialise with an empty anims map', function ()
{
expect(manager.anims.size).toBe(0);
});
it('should initialise with an empty mixes map', function ()
{
expect(manager.mixes.size).toBe(0);
});
it('should store a reference to the game', function ()
{
var game = createMockGame();
var mgr = new AnimationManager(game);
expect(mgr.game).toBe(game);
});
});
// -------------------------------------------------------------------------
// boot
// -------------------------------------------------------------------------
describe('boot', function ()
{
it('should assign textureManager from game.textures', function ()
{
var game = createMockGame();
var mgr = new AnimationManager(game);
mgr.boot();
expect(mgr.textureManager).toBe(game.textures);
});
});
// -------------------------------------------------------------------------
// exists
// -------------------------------------------------------------------------
describe('exists', function ()
{
it('should return false when key does not exist', function ()
{
expect(manager.exists('missing')).toBe(false);
});
it('should return true when key exists', function ()
{
manager.anims.set('walk', createMockAnim('walk'));
expect(manager.exists('walk')).toBe(true);
});
});
// -------------------------------------------------------------------------
// add
// -------------------------------------------------------------------------
describe('add', function ()
{
it('should add an animation under the given key', function ()
{
var anim = createMockAnim('run');
manager.add('run', anim);
expect(manager.anims.has('run')).toBe(true);
});
it('should set the key on the animation object', function ()
{
var anim = { key: '' };
manager.add('jump', anim);
expect(anim.key).toBe('jump');
});
it('should emit ADD_ANIMATION', function ()
{
var fired = false;
manager.on('add', function () { fired = true; });
manager.add('idle', createMockAnim('idle'));
expect(fired).toBe(true);
});
it('should return this for chaining', function ()
{
var result = manager.add('walk', createMockAnim('walk'));
expect(result).toBe(manager);
});
it('should not overwrite an existing key', function ()
{
var first = createMockAnim('run');
var second = createMockAnim('run');
manager.add('run', first);
manager.add('run', second);
expect(manager.anims.get('run')).toBe(first);
});
});
// -------------------------------------------------------------------------
// get
// -------------------------------------------------------------------------
describe('get', function ()
{
it('should return undefined for a key that does not exist', function ()
{
expect(manager.get('ghost')).toBeUndefined();
});
it('should return the animation for an existing key', function ()
{
var anim = createMockAnim('fly');
manager.anims.set('fly', anim);
expect(manager.get('fly')).toBe(anim);
});
});
// -------------------------------------------------------------------------
// addMix / getMix / removeMix
// -------------------------------------------------------------------------
describe('addMix', function ()
{
it('should return this for chaining', function ()
{
var result = manager.addMix('a', 'b', 500);
expect(result).toBe(manager);
});
it('should not add a mix when animations do not exist in the manager', function ()
{
manager.addMix('a', 'b', 500);
expect(manager.getMix('a', 'b')).toBe(0);
});
it('should add a mix when both animations exist', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
manager.addMix('idle', 'run', 200);
expect(manager.getMix('idle', 'run')).toBe(200);
});
it('should accept Animation instances as well as string keys', function ()
{
var animA = createMockAnim('idle');
var animB = createMockAnim('run');
manager.anims.set('idle', animA);
manager.anims.set('run', animB);
manager.addMix(animA, animB, 300);
expect(manager.getMix('idle', 'run')).toBe(300);
});
it('should update an existing mix when called again', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
manager.addMix('idle', 'run', 100);
manager.addMix('idle', 'run', 999);
expect(manager.getMix('idle', 'run')).toBe(999);
});
});
describe('getMix', function ()
{
it('should return 0 when no mix exists', function ()
{
expect(manager.getMix('x', 'y')).toBe(0);
});
it('should return the correct delay for an existing mix', function ()
{
manager.anims.set('a', createMockAnim('a'));
manager.anims.set('b', createMockAnim('b'));
manager.addMix('a', 'b', 750);
expect(manager.getMix('a', 'b')).toBe(750);
});
it('should return 0 for the reverse direction if only one way mix exists', function ()
{
manager.anims.set('a', createMockAnim('a'));
manager.anims.set('b', createMockAnim('b'));
manager.addMix('a', 'b', 750);
expect(manager.getMix('b', 'a')).toBe(0);
});
});
describe('removeMix', function ()
{
it('should return this for chaining', function ()
{
var result = manager.removeMix('a', 'b');
expect(result).toBe(manager);
});
it('should remove a specific pairing when animB is provided', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
manager.addMix('idle', 'run', 200);
manager.removeMix('idle', 'run');
expect(manager.getMix('idle', 'run')).toBe(0);
});
it('should remove all mixes for animA when animB is not provided', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
manager.anims.set('walk', createMockAnim('walk'));
manager.addMix('idle', 'run', 200);
manager.addMix('idle', 'walk', 300);
manager.removeMix('idle');
expect(manager.getMix('idle', 'run')).toBe(0);
expect(manager.getMix('idle', 'walk')).toBe(0);
});
it('should not throw when removing a mix that does not exist', function ()
{
expect(function () { manager.removeMix('nonexistent', 'also-nonexistent'); }).not.toThrow();
});
});
// -------------------------------------------------------------------------
// pauseAll / resumeAll
// -------------------------------------------------------------------------
describe('pauseAll', function ()
{
it('should set paused to true', function ()
{
manager.pauseAll();
expect(manager.paused).toBe(true);
});
it('should emit PAUSE_ALL event', function ()
{
var fired = false;
manager.on('pauseall', function () { fired = true; });
manager.pauseAll();
expect(fired).toBe(true);
});
it('should not emit a second time if already paused', function ()
{
var count = 0;
manager.on('pauseall', function () { count++; });
manager.pauseAll();
manager.pauseAll();
expect(count).toBe(1);
});
it('should return this for chaining', function ()
{
expect(manager.pauseAll()).toBe(manager);
});
});
describe('resumeAll', function ()
{
it('should set paused to false', function ()
{
manager.paused = true;
manager.resumeAll();
expect(manager.paused).toBe(false);
});
it('should emit RESUME_ALL event', function ()
{
var fired = false;
manager.paused = true;
manager.on('resumeall', function () { fired = true; });
manager.resumeAll();
expect(fired).toBe(true);
});
it('should not emit if not currently paused', function ()
{
var count = 0;
manager.on('resumeall', function () { count++; });
manager.resumeAll();
expect(count).toBe(0);
});
it('should return this for chaining', function ()
{
expect(manager.resumeAll()).toBe(manager);
});
});
// -------------------------------------------------------------------------
// remove
// -------------------------------------------------------------------------
describe('remove', function ()
{
it('should return the animation that was removed', function ()
{
var anim = createMockAnim('run');
manager.anims.set('run', anim);
var result = manager.remove('run');
expect(result).toBe(anim);
});
it('should delete the animation from the map', function ()
{
manager.anims.set('run', createMockAnim('run'));
manager.remove('run');
expect(manager.anims.has('run')).toBe(false);
});
it('should emit REMOVE_ANIMATION', function ()
{
var fired = false;
manager.anims.set('run', createMockAnim('run'));
manager.on('remove', function () { fired = true; });
manager.remove('run');
expect(fired).toBe(true);
});
it('should return undefined when key does not exist', function ()
{
expect(manager.remove('ghost')).toBeUndefined();
});
it('should also remove any mixes for the animation', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
manager.addMix('idle', 'run', 200);
manager.remove('idle');
expect(manager.getMix('idle', 'run')).toBe(0);
});
});
// -------------------------------------------------------------------------
// toJSON
// -------------------------------------------------------------------------
describe('toJSON', function ()
{
it('should return an object with anims array and globalTimeScale', function ()
{
var result = manager.toJSON();
expect(Array.isArray(result.anims)).toBe(true);
expect(typeof result.globalTimeScale).toBe('number');
});
it('should include globalTimeScale in output', function ()
{
manager.globalTimeScale = 2;
var result = manager.toJSON();
expect(result.globalTimeScale).toBe(2);
});
it('should return all animations when no key is provided', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
var result = manager.toJSON();
expect(result.anims.length).toBe(2);
});
it('should return only the specified animation when a key is provided', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.anims.set('run', createMockAnim('run'));
var result = manager.toJSON('idle');
expect(result.anims.length).toBe(1);
expect(result.anims[0].key).toBe('idle');
});
});
// -------------------------------------------------------------------------
// fromJSON
// -------------------------------------------------------------------------
describe('fromJSON', function ()
{
it('should accept a JSON string', function ()
{
var json = JSON.stringify({ anims: [], globalTimeScale: 1 });
expect(function () { manager.fromJSON(json); }).not.toThrow();
});
it('should return an empty array when given an empty anims array', function ()
{
var result = manager.fromJSON({ anims: [] });
expect(result).toEqual([]);
});
it('should set globalTimeScale from JSON object', function ()
{
manager.fromJSON({ anims: [], globalTimeScale: 3 });
expect(manager.globalTimeScale).toBe(3);
});
it('should clear existing animations when clearCurrentAnimations is true', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.fromJSON({ anims: [] }, true);
expect(manager.anims.size).toBe(0);
});
it('should not clear existing animations when clearCurrentAnimations is false', function ()
{
manager.anims.set('idle', createMockAnim('idle'));
manager.fromJSON({ anims: [] }, false);
expect(manager.anims.size).toBe(1);
});
});
// -------------------------------------------------------------------------
// play
// -------------------------------------------------------------------------
describe('play', function ()
{
it('should call anims.play on each child', function ()
{
var called = [];
var child = { anims: { play: function (k) { called.push(k); } } };
manager.play('run', [child]);
expect(called).toEqual(['run']);
});
it('should wrap a single child in an array', function ()
{
var called = 0;
var child = { anims: { play: function () { called++; } } };
manager.play('run', child);
expect(called).toBe(1);
});
it('should return this for chaining', function ()
{
expect(manager.play('x', [])).toBe(manager);
});
});
// -------------------------------------------------------------------------
// staggerPlay
// -------------------------------------------------------------------------
describe('staggerPlay', function ()
{
it('should call playAfterDelay with increasing offsets', function ()
{
var delays = [];
function makeChild ()
{
return { anims: { playAfterDelay: function (k, d) { delays.push(d); } } };
}
var children = [ makeChild(), makeChild(), makeChild() ];
manager.staggerPlay('run', children, 100);
expect(delays[0]).toBe(0);
expect(delays[1]).toBe(100);
expect(delays[2]).toBe(200);
});
it('should offset first child when staggerFirst is true (default)', function ()
{
var delays = [];
function makeChild ()
{
return { anims: { playAfterDelay: function (k, d) { delays.push(d); } } };
}
var children = [ makeChild(), makeChild() ];
manager.staggerPlay('run', children, 500, true);
expect(delays[0]).toBe(0);
expect(delays[1]).toBe(500);
});
it('should handle negative stagger in reverse order', function ()
{
var delays = [];
function makeChild ()
{
return { anims: { playAfterDelay: function (k, d) { delays.push(d); } } };
}
var children = [ makeChild(), makeChild(), makeChild() ];
manager.staggerPlay('run', children, -1000);
// len = 3, delays: abs(-1000) * (3-i) => 3000, 2000, 1000
expect(delays[0]).toBe(3000);
expect(delays[1]).toBe(2000);
expect(delays[2]).toBe(1000);
});
it('should return this for chaining', function ()
{
expect(manager.staggerPlay('x', [], 0)).toBe(manager);
});
});
// -------------------------------------------------------------------------
// destroy
// -------------------------------------------------------------------------
describe('destroy', function ()
{
it('should clear the anims map', function ()
{
manager.anims.set('run', createMockAnim('run'));
manager.destroy();
expect(manager.anims.size).toBe(0);
});
it('should clear the mixes map', function ()
{
manager.mixes.set('run', {});
manager.destroy();
expect(manager.mixes.size).toBe(0);
});
it('should null out textureManager', function ()
{
manager.textureManager = {};
manager.destroy();
expect(manager.textureManager).toBeNull();
});
it('should null out game reference', function ()
{
manager.destroy();
expect(manager.game).toBeNull();
});
});
});