UNPKG

playable

Version:

Video player based on HTML5Video

194 lines (161 loc) 6.8 kB
import { expect } from 'chai'; import * as sinon from 'sinon'; import EventEmitter from '../../../../modules/event-emitter/event-emitter'; import { VideoEvent, EngineState } from '../../../../constants'; import StateEngine, { NATIVE_VIDEO_EVENTS_TO_STATE } from './state-engine'; import { setProperty, resetProperty } from '../../../../testkit'; declare const navigator: any; const NATIVE_EVENTS = { LOAD_START: { type: 'loadstart' }, LOADED_META_DATA: { type: 'loadedmetadata' }, CAN_PLAY: { type: 'canplay' }, PLAY: { type: 'play' }, PLAYING: { type: 'playing' }, WAITING: { type: 'waiting' }, PAUSE: { type: 'pause' }, ENDED: { type: 'ended' }, SEEKING: { type: 'seeking' }, SEEKED: { type: 'seeked' }, }; describe('NativeEventsBroadcaster', () => { let video: any; let engine: any; let eventEmitter: any; beforeEach(() => { video = { addEventListener: sinon.spy(), removeEventListener: sinon.spy(), played: { length: 1, }, tagName: 'VIDEO', }; eventEmitter = new EventEmitter(); sinon.spy(eventEmitter, 'emitAsync'); engine = new StateEngine(eventEmitter, video); sinon.spy(engine, 'setState'); }); afterEach(() => { resetProperty(navigator, 'userAgent'); eventEmitter.emitAsync.restore(); engine.setState.restore(); }); it('should attach events to video tag on initialization', () => { expect(video.addEventListener.args.length).to.be.equal( NATIVE_VIDEO_EVENTS_TO_STATE.length, ); video.addEventListener.args.forEach((arg: any) => { expect(NATIVE_VIDEO_EVENTS_TO_STATE.indexOf(arg[0]) !== -1).to.be.true; expect(arg[1] === engine._processEventFromVideo).to.be.true; }); }); it('should detach events from video tag on destroy', () => { engine.destroy(); expect(video.removeEventListener.args.length).to.be.equal( NATIVE_VIDEO_EVENTS_TO_STATE.length, ); video.removeEventListener.args.forEach((arg: any) => { expect(NATIVE_VIDEO_EVENTS_TO_STATE.indexOf(arg[0]) !== -1).to.be.true; expect(arg[1] === engine._processEventFromVideo).to.be.true; }); }); it('should have method for setting state', () => { expect(engine.setState).to.exist; engine._currentState = EngineState.LOAD_STARTED; engine.setState(EngineState.READY_TO_PLAY); expect( eventEmitter.emitAsync.calledWith(VideoEvent.STATE_CHANGED, { prevState: EngineState.LOAD_STARTED, nextState: EngineState.READY_TO_PLAY, }), ); expect(engine._currentState).to.be.equal(EngineState.READY_TO_PLAY); }); it('should not trigger change of state on same state', () => { engine.setState(EngineState.READY_TO_PLAY); expect(eventEmitter.emitAsync.calledTwice).to.be.true; engine.setState(EngineState.READY_TO_PLAY); expect(eventEmitter.emitAsync.calledTwice).to.be.true; }); it('should set state on loadstart event', () => { engine._processEventFromVideo(NATIVE_EVENTS.LOAD_START); expect(engine.setState.calledWith(EngineState.LOAD_STARTED)).to.be.true; }); it('should set state on loadedmetadata event', () => { expect(engine._isMetadataLoaded).to.be.false; engine._processEventFromVideo(NATIVE_EVENTS.LOADED_META_DATA); expect(engine.setState.calledWith(EngineState.METADATA_LOADED)).to.be.true; expect(engine.isMetadataLoaded).to.be.true; }); it('should set state on canplay event only after medatada loaded', () => { engine._processEventFromVideo(NATIVE_EVENTS.CAN_PLAY); expect(engine.setState.calledWith(EngineState.READY_TO_PLAY)).to.be.false; engine._processEventFromVideo(NATIVE_EVENTS.LOADED_META_DATA); engine._processEventFromVideo(NATIVE_EVENTS.CAN_PLAY); expect(engine.setState.calledWith(EngineState.READY_TO_PLAY)).to.be.true; }); it('should set state on play event', () => { engine._processEventFromVideo(NATIVE_EVENTS.PLAY); expect(engine.setState.calledWith(EngineState.PLAY_REQUESTED)).to.be.true; }); it('should set state on playing event', () => { engine._processEventFromVideo(NATIVE_EVENTS.PLAYING); expect(engine.setState.calledWith(EngineState.PLAYING)).to.be.true; }); it('should not set state on playing event if video is not actually playing', () => { setProperty(navigator, 'userAgent', 'safari'); video.paused = true; engine._processEventFromVideo(NATIVE_EVENTS.PLAYING); expect(engine.setState.calledWith(EngineState.PLAYING)).to.be.false; }); it('should set state on waiting event', () => { engine._processEventFromVideo(NATIVE_EVENTS.WAITING); expect(engine.setState.calledWith(EngineState.WAITING)).to.be.true; }); it('should set state on pause event', () => { engine._processEventFromVideo(NATIVE_EVENTS.PAUSE); expect(engine.setState.calledWith(EngineState.PAUSED)).to.be.true; }); it('should not set state on pause event if there is no played chunks', () => { setProperty(navigator, 'userAgent', 'safari'); video.played.length = 0; engine._processEventFromVideo(NATIVE_EVENTS.PAUSE); expect(engine.setState.calledWith(EngineState.PAUSED)).to.be.false; }); it('should set state on ended event', () => { engine._processEventFromVideo(NATIVE_EVENTS.ENDED); expect(engine.setState.calledWith(EngineState.ENDED)).to.be.true; }); it('should set state on seeking event', () => { engine._processEventFromVideo(NATIVE_EVENTS.SEEKING); expect(engine.setState.calledWith(EngineState.SEEK_IN_PROGRESS)).to.be.true; }); it('should set state on seeked event', () => { video.paused = true; engine._processEventFromVideo(NATIVE_EVENTS.SEEKED); expect(engine.setState.calledWith(EngineState.PAUSED)).to.be.true; video.paused = false; engine._processEventFromVideo(NATIVE_EVENTS.SEEKED); expect(engine.setState.calledWith(EngineState.PLAYING)).to.be.true; }); it('should dodge sneaky bug with dash manifest', () => { engine.setState(EngineState.METADATA_LOADED); engine.setState(EngineState.SEEK_IN_PROGRESS); expect(engine.state).to.be.equal(EngineState.METADATA_LOADED); engine.setState(EngineState.PAUSED); expect(engine.state).to.be.equal(EngineState.METADATA_LOADED); }); it('should collect timestamps', () => { engine._processEventFromVideo(NATIVE_EVENTS.LOAD_START); engine._processEventFromVideo(NATIVE_EVENTS.LOADED_META_DATA); engine._processEventFromVideo(NATIVE_EVENTS.CAN_PLAY); expect(Object.keys(engine.stateTimestamps)).to.be.deep.equal([ 'engine-state/metadata-loaded', 'engine-state/ready-to-play', ]); }); it('should do nothing if event is not in list', () => { engine._processEventFromVideo(); expect(eventEmitter.emitAsync.called).to.be.false; }); });