UNPKG

phaser

Version:

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

1,037 lines (785 loc) 27.4 kB
var EventEmitter = require('eventemitter3'); var Timeline = require('../../src/time/Timeline'); function createMockScene () { var sysEvents = new EventEmitter(); return { sys: { events: sysEvents, tweens: { add: vi.fn(function () { return { paused: false, stop: vi.fn() }; }) }, sound: { play: vi.fn() } } }; } function createTimeline (config) { var scene = createMockScene(); return new Timeline(scene, config); } describe('Timeline', function () { describe('constructor', function () { it('should set default property values', function () { var tl = createTimeline(); expect(tl.elapsed).toBe(0); expect(tl.timeScale).toBe(1); expect(tl.paused).toBe(true); expect(tl.complete).toBe(false); expect(tl.totalComplete).toBe(0); expect(tl.loop).toBe(0); expect(tl.iteration).toBe(0); expect(Array.isArray(tl.events)).toBe(true); expect(tl.events.length).toBe(0); }); it('should store the scene and systems references', function () { var scene = createMockScene(); var tl = new Timeline(scene); expect(tl.scene).toBe(scene); expect(tl.systems).toBe(scene.sys); }); it('should add events when config is provided as an object', function () { var tl = createTimeline({ at: 500, run: function () {} }); expect(tl.events.length).toBe(1); expect(tl.events[0].time).toBe(500); }); it('should add events when config is provided as an array', function () { var tl = createTimeline([ { at: 100 }, { at: 200 }, { at: 300 } ]); expect(tl.events.length).toBe(3); }); it('should register listeners on the scene event emitter', function () { var scene = createMockScene(); var listenerCount = scene.sys.events.listenerCount('preupdate'); new Timeline(scene); expect(scene.sys.events.listenerCount('preupdate')).toBeGreaterThan(listenerCount); }); }); describe('preUpdate', function () { it('should increase elapsed by delta when not paused', function () { var tl = createTimeline(); tl.paused = false; tl.preUpdate(0, 16); expect(tl.elapsed).toBe(16); }); it('should accumulate elapsed over multiple calls', function () { var tl = createTimeline(); tl.paused = false; tl.preUpdate(0, 16); tl.preUpdate(16, 16); tl.preUpdate(32, 16); expect(tl.elapsed).toBe(48); }); it('should not increase elapsed when paused', function () { var tl = createTimeline(); tl.paused = true; tl.preUpdate(0, 16); expect(tl.elapsed).toBe(0); }); it('should scale delta by timeScale', function () { var tl = createTimeline(); tl.paused = false; tl.timeScale = 2; tl.preUpdate(0, 16); expect(tl.elapsed).toBe(32); }); it('should apply timeScale of 0.5 correctly', function () { var tl = createTimeline(); tl.paused = false; tl.timeScale = 0.5; tl.preUpdate(0, 100); expect(tl.elapsed).toBe(50); }); it('should freeze time when timeScale is 0', function () { var tl = createTimeline(); tl.paused = false; tl.timeScale = 0; tl.preUpdate(0, 16); expect(tl.elapsed).toBe(0); }); }); describe('update', function () { it('should not process events when paused', function () { var ran = false; var tl = createTimeline({ at: 0, run: function () { ran = true; } }); tl.paused = true; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(false); }); it('should not process events when complete', function () { var ran = false; var tl = createTimeline({ at: 0, run: function () { ran = true; } }); tl.paused = false; tl.complete = true; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(false); }); it('should fire an event when elapsed >= event.time', function () { var ran = false; var tl = createTimeline({ at: 100, run: function () { ran = true; } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(true); }); it('should not fire an event when elapsed < event.time', function () { var ran = false; var tl = createTimeline({ at: 200, run: function () { ran = true; } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(false); }); it('should increment totalComplete when an event fires', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.totalComplete).toBe(1); }); it('should mark the event as complete after firing', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.events[0].complete).toBe(true); }); it('should set complete=true when all events have fired', function () { var tl = createTimeline([{ at: 0 }, { at: 50 }]); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.complete).toBe(true); }); it('should not set complete when some events have not yet fired', function () { var tl = createTimeline([{ at: 50 }, { at: 200 }]); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.complete).toBe(false); }); it('should remove once events after they fire', function () { var tl = createTimeline({ at: 0, once: true }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.events.length).toBe(0); }); it('should emit the event name when event.event is set', function () { var tl = createTimeline({ at: 0, event: 'MY_EVENT' }); tl.paused = false; tl.elapsed = 100; var emitted = false; tl.on('MY_EVENT', function () { emitted = true; }); tl.update(0, 0); expect(emitted).toBe(true); }); it('should call run with target as context when target is set', function () { var target = { name: 'myTarget' }; var ctx = null; var tl = createTimeline({ at: 0, run: function () { ctx = this; }, target: target }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(ctx).toBe(target); }); it('should apply set properties to target', function () { var target = { x: 0, y: 0 }; var tl = createTimeline({ at: 0, target: target, set: { x: 42, y: 99 } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(target.x).toBe(42); expect(target.y).toBe(99); }); it('should call stop when event.stop is true', function () { var tl = createTimeline({ at: 0, stop: true }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(tl.paused).toBe(true); expect(tl.complete).toBe(true); }); it('should skip event when if callback returns false', function () { var ran = false; var tl = createTimeline({ at: 0, if: function () { return false; }, run: function () { ran = true; } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(false); }); it('should run event when if callback returns true', function () { var ran = false; var tl = createTimeline({ at: 0, if: function () { return true; }, run: function () { ran = true; } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); expect(ran).toBe(true); }); it('should emit COMPLETE event when all events are done', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; var completed = false; tl.on('complete', function () { completed = true; }); tl.update(0, 0); expect(completed).toBe(true); }); it('should not fire an event that is already complete', function () { var count = 0; var tl = createTimeline({ at: 0, run: function () { count++; } }); tl.paused = false; tl.elapsed = 100; tl.update(0, 0); tl.update(0, 0); expect(count).toBe(1); }); it('should loop the timeline when loop is set', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; tl.loop = 1; tl.update(0, 0); expect(tl.iteration).toBe(1); expect(tl.elapsed).toBe(0); expect(tl.complete).toBe(false); }); it('should stop looping when iteration reaches loop count', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; tl.loop = 1; tl.iteration = 1; tl.update(0, 0); expect(tl.complete).toBe(true); }); it('should loop indefinitely when loop is -1', function () { var tl = createTimeline({ at: 0 }); tl.paused = false; tl.elapsed = 100; tl.loop = -1; tl.iteration = 100; tl.update(0, 0); expect(tl.iteration).toBe(101); expect(tl.complete).toBe(false); }); }); describe('play', function () { it('should set paused to false', function () { var tl = createTimeline(); tl.paused = true; tl.play(); expect(tl.paused).toBe(false); }); it('should set complete to false', function () { var tl = createTimeline(); tl.complete = true; tl.play(); expect(tl.complete).toBe(false); }); it('should reset totalComplete to zero', function () { var tl = createTimeline(); tl.totalComplete = 5; tl.play(); expect(tl.totalComplete).toBe(0); }); it('should reset elapsed to zero when fromStart is true', function () { var tl = createTimeline({ at: 0 }); tl.elapsed = 999; tl.play(true); expect(tl.elapsed).toBe(0); }); it('should not reset elapsed when fromStart is false', function () { var tl = createTimeline(); tl.elapsed = 500; tl.paused = false; tl.play(false); expect(tl.elapsed).toBe(500); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.play(); expect(result).toBe(tl); }); }); describe('pause', function () { it('should set paused to true', function () { var tl = createTimeline(); tl.paused = false; tl.pause(); expect(tl.paused).toBe(true); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.pause(); expect(result).toBe(tl); }); it('should pause any active tween instances', function () { var tweenInstance = { paused: false, stop: vi.fn() }; var tl = createTimeline({ at: 0, tween: {} }); tl.events[0].tweenInstance = tweenInstance; tl.paused = false; tl.pause(); expect(tweenInstance.paused).toBe(true); }); }); describe('repeat', function () { it('should set loop to -1 when called with no arguments', function () { var tl = createTimeline(); tl.repeat(); expect(tl.loop).toBe(-1); }); it('should set loop to -1 when called with true', function () { var tl = createTimeline(); tl.repeat(true); expect(tl.loop).toBe(-1); }); it('should set loop to 0 when called with false', function () { var tl = createTimeline(); tl.loop = -1; tl.repeat(false); expect(tl.loop).toBe(0); }); it('should set loop to -1 when called with a negative number', function () { var tl = createTimeline(); tl.repeat(-5); expect(tl.loop).toBe(-5); }); it('should set loop to the given positive number', function () { var tl = createTimeline(); tl.repeat(3); expect(tl.loop).toBe(3); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.repeat(); expect(result).toBe(tl); }); }); describe('resume', function () { it('should set paused to false', function () { var tl = createTimeline(); tl.paused = true; tl.resume(); expect(tl.paused).toBe(false); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.resume(); expect(result).toBe(tl); }); it('should resume any paused tween instances', function () { var tweenInstance = { paused: true, stop: vi.fn() }; var tl = createTimeline({ at: 0 }); tl.events[0].tweenInstance = tweenInstance; tl.resume(); expect(tweenInstance.paused).toBe(false); }); }); describe('stop', function () { it('should set paused to true', function () { var tl = createTimeline(); tl.paused = false; tl.stop(); expect(tl.paused).toBe(true); }); it('should set complete to true', function () { var tl = createTimeline(); tl.complete = false; tl.stop(); expect(tl.complete).toBe(true); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.stop(); expect(result).toBe(tl); }); }); describe('reset', function () { it('should reset elapsed to zero', function () { var tl = createTimeline(); tl.elapsed = 500; tl.reset(); expect(tl.elapsed).toBe(0); }); it('should reset iteration to zero when loop is false', function () { var tl = createTimeline(); tl.iteration = 5; tl.reset(); expect(tl.iteration).toBe(0); }); it('should preserve iteration when loop is true', function () { var tl = createTimeline(); tl.iteration = 3; tl.reset(true); expect(tl.iteration).toBe(3); }); it('should mark all events as incomplete', function () { var tl = createTimeline([{ at: 0 }, { at: 100 }]); tl.events[0].complete = true; tl.events[1].complete = true; tl.reset(); expect(tl.events[0].complete).toBe(false); expect(tl.events[1].complete).toBe(false); }); it('should reset event repeat counts when loop is false', function () { var tl = createTimeline({ at: 0 }); tl.events[0].repeat = 3; tl.reset(); expect(tl.events[0].repeat).toBe(0); }); it('should preserve event repeat counts when loop is true', function () { var tl = createTimeline({ at: 0 }); tl.events[0].repeat = 3; tl.reset(true); expect(tl.events[0].repeat).toBe(3); }); it('should stop any active tween instances', function () { var tweenInstance = { paused: false, stop: vi.fn() }; var tl = createTimeline({ at: 0 }); tl.events[0].tweenInstance = tweenInstance; tl.reset(); expect(tweenInstance.stop).toHaveBeenCalled(); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.reset(); expect(result).toBe(tl); }); }); describe('add', function () { it('should add a single event from a config object', function () { var tl = createTimeline(); tl.add({ at: 500 }); expect(tl.events.length).toBe(1); }); it('should add multiple events from an array', function () { var tl = createTimeline(); tl.add([{ at: 100 }, { at: 200 }, { at: 300 }]); expect(tl.events.length).toBe(3); }); it('should set event time from the at property', function () { var tl = createTimeline(); tl.add({ at: 750 }); expect(tl.events[0].time).toBe(750); }); it('should default event time to 0 when at is not specified', function () { var tl = createTimeline(); tl.add({}); expect(tl.events[0].time).toBe(0); }); it('should calculate time from elapsed when in is specified', function () { var tl = createTimeline(); tl.elapsed = 200; tl.add({ in: 300 }); expect(tl.events[0].time).toBe(500); }); it('should calculate time relative to previous event when from is specified', function () { var tl = createTimeline(); tl.add([{ at: 500 }, { from: 200 }]); expect(tl.events[1].time).toBe(700); }); it('should set event complete to false', function () { var tl = createTimeline(); tl.add({ at: 0 }); expect(tl.events[0].complete).toBe(false); }); it('should set event repeat to 0', function () { var tl = createTimeline(); tl.add({ at: 0 }); expect(tl.events[0].repeat).toBe(0); }); it('should store the run callback', function () { var fn = function () {}; var tl = createTimeline(); tl.add({ at: 0, run: fn }); expect(tl.events[0].run).toBe(fn); }); it('should store the event name', function () { var tl = createTimeline(); tl.add({ at: 0, event: 'MY_EVENT' }); expect(tl.events[0].event).toBe('MY_EVENT'); }); it('should store the target reference', function () { var target = { id: 1 }; var tl = createTimeline(); tl.add({ at: 0, target: target }); expect(tl.events[0].target).toBe(target); }); it('should set once to false by default', function () { var tl = createTimeline(); tl.add({ at: 0 }); expect(tl.events[0].once).toBe(false); }); it('should set once to true when specified', function () { var tl = createTimeline(); tl.add({ at: 0, once: true }); expect(tl.events[0].once).toBe(true); }); it('should set stop to false by default', function () { var tl = createTimeline(); tl.add({ at: 0 }); expect(tl.events[0].stop).toBe(false); }); it('should set complete to false on the timeline itself', function () { var tl = createTimeline(); tl.complete = true; tl.add({ at: 0 }); expect(tl.complete).toBe(false); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.add({ at: 0 }); expect(result).toBe(tl); }); }); describe('clear', function () { it('should remove all events', function () { var tl = createTimeline([{ at: 0 }, { at: 100 }, { at: 200 }]); tl.clear(); expect(tl.events.length).toBe(0); }); it('should reset elapsed to zero', function () { var tl = createTimeline(); tl.elapsed = 999; tl.clear(); expect(tl.elapsed).toBe(0); }); it('should set paused to true', function () { var tl = createTimeline(); tl.paused = false; tl.clear(); expect(tl.paused).toBe(true); }); it('should stop any active tween instances', function () { var tweenInstance = { paused: false, stop: vi.fn() }; var tl = createTimeline({ at: 0 }); tl.events[0].tweenInstance = tweenInstance; tl.clear(); expect(tweenInstance.stop).toHaveBeenCalled(); }); it('should return the Timeline instance for chaining', function () { var tl = createTimeline(); var result = tl.clear(); expect(result).toBe(tl); }); }); describe('isPlaying', function () { it('should return false when paused is true', function () { var tl = createTimeline(); tl.paused = true; tl.complete = false; expect(tl.isPlaying()).toBe(false); }); it('should return false when complete is true', function () { var tl = createTimeline(); tl.paused = false; tl.complete = true; expect(tl.isPlaying()).toBe(false); }); it('should return false when both paused and complete are true', function () { var tl = createTimeline(); tl.paused = true; tl.complete = true; expect(tl.isPlaying()).toBe(false); }); it('should return true when not paused and not complete', function () { var tl = createTimeline(); tl.paused = false; tl.complete = false; expect(tl.isPlaying()).toBe(true); }); }); describe('getProgress', function () { it('should return 0 when no events have completed', function () { var tl = createTimeline([{ at: 100 }, { at: 200 }, { at: 300 }]); expect(tl.getProgress()).toBe(0); }); it('should return 0.5 when half of events have completed', function () { var tl = createTimeline([{ at: 100 }, { at: 200 }]); tl.totalComplete = 1; expect(tl.getProgress()).toBe(0.5); }); it('should return 1 when all events have completed', function () { var tl = createTimeline([{ at: 100 }, { at: 200 }]); tl.totalComplete = 2; expect(tl.getProgress()).toBe(1); }); it('should return NaN when there are no events', function () { var tl = createTimeline(); expect(isNaN(tl.getProgress())).toBe(true); }); it('should cap progress at 1 even if totalComplete exceeds event count', function () { var tl = createTimeline([{ at: 100 }, { at: 200 }]); tl.totalComplete = 10; expect(tl.getProgress()).toBe(1); }); it('should return a value between 0 and 1 for partial progress', function () { var tl = createTimeline([{ at: 100 }, { at: 200 }, { at: 300 }, { at: 400 }]); tl.totalComplete = 1; expect(tl.getProgress()).toBeCloseTo(0.25); }); }); describe('destroy', function () { it('should set scene to null', function () { var tl = createTimeline(); tl.destroy(); expect(tl.scene).toBeNull(); }); it('should set systems to null', function () { var tl = createTimeline(); tl.destroy(); expect(tl.systems).toBeNull(); }); it('should clear all events', function () { var tl = createTimeline([{ at: 0 }, { at: 100 }]); tl.destroy(); expect(tl.events.length).toBe(0); }); it('should remove listeners from the scene event emitter', function () { var scene = createMockScene(); var tl = new Timeline(scene); var listenerCountBefore = scene.sys.events.listenerCount('preupdate'); tl.destroy(); expect(scene.sys.events.listenerCount('preupdate')).toBeLessThan(listenerCountBefore); }); }); describe('chaining', function () { it('should support method chaining for play, pause, resume, stop', function () { var tl = createTimeline(); var result = tl.play().pause().resume().stop(); expect(result).toBe(tl); }); it('should support chaining repeat and play', function () { var tl = createTimeline({ at: 0 }); var result = tl.repeat().play(); expect(result).toBe(tl); expect(tl.loop).toBe(-1); expect(tl.paused).toBe(false); }); }); });