UNPKG

playable

Version:

Video player based on HTML5Video

316 lines (254 loc) 9.53 kB
import { expect } from 'chai'; import * as sinon from 'sinon'; import { VideoEvent, UIEvent, EngineState, LiveState } from '../../constants'; import createPlayerTestkit, { setProperty, resetProperty } from '../../testkit'; describe('LiveStateEngine', () => { let testkit; let engine: any; let liveStateEngine: any; let eventEmitter: any; beforeEach(() => { testkit = createPlayerTestkit(); engine = testkit.getModule('engine'); liveStateEngine = testkit.getModule('liveStateEngine'); eventEmitter = testkit.getModule('eventEmitter'); sinon.spy(eventEmitter, 'emitAsync'); }); afterEach(() => { eventEmitter.emitAsync.restore(); }); it('should reset state on `STATES.SRC_SET`', async function() { const prevState = LiveState.NOT_SYNC; liveStateEngine._setState(prevState); expect( liveStateEngine.state, 'not `LiveState.NONE` before `SRC_SET`', ).to.not.equal(LiveState.NONE); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.SRC_SET, }); expect(liveStateEngine.state).to.equal(LiveState.NONE); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState, nextState: LiveState.NONE, }, ), 'new live state emitted', ).to.equal(true); }); describe('with dynamic content', () => { beforeEach(() => { setProperty(engine, 'isDynamicContent', true); }); afterEach(() => { resetProperty(engine, 'isDynamicContent'); }); it('should set `INITIAL` state on `METADATA_LOADED`', async function() { await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.SRC_SET, }); expect( liveStateEngine.state, '`LiveState.NONE` before `METADATA_LOADED`', ).to.equal(LiveState.NONE); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.METADATA_LOADED, }); expect(liveStateEngine.state).to.equal(LiveState.INITIAL); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.NONE, nextState: LiveState.INITIAL, }, ), 'new live state emitted', ).to.equal(true); }); describe('after `INITIAL`', () => { beforeEach(async function() { await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.SRC_SET, }); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.METADATA_LOADED, }); }); it('should sync to live on `PLAY_REQUESTED`', async function() { const syncWithLiveSpy = sinon.stub(engine, 'syncWithLive'); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAY_REQUESTED, }); expect(syncWithLiveSpy.called).to.equal(true); syncWithLiveSpy.restore(); }); describe('on `PLAYING`', () => { it('should set `SYNC` if `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', true); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.SYNC); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.INITIAL, nextState: LiveState.SYNC, }, ), 'new live state emitted', ).to.equal(true); resetProperty(engine, 'isSyncWithLive'); }); it('should set `NOT_SYNC` if not `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', false); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.NOT_SYNC); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.INITIAL, nextState: LiveState.NOT_SYNC, }, ), 'new live state emitted', ).to.equal(true); resetProperty(engine, 'isSyncWithLive'); }); }); }); describe('after `NOT_SYNC` on `PLAYING`', async function() { beforeEach(() => { liveStateEngine._setState(LiveState.NOT_SYNC); }); it('should set `SYNC` if `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', true); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.SYNC); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.NOT_SYNC, nextState: LiveState.SYNC, }, ), 'new live state emitted', ).to.equal(true); resetProperty(engine, 'isSyncWithLive'); }); it('should ignore if not `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', false); // reset spy state before test eventEmitter.emitAsync.resetHistory(); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.NOT_SYNC); // NOTE: ensure emit is not called with new `LiveState` expect(eventEmitter.emitAsync.callCount).to.equal(1); resetProperty(engine, 'isSyncWithLive'); }); }); describe('on `PLAYING` after seek', () => { beforeEach(async function() { engine._output._stateEngine.setState(EngineState.PLAYING); liveStateEngine._setState(LiveState.SYNC); // emulate seek by UI await eventEmitter.emitAsync(UIEvent.PROGRESS_CHANGE); }); it('should ignore if `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', true); // reset spy state before test eventEmitter.emitAsync.resetHistory(); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.SYNC); // NOTE: ensure emit is not called with new `LiveState` expect(eventEmitter.emitAsync.callCount).to.equal(1); resetProperty(engine, 'isSyncWithLive'); }); it('should set `NOT_SYNC` if not `isSyncWithLive`', async function() { setProperty(engine, 'isSyncWithLive', false); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.PLAYING, }); expect(liveStateEngine.state).to.equal(LiveState.NOT_SYNC); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.SYNC, nextState: LiveState.NOT_SYNC, }, ), 'new live state emitted', ).to.equal(true); resetProperty(engine, 'isSyncWithLive'); }); }); it('should set `NOT_SYNC` on `PAUSE` by UI', async function() { liveStateEngine._setState(LiveState.SYNC); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { prevState: EngineState.PLAYING, nextState: EngineState.PAUSED, }); expect(liveStateEngine.state).to.equal(LiveState.NOT_SYNC); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.SYNC, nextState: LiveState.NOT_SYNC, }, ), 'new live state emitted', ).to.equal(true); }); it('should set `ENDED` on stream ended', async function() { liveStateEngine._setState(LiveState.SYNC); await eventEmitter.emitAsync(VideoEvent.DYNAMIC_CONTENT_ENDED); expect(liveStateEngine.state).to.equal(LiveState.ENDED); expect( eventEmitter.emitAsync.lastCall.calledWith( VideoEvent.LIVE_STATE_CHANGED, { prevState: LiveState.SYNC, nextState: LiveState.ENDED, }, ), 'new live state emitted', ).to.equal(true); }); }); it('should ignore events if not `isDynamicContent`', async function() { setProperty(engine, 'isDynamicContent', false); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.SRC_SET, }); expect( liveStateEngine.state, '`LiveState.NONE` before `METADATA_LOADED`', ).to.equal(LiveState.NONE); // reset spy state before test eventEmitter.emitAsync.resetHistory(); await eventEmitter.emitAsync(VideoEvent.STATE_CHANGED, { nextState: EngineState.METADATA_LOADED, }); expect(liveStateEngine.state).to.equal(LiveState.NONE); // NOTE: ensure emit is not called with new `LiveState` expect(eventEmitter.emitAsync.callCount).to.equal(1); resetProperty(engine, 'isDynamicContent'); }); });