From 606cd6b551837a50751828aa90c88df9279d9be2 Mon Sep 17 00:00:00 2001 From: Tom Coward <34864926+tom-coward@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:34:56 +0100 Subject: [PATCH] Add tests for setPlaybackRate & setStallState in VideoModel --- src/streaming/models/VideoModel.js | 15 +++- test/unit/mocks/VideoElementMock.js | 35 +++++++++ .../streaming/streaming.models.VideoModel.js | 71 +++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 test/unit/test/streaming/streaming.models.VideoModel.js diff --git a/src/streaming/models/VideoModel.js b/src/streaming/models/VideoModel.js index 769bf08005..18f08f2b9a 100644 --- a/src/streaming/models/VideoModel.js +++ b/src/streaming/models/VideoModel.js @@ -48,6 +48,7 @@ function VideoModel() { let instance, logger, + settings, element, _currentTime, setCurrentTimeReadyStateFunction, @@ -61,11 +62,11 @@ function VideoModel() { const context = this.context; const eventBus = EventBus(context).getInstance(); - const settings = Settings(context).getInstance(); const stalledStreams = []; function setup() { logger = Debug(context).getInstance().getLogger(instance); + settings = Settings(context).getInstance(); _currentTime = NaN; } @@ -76,6 +77,17 @@ function VideoModel() { function reset() { clearTimeout(timeout); eventBus.off(Events.PLAYBACK_PLAYING, onPlaying, this); + stalledStreams.length = 0; + } + + function setConfig(config) { + if (!config) { + return; + } + + if (config.settings) { + settings = config.settings; + } } function setPlaybackRate(value, ignoreReadyState = false) { @@ -540,6 +552,7 @@ function VideoModel() { removeChild, removeEventListener, reset, + setConfig, setCurrentTime, setDisableRemotePlayback, setElement, diff --git a/test/unit/mocks/VideoElementMock.js b/test/unit/mocks/VideoElementMock.js index 30c2b52e4a..9e0a127772 100644 --- a/test/unit/mocks/VideoElementMock.js +++ b/test/unit/mocks/VideoElementMock.js @@ -26,6 +26,8 @@ class VideoElementMock { this.nodeName = 'VIDEO'; this.videoWidth = 800; this.videoHeight = 600; + this.readyState = 0; + this.events = {}; } constructor() { @@ -46,6 +48,39 @@ class VideoElementMock { return textTrack.getCurrentCue(); } + addEventListener(type, handler) { + if (this.events.hasOwnProperty(type)) { + this.events[type].push(handler); + } else { + this.events[type] = [handler]; + } + } + + removeEventListener(type, handler) { + if (!this.events.hasOwnProperty(type)) { + return; + } + + let index = this.events[type].indexOf(handler); + if (index != -1) { + this.events[type].splice(index, 1); + } + } + + dispatchEvent(event) { + const { type } = event; + + if (!this.events.hasOwnProperty(type)) { + return; + } + + let evs = this.events[type]; + let l = evs.length; + for (let i = 0; i < l; i++) { + evs[i](); + } + } + reset() { this.setup(); } diff --git a/test/unit/test/streaming/streaming.models.VideoModel.js b/test/unit/test/streaming/streaming.models.VideoModel.js new file mode 100644 index 0000000000..8fda9f7607 --- /dev/null +++ b/test/unit/test/streaming/streaming.models.VideoModel.js @@ -0,0 +1,71 @@ +import VideoModel from '../../../../src/streaming/models/VideoModel.js'; +import VideoElementMock from '../../mocks/VideoElementMock.js'; +import Settings from '../../../../src/core/Settings.js'; +import Constants from '../../../../src/streaming/constants/Constants.js'; + +import {expect} from 'chai'; + +describe('VideoModel', () => { + const context = {}; + const videoModel = VideoModel(context).getInstance(); + const videoElementMock = new VideoElementMock(); + const settings = Settings(context).getInstance(); + + beforeEach(() => { + videoModel.setElement(videoElementMock); + }); + + afterEach(() => { + videoModel.reset(); + videoElementMock.reset(); + settings.reset(); + }); + + describe('setPlaybackRate()', () => { + it('Should always set playback rate even when not in ready state if ignoring ready state', () => { + videoElementMock.playbackRate = 1; + videoElementMock.readyState = Constants.VIDEO_ELEMENT_READY_STATES.HAVE_NOTHING; + + videoModel.setPlaybackRate(0, true); + expect(videoElementMock.playbackRate).to.equal(0); + }); + + it('Should set playback rate if the video element is in ready state', () => { + videoElementMock.playbackRate = 1; + videoElementMock.readyState = Constants.VIDEO_ELEMENT_READY_STATES.HAVE_FUTURE_DATA; + + videoModel.setPlaybackRate(0.5, false); + expect(videoElementMock.playbackRate).to.equal(0.5); + }); + }); + + describe('setStallState()', () => { + describe('syntheticStallEvents enabled', () => { + beforeEach(() => { + settings.update({ streaming: { buffer: { syntheticStallEvents: { enabled: true, ignoreReadyState: false } }}}); + videoModel.setConfig({ settings }); + }) + + it('Should set playback rate to 0 on stall if video element is in ready state', () => { + videoElementMock.playbackRate = 1; + videoElementMock.readyState = Constants.VIDEO_ELEMENT_READY_STATES.HAVE_FUTURE_DATA; + + videoModel.setStallState('video', true); + + expect(videoElementMock.playbackRate).to.equal(0); + }); + + it('Should emit a waiting event on stall if video element is in ready state', (done) => { + videoElementMock.readyState = Constants.VIDEO_ELEMENT_READY_STATES.HAVE_FUTURE_DATA; + + const onWaiting = () => { + videoElementMock.removeEventListener('waiting', onWaiting); + done(); + }; + videoElementMock.addEventListener('waiting', onWaiting); + + videoModel.setStallState('video', true); + }); + }); + }); +}); \ No newline at end of file