phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
997 lines (759 loc) • 28.2 kB
JavaScript
var Tween = require('../../../src/tweens/tween/Tween');
var TWEEN_CONST = require('../../../src/tweens/tween/const');
function createParent ()
{
return {
timeScale: 1,
makeActive: function () {},
remove: function () {},
reset: function () {}
};
}
function createTween (targets, parent)
{
if (targets === undefined) { targets = [ { x: 0, y: 0 } ]; }
if (parent === undefined) { parent = createParent(); }
return new Tween(parent, targets);
}
function createTweenData (key, current, start, end, isForward)
{
return {
key: key,
current: current,
start: start,
end: end,
isPlayingForward: function () { return isForward === true; },
isPlayingBackward: function () { return isForward === false; },
reset: function () {}
};
}
describe('Tween', function ()
{
describe('constructor', function ()
{
it('should store the given targets', function ()
{
var targets = [ { x: 0 }, { y: 0 } ];
var tween = createTween(targets);
expect(tween.targets).toBe(targets);
});
it('should set totalTargets to targets.length', function ()
{
var targets = [ {}, {}, {} ];
var tween = createTween(targets);
expect(tween.totalTargets).toBe(3);
});
it('should initialise isSeeking to false', function ()
{
var tween = createTween();
expect(tween.isSeeking).toBe(false);
});
it('should initialise isInfinite to false', function ()
{
var tween = createTween();
expect(tween.isInfinite).toBe(false);
});
it('should initialise elapsed to zero', function ()
{
var tween = createTween();
expect(tween.elapsed).toBe(0);
});
it('should initialise totalElapsed to zero', function ()
{
var tween = createTween();
expect(tween.totalElapsed).toBe(0);
});
it('should initialise duration to zero', function ()
{
var tween = createTween();
expect(tween.duration).toBe(0);
});
it('should initialise progress to zero', function ()
{
var tween = createTween();
expect(tween.progress).toBe(0);
});
it('should initialise totalDuration to zero', function ()
{
var tween = createTween();
expect(tween.totalDuration).toBe(0);
});
it('should initialise totalProgress to zero', function ()
{
var tween = createTween();
expect(tween.totalProgress).toBe(0);
});
it('should initialise isNumberTween to false', function ()
{
var tween = createTween();
expect(tween.isNumberTween).toBe(false);
});
it('should store the parent reference', function ()
{
var parent = createParent();
var tween = createTween([ {} ], parent);
expect(tween.parent).toBe(parent);
});
it('should work with an empty targets array', function ()
{
var tween = createTween([]);
expect(tween.targets).toEqual([]);
expect(tween.totalTargets).toBe(0);
});
});
describe('add', function ()
{
it('should return a TweenData instance', function ()
{
var tween = createTween();
var getEnd = function () { return 100; };
var getStart = function () { return 0; };
var ease = function (v) { return v; };
var delay = function () { return 0; };
var tweenData = tween.add(0, 'x', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
expect(tweenData).not.toBeNull();
expect(typeof tweenData).toBe('object');
});
it('should push the TweenData into the data array', function ()
{
var tween = createTween();
var getEnd = function () { return 100; };
var getStart = function () { return 0; };
var ease = function (v) { return v; };
var delay = function () { return 0; };
expect(tween.data.length).toBe(0);
tween.add(0, 'x', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
expect(tween.data.length).toBe(1);
});
it('should update totalData after adding', function ()
{
var tween = createTween();
var getEnd = function () { return 100; };
var getStart = function () { return 0; };
var ease = function (v) { return v; };
var delay = function () { return 0; };
tween.add(0, 'x', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
expect(tween.totalData).toBe(1);
});
it('should accumulate multiple TweenData entries', function ()
{
var tween = createTween([ { x: 0, y: 0 } ]);
var getEnd = function () { return 100; };
var getStart = function () { return 0; };
var ease = function (v) { return v; };
var delay = function () { return 0; };
tween.add(0, 'x', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
tween.add(0, 'y', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
expect(tween.data.length).toBe(2);
expect(tween.totalData).toBe(2);
});
});
describe('addFrame', function ()
{
it('should return a TweenFrameData instance', function ()
{
var tween = createTween();
var delay = function () { return 0; };
var tweenData = tween.addFrame(0, 'texture', 'frame01', delay, 1000, 0, 0, 0, false, false);
expect(tweenData).not.toBeNull();
expect(typeof tweenData).toBe('object');
});
it('should push the TweenFrameData into the data array', function ()
{
var tween = createTween();
var delay = function () { return 0; };
tween.addFrame(0, 'texture', 'frame01', delay, 1000, 0, 0, 0, false, false);
expect(tween.data.length).toBe(1);
});
it('should update totalData after adding', function ()
{
var tween = createTween();
var delay = function () { return 0; };
tween.addFrame(0, 'texture', 'frame01', delay, 1000, 0, 0, 0, false, false);
expect(tween.totalData).toBe(1);
});
it('should accumulate alongside TweenData entries', function ()
{
var tween = createTween();
var delay = function () { return 0; };
var ease = function (v) { return v; };
var getEnd = function () { return 100; };
var getStart = function () { return 0; };
tween.add(0, 'x', getEnd, getStart, null, ease, delay, 1000, false, 0, 0, 0, false, false, null, null);
tween.addFrame(0, 'texture', 'frame01', delay, 1000, 0, 0, 0, false, false);
expect(tween.totalData).toBe(2);
});
});
describe('getValue', function ()
{
it('should return null when data is null (destroyed tween)', function ()
{
var tween = createTween();
tween.data = null;
expect(tween.getValue()).toBeNull();
});
it('should return the current value of the first tween data by default', function ()
{
var tween = createTween();
tween.data = [ createTweenData('x', 42, 0, 100, true) ];
expect(tween.getValue()).toBe(42);
});
it('should return the current value at the specified index', function ()
{
var tween = createTween();
tween.data = [
createTweenData('x', 10, 0, 100, true),
createTweenData('y', 50, 0, 100, true)
];
expect(tween.getValue(0)).toBe(10);
expect(tween.getValue(1)).toBe(50);
});
it('should default index to 0 when not provided', function ()
{
var tween = createTween();
tween.data = [
createTweenData('x', 77, 0, 100, true),
createTweenData('y', 33, 0, 100, true)
];
expect(tween.getValue()).toBe(77);
});
it('should return a floating point value', function ()
{
var tween = createTween();
tween.data = [ createTweenData('x', 3.14, 0, 10, true) ];
expect(tween.getValue()).toBeCloseTo(3.14);
});
});
describe('hasTarget', function ()
{
it('should return true when the target is in the targets array', function ()
{
var target = { x: 0 };
var tween = createTween([ target ]);
expect(tween.hasTarget(target)).toBe(true);
});
it('should return false when the target is not in the targets array', function ()
{
var target = { x: 0 };
var other = { x: 1 };
var tween = createTween([ target ]);
expect(tween.hasTarget(other)).toBe(false);
});
it('should return falsy when targets is null', function ()
{
var tween = createTween();
tween.targets = null;
expect(tween.hasTarget({})).toBeFalsy();
});
it('should return false for an empty targets array', function ()
{
var tween = createTween([]);
expect(tween.hasTarget({ x: 0 })).toBe(false);
});
it('should find the correct target among multiple targets', function ()
{
var t1 = { id: 1 };
var t2 = { id: 2 };
var t3 = { id: 3 };
var tween = createTween([ t1, t2, t3 ]);
expect(tween.hasTarget(t1)).toBe(true);
expect(tween.hasTarget(t2)).toBe(true);
expect(tween.hasTarget(t3)).toBe(true);
expect(tween.hasTarget({ id: 4 })).toBe(false);
});
});
describe('updateTo', function ()
{
it('should return this for method chaining', function ()
{
var tween = createTween();
tween.data = [];
tween.totalData = 0;
expect(tween.updateTo('x', 100)).toBe(tween);
});
it('should update end value for matching forward-playing tween data', function ()
{
var tween = createTween();
var td = createTweenData('x', 50, 0, 100, true);
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('x', 200);
expect(td.end).toBe(200);
});
it('should update end value for matching backward-playing tween data', function ()
{
var tween = createTween();
var td = createTweenData('x', 50, 0, 100, false);
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('x', 300);
expect(td.end).toBe(300);
});
it('should not update end when tween data is not playing', function ()
{
var tween = createTween();
var td = {
key: 'x',
current: 50,
start: 0,
end: 100,
isPlayingForward: function () { return false; },
isPlayingBackward: function () { return false; }
};
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('x', 999);
expect(td.end).toBe(100);
});
it('should not update any data when key is texture', function ()
{
var tween = createTween();
var td = createTweenData('texture', 0, 0, 1, true);
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('texture', 999);
expect(td.end).toBe(1);
});
it('should set start to current when startToCurrent is true', function ()
{
var tween = createTween();
var td = createTweenData('x', 75, 0, 100, true);
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('x', 200, true);
expect(td.end).toBe(200);
expect(td.start).toBe(75);
});
it('should not modify start when startToCurrent is false', function ()
{
var tween = createTween();
var td = createTweenData('x', 75, 0, 100, true);
tween.data = [ td ];
tween.totalData = 1;
tween.updateTo('x', 200, false);
expect(td.start).toBe(0);
});
it('should only update tween data with matching key', function ()
{
var tween = createTween();
var tdX = createTweenData('x', 50, 0, 100, true);
var tdY = createTweenData('y', 50, 0, 100, true);
tween.data = [ tdX, tdY ];
tween.totalData = 2;
tween.updateTo('x', 999);
expect(tdX.end).toBe(999);
expect(tdY.end).toBe(100);
});
});
describe('onCompleteHandler', function ()
{
it('should set progress to 1', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.emit = function () {};
tween.setDestroyedState = function () {};
tween.setFinishedState = function () {};
tween.setPendingRemoveState = function () {};
tween.chain = null;
tween.persist = false;
tween.onCompleteHandler();
expect(tween.progress).toBe(1);
});
it('should set totalProgress to 1', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.emit = function () {};
tween.setDestroyedState = function () {};
tween.setFinishedState = function () {};
tween.setPendingRemoveState = function () {};
tween.chain = null;
tween.persist = false;
tween.onCompleteHandler();
expect(tween.totalProgress).toBe(1);
});
});
describe('dispatchEvent', function ()
{
it('should emit the event when not seeking', function ()
{
var tween = createTween();
var emitted = [];
tween.emit = function (event)
{
emitted.push(event);
};
tween.callbacks = null;
tween.isSeeking = false;
tween.dispatchEvent('test-event', null);
expect(emitted.length).toBe(1);
expect(emitted[0]).toBe('test-event');
});
it('should not emit when seeking', function ()
{
var tween = createTween();
var emitted = [];
tween.emit = function (event)
{
emitted.push(event);
};
tween.callbacks = null;
tween.isSeeking = true;
tween.dispatchEvent('test-event', null);
expect(emitted.length).toBe(0);
});
it('should invoke the callback handler when present and not seeking', function ()
{
var tween = createTween();
var called = false;
tween.emit = function () {};
tween.isSeeking = false;
tween.callbackScope = null;
tween.callbacks = {
onComplete: {
func: function () { called = true; },
params: []
}
};
tween.dispatchEvent('some-event', 'onComplete');
expect(called).toBe(true);
});
it('should not invoke callback when isSeeking is true', function ()
{
var tween = createTween();
var called = false;
tween.emit = function () {};
tween.isSeeking = true;
tween.callbackScope = null;
tween.callbacks = {
onComplete: {
func: function () { called = true; },
params: []
}
};
tween.dispatchEvent('some-event', 'onComplete');
expect(called).toBe(false);
});
it('should not throw when callbacks is null', function ()
{
var tween = createTween();
tween.emit = function () {};
tween.isSeeking = false;
tween.callbacks = null;
expect(function ()
{
tween.dispatchEvent('some-event', 'onComplete');
}).not.toThrow();
});
it('should pass tween and targets to the callback', function ()
{
var target = { x: 0 };
var tween = createTween([ target ]);
var received = null;
tween.emit = function () {};
tween.isSeeking = false;
tween.callbackScope = null;
tween.callbacks = {
onStart: {
func: function (tw, targets)
{
received = { tw: tw, targets: targets };
},
params: []
}
};
tween.dispatchEvent('tween-start', 'onStart');
expect(received).not.toBeNull();
expect(received.tw).toBe(tween);
expect(received.targets).toBe(tween.targets);
});
});
describe('forward', function ()
{
it('should return this for method chaining', function ()
{
var tween = createTween();
tween.update = function () { return false; };
expect(tween.forward(100)).toBe(tween);
});
it('should call update with the given ms value', function ()
{
var tween = createTween();
var updateCalledWith = null;
tween.update = function (delta)
{
updateCalledWith = delta;
return false;
};
tween.forward(250);
expect(updateCalledWith).toBe(250);
});
});
describe('rewind', function ()
{
it('should return this for method chaining', function ()
{
var tween = createTween();
tween.update = function () { return false; };
expect(tween.rewind(100)).toBe(tween);
});
it('should call update with the negated ms value', function ()
{
var tween = createTween();
var updateCalledWith = null;
tween.update = function (delta)
{
updateCalledWith = delta;
return false;
};
tween.rewind(250);
expect(updateCalledWith).toBe(-250);
});
it('should negate zero correctly', function ()
{
var tween = createTween();
var updateCalledWith = null;
tween.update = function (delta)
{
updateCalledWith = delta;
return false;
};
tween.rewind(0);
expect(updateCalledWith).toBe(-0);
});
});
describe('reset', function ()
{
it('should return this for method chaining', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
expect(tween.reset()).toBe(tween);
});
it('should reset elapsed to zero', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.elapsed = 999;
tween.reset();
expect(tween.elapsed).toBe(0);
});
it('should reset totalElapsed to zero', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.totalElapsed = 999;
tween.reset();
expect(tween.totalElapsed).toBe(0);
});
it('should reset progress to zero', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.progress = 0.75;
tween.reset();
expect(tween.progress).toBe(0);
});
it('should reset totalProgress to zero', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.totalProgress = 0.5;
tween.reset();
expect(tween.totalProgress).toBe(0);
});
it('should set loopCounter to loop value', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.loop = 3;
tween.reset();
expect(tween.loopCounter).toBe(3);
});
it('should set isInfinite to true when loop is -1', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.loop = -1;
tween.reset();
expect(tween.isInfinite).toBe(true);
});
it('should set loopCounter to TWEEN_CONST.MAX when loop is -1', function ()
{
var tween = createTween();
tween.dispatchEvent = function () {};
tween.initTweenData = function () {};
tween.setActiveState = function () {};
tween.loop = -1;
tween.reset();
expect(tween.loopCounter).toBe(TWEEN_CONST.MAX);
});
it('should not call initTweenData when skipInit is true', function ()
{
var tween = createTween();
var initCalled = false;
tween.dispatchEvent = function () {};
tween.initTweenData = function () { initCalled = true; };
tween.setActiveState = function () {};
tween.reset(true);
expect(initCalled).toBe(false);
});
it('should call initTweenData when skipInit is false', function ()
{
var tween = createTween();
var initCalled = false;
tween.dispatchEvent = function () {};
tween.initTweenData = function () { initCalled = true; };
tween.setActiveState = function () {};
tween.reset(false);
expect(initCalled).toBe(true);
});
});
describe('initTweenData', function ()
{
it('should clamp duration to at least 0.01', function ()
{
var tween = createTween();
// No data, so duration stays 0 — gets clamped to 0.01
tween.data = [];
tween.totalData = 0;
tween.completeDelay = 0;
tween.loopCounter = 0;
tween.loopDelay = 0;
tween.initTweenData();
expect(tween.duration).toBeCloseTo(0.01);
});
it('should compute totalDuration without loops', function ()
{
var tween = createTween();
var resetCount = 0;
tween.data = [
{
reset: function ()
{
resetCount++;
tween.duration = 1000;
tween.startDelay = 0;
}
}
];
tween.totalData = 1;
tween.completeDelay = 200;
tween.loopCounter = 0;
tween.loopDelay = 0;
tween.initTweenData();
// duration (1000) + completeDelay (200) = 1200
expect(tween.totalDuration).toBe(1200);
});
it('should compute totalDuration with loops', function ()
{
var tween = createTween();
tween.data = [
{
reset: function ()
{
tween.duration = 1000;
tween.startDelay = 0;
}
}
];
tween.totalData = 1;
tween.completeDelay = 0;
tween.loopCounter = 2;
tween.loopDelay = 100;
tween.initTweenData();
// duration + ((duration + loopDelay) * loopCounter)
// 1000 + ((1000 + 100) * 2) = 1000 + 2200 = 3200
expect(tween.totalDuration).toBe(3200);
});
it('should call reset on each tween data entry', function ()
{
var tween = createTween();
var resets = [];
tween.data = [
{
reset: function (seeking)
{
resets.push(seeking);
tween.duration = 500;
tween.startDelay = 0;
}
},
{
reset: function (seeking)
{
resets.push(seeking);
}
}
];
tween.totalData = 2;
tween.completeDelay = 0;
tween.loopCounter = 0;
tween.loopDelay = 0;
tween.initTweenData(false);
expect(resets.length).toBe(2);
expect(resets[0]).toBe(false);
expect(resets[1]).toBe(false);
});
it('should pass isSeeking=true to tween data reset when seeking', function ()
{
var tween = createTween();
var seekingArg = null;
tween.data = [
{
reset: function (seeking)
{
seekingArg = seeking;
tween.duration = 500;
tween.startDelay = 0;
}
}
];
tween.totalData = 1;
tween.completeDelay = 0;
tween.loopCounter = 0;
tween.loopDelay = 0;
tween.initTweenData(true);
expect(seekingArg).toBe(true);
});
});
describe('destroy', function ()
{
it('should set targets to null', function ()
{
var tween = createTween([ { x: 0 } ]);
// Stub BaseTween.destroy dependencies
tween.emit = function () {};
tween.removeAllListeners = function () {};
tween.setDestroyedState = function () {};
tween.data = [];
tween.destroy();
expect(tween.targets).toBeNull();
});
});
describe('hasTarget after destroy', function ()
{
it('should return falsy after targets set to null', function ()
{
var target = { x: 0 };
var tween = createTween([ target ]);
tween.targets = null;
expect(tween.hasTarget(target)).toBeFalsy();
});
});
});