UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

612 lines (508 loc) 19.8 kB
var TweenData = require('../../../src/tweens/tween/TweenData'); var Events = require('../../../src/tweens/events'); function createMockTween (target) { return { targets: [ target !== undefined ? target : { x: 0 } ], totalTargets: 1, duration: 0, startDelay: 999, isInfinite: false, isSeeking: false, isNumberTween: false, callbacks: {}, callbackScope: null, emit: vi.fn() }; } function createTweenData (tweenOverride, options) { var opts = options || {}; var tween = tweenOverride || createMockTween(); var getEnd = opts.getEnd || function (t, k, v) { return 100; }; var getStart = opts.getStart || function (t, k, v) { return 0; }; var getActive = opts.getActive !== undefined ? opts.getActive : null; var ease = opts.ease || function (v) { return v; }; var delay = opts.delay || function () { return 0; }; var duration = opts.duration !== undefined ? opts.duration : 1000; var yoyo = opts.yoyo !== undefined ? opts.yoyo : false; var hold = opts.hold !== undefined ? opts.hold : 0; var repeat = opts.repeat !== undefined ? opts.repeat : 0; var repeatDelay = opts.repeatDelay !== undefined ? opts.repeatDelay : 0; var flipX = opts.flipX !== undefined ? opts.flipX : false; var flipY = opts.flipY !== undefined ? opts.flipY : false; var interpolation = opts.interpolation !== undefined ? opts.interpolation : null; var interpolationData = opts.interpolationData !== undefined ? opts.interpolationData : null; return new TweenData( tween, 0, opts.key !== undefined ? opts.key : 'x', getEnd, getStart, getActive, ease, delay, duration, yoyo, hold, repeat, repeatDelay, flipX, flipY, interpolation, interpolationData ); } describe('TweenData', function () { describe('constructor', function () { it('should store the key property', function () { var td = createTweenData(null, { key: 'alpha' }); expect(td.key).toBe('alpha'); }); it('should store the ease function', function () { var ease = function (v) { return v * v; }; var td = createTweenData(null, { ease: ease }); expect(td.ease).toBe(ease); }); it('should store getEndValue callback', function () { var getEnd = function () { return 200; }; var td = createTweenData(null, { getEnd: getEnd }); expect(td.getEndValue).toBe(getEnd); }); it('should store getStartValue callback', function () { var getStart = function () { return 50; }; var td = createTweenData(null, { getStart: getStart }); expect(td.getStartValue).toBe(getStart); }); it('should store getActiveValue callback when provided', function () { var getActive = function () { return 10; }; var td = createTweenData(null, { getActive: getActive }); expect(td.getActiveValue).toBe(getActive); }); it('should set getActiveValue to null when not provided', function () { var td = createTweenData(null, { getActive: null }); expect(td.getActiveValue).toBeNull(); }); it('should initialise start, previous, current and end to zero', function () { var td = createTweenData(); expect(td.start).toBe(0); expect(td.previous).toBe(0); expect(td.current).toBe(0); expect(td.end).toBe(0); }); it('should store interpolation function', function () { var interp = function (data, v) { return v; }; var td = createTweenData(null, { interpolation: interp }); expect(td.interpolation).toBe(interp); }); it('should store interpolationData array', function () { var data = [ 0, 50, 100 ]; var td = createTweenData(null, { interpolationData: data }); expect(td.interpolationData).toBe(data); }); it('should default interpolation and interpolationData to null', function () { var td = createTweenData(); expect(td.interpolation).toBeNull(); expect(td.interpolationData).toBeNull(); }); it('should inherit tween and targetIndex from BaseTweenData', function () { var tween = createMockTween(); var td = createTweenData(tween); expect(td.tween).toBe(tween); expect(td.targetIndex).toBe(0); }); it('should inherit yoyo, hold, repeat and repeatDelay from BaseTweenData', function () { var td = createTweenData(null, { yoyo: true, hold: 200, repeat: 3, repeatDelay: 100 }); expect(td.yoyo).toBe(true); expect(td.hold).toBe(200); expect(td.repeat).toBe(3); expect(td.repeatDelay).toBe(100); }); it('should inherit flipX and flipY from BaseTweenData', function () { var td = createTweenData(null, { flipX: true, flipY: true }); expect(td.flipX).toBe(true); expect(td.flipY).toBe(true); }); it('should set a minimum duration of 0.01 when duration is zero', function () { var td = createTweenData(null, { duration: 0 }); expect(td.duration).toBe(0.01); }); it('should store the provided duration when positive', function () { var td = createTweenData(null, { duration: 500 }); expect(td.duration).toBe(500); }); }); describe('reset', function () { it('should reset start, previous, current and end to zero', function () { var td = createTweenData(); td.start = 50; td.previous = 25; td.current = 75; td.end = 100; td.reset(); expect(td.start).toBe(0); expect(td.previous).toBe(0); expect(td.current).toBe(0); expect(td.end).toBe(0); }); it('should reset progress and elapsed to zero', function () { var td = createTweenData(); td.progress = 0.8; td.elapsed = 800; td.reset(); expect(td.progress).toBe(0); expect(td.elapsed).toBe(0); }); it('should set state to PENDING_RENDER when there is no delay', function () { var td = createTweenData(null, { delay: function () { return 0; } }); td.reset(); expect(td.isPendingRender()).toBe(true); }); it('should set state to DELAY when delay is greater than zero', function () { var td = createTweenData(null, { delay: function () { return 500; } }); td.reset(); expect(td.isDelayed()).toBe(true); }); it('should assign target[key] to this.start when isSeeking is true', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x' }); td.start = 42; td.reset(true); expect(target.x).toBe(42); }); it('should not assign target[key] to this.start when isSeeking is false', function () { var target = { x: 99 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x' }); td.start = 42; td.reset(false); // After reset, start is zeroed — isSeeking=false path does not write to target expect(td.start).toBe(0); }); it('should call getActiveValue and apply it to target[key] when getActiveValue is set', function () { var target = { x: 0 }; var tween = createMockTween(target); var getActive = vi.fn(function () { return 77; }); var td = createTweenData(tween, { key: 'x', getActive: getActive }); td.reset(); expect(getActive).toHaveBeenCalled(); expect(target.x).toBe(77); }); it('should not call getActiveValue when it is null', function () { var td = createTweenData(null, { getActive: null }); // Should not throw expect(function () { td.reset(); }).not.toThrow(); }); }); describe('update', function () { it('should return false and set complete state when target is null', function () { var tween = createMockTween(null); tween.targets = [ null ]; var td = createTweenData(tween); td.setPlayingForwardState(); var result = td.update(16); expect(result).toBe(false); expect(td.isComplete()).toBe(true); }); it('should return false and set complete state when target isDestroyed', function () { var target = { x: 0, isDestroyed: true }; var tween = createMockTween(target); var td = createTweenData(tween); td.setPlayingForwardState(); var result = td.update(16); expect(result).toBe(false); expect(td.isComplete()).toBe(true); }); it('should initialise start, end and current during PENDING_RENDER and return true', function () { var target = { x: 5 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', getStart: function () { return 10; }, getEnd: function () { return 200; } }); td.setPendingRenderState(); var result = td.update(0); expect(result).toBe(true); expect(td.start).toBe(10); expect(td.end).toBe(200); expect(td.current).toBe(10); expect(target.x).toBe(10); expect(td.isPlayingForward()).toBe(true); }); it('should advance elapsed and progress when playing forward', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); // PENDING_RENDER -> PLAYING_FORWARD td.update(500); expect(td.elapsed).toBe(500); expect(td.progress).toBeCloseTo(0.5); }); it('should update target[key] to interpolated value when playing forward', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000, ease: function (v) { return v; }, getStart: function () { return 0; }, getEnd: function () { return 100; } }); td.setPendingRenderState(); td.update(0); // PENDING_RENDER -> PLAYING_FORWARD, start=0, end=100 td.update(500); expect(td.current).toBeCloseTo(50); expect(target.x).toBeCloseTo(50); }); it('should return true while still playing forward', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); var result = td.update(500); expect(result).toBe(true); }); it('should return false when playback is complete (no repeat/yoyo)', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); td.update(1000); var result = td.update(0); expect(result).toBe(false); }); it('should set state to COMPLETE when playback finishes with no yoyo or repeat', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); td.update(1000); expect(td.isComplete()).toBe(true); }); it('should use interpolation function when set', function () { var target = { x: 0 }; var tween = createMockTween(target); var interpData = [ 0, 50, 100 ]; var interp = vi.fn(function (data, v) { return data[1]; }); var td = createTweenData(tween, { key: 'x', duration: 1000, interpolation: interp, interpolationData: interpData }); td.setPendingRenderState(); td.update(0); td.update(500); expect(interp).toHaveBeenCalledWith(interpData, expect.any(Number)); expect(target.x).toBe(50); }); it('should clamp progress to 1 when elapsed exceeds duration', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); td.update(2000); expect(td.progress).toBeLessThanOrEqual(1); }); it('should track previous value from the prior step', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); td.update(400); var afterFirst = td.current; td.update(200); expect(td.previous).toBe(afterFirst); }); it('should dispatch TWEEN_UPDATE event while playing forward', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 1000 }); td.setPendingRenderState(); td.update(0); tween.emit.mockClear(); td.update(500); expect(tween.emit).toHaveBeenCalledWith( Events.TWEEN_UPDATE, tween, 'x', target, expect.any(Number), expect.any(Number) ); }); it('should set hold state when hold > 0 and forward playback completes', function () { var target = { x: 0 }; var tween = createMockTween(target); var td = createTweenData(tween, { key: 'x', duration: 500, hold: 200 }); td.setPendingRenderState(); td.update(0); td.update(500); expect(td.isHolding()).toBe(true); }); }); describe('dispatchEvent', function () { it('should emit event on tween when not seeking', function () { var target = { x: 0 }; var tween = createMockTween(target); tween.isSeeking = false; var td = createTweenData(tween, { key: 'x' }); td.current = 75; td.previous = 50; td.dispatchEvent(Events.TWEEN_UPDATE, null); expect(tween.emit).toHaveBeenCalledWith( Events.TWEEN_UPDATE, tween, 'x', target, 75, 50 ); }); it('should not emit event when isSeeking is true', function () { var tween = createMockTween(); tween.isSeeking = true; var td = createTweenData(tween); td.dispatchEvent(Events.TWEEN_UPDATE, null); expect(tween.emit).not.toHaveBeenCalled(); }); it('should invoke the named callback handler when it exists', function () { var target = { x: 0 }; var tween = createMockTween(target); tween.isSeeking = false; tween.callbackScope = {}; var handlerFunc = vi.fn(); tween.callbacks = { onUpdate: { func: handlerFunc, params: [] } }; var td = createTweenData(tween); td.dispatchEvent(Events.TWEEN_UPDATE, 'onUpdate'); expect(handlerFunc).toHaveBeenCalled(); }); it('should not throw when callback name is null', function () { var tween = createMockTween(); tween.isSeeking = false; tween.callbacks = {}; var td = createTweenData(tween); expect(function () { td.dispatchEvent(Events.TWEEN_UPDATE, null); }).not.toThrow(); }); it('should not throw when callback name refers to missing handler', function () { var tween = createMockTween(); tween.isSeeking = false; tween.callbacks = {}; var td = createTweenData(tween); expect(function () { td.dispatchEvent(Events.TWEEN_UPDATE, 'onUpdate'); }).not.toThrow(); }); it('should pass extra handler params when provided', function () { var target = { x: 0 }; var tween = createMockTween(target); tween.isSeeking = false; tween.callbackScope = {}; var handlerFunc = vi.fn(); tween.callbacks = { onUpdate: { func: handlerFunc, params: [ 'extra1', 'extra2' ] } }; var td = createTweenData(tween); td.current = 10; td.previous = 5; td.dispatchEvent(Events.TWEEN_UPDATE, 'onUpdate'); var args = handlerFunc.mock.calls[0]; expect(args).toContain('extra1'); expect(args).toContain('extra2'); }); }); describe('destroy', function () { it('should null out getActiveValue', function () { var td = createTweenData(null, { getActive: function () { return 0; } }); td.destroy(); expect(td.getActiveValue).toBeNull(); }); it('should null out getEndValue', function () { var td = createTweenData(); td.destroy(); expect(td.getEndValue).toBeNull(); }); it('should null out getStartValue', function () { var td = createTweenData(); td.destroy(); expect(td.getStartValue).toBeNull(); }); it('should null out ease', function () { var td = createTweenData(); td.destroy(); expect(td.ease).toBeNull(); }); it('should null out tween reference via BaseTweenData destroy', function () { var td = createTweenData(); td.destroy(); expect(td.tween).toBeNull(); }); it('should set state to COMPLETE via BaseTweenData destroy', function () { var td = createTweenData(); td.destroy(); expect(td.isComplete()).toBe(true); }); }); });