TESTS: Add tests for fix of end-time in the video player.

BLD-662
This commit is contained in:
Valera Rozuvan
2014-04-09 05:19:13 +00:00
committed by Valera Rozuvan
parent dc373f22b1
commit 450dfe17b0
9 changed files with 302 additions and 104 deletions

View File

@@ -117,7 +117,7 @@
runs(function () {
expect(state.videoPlayer.player.getPlayerState())
.toBe(STATUS.PLAYING);
.toBe(STATUS.BUFFERING);
});
});

View File

@@ -366,19 +366,22 @@ function (VideoPlayer) {
});
it('Slider event causes log update', function () {
runs(function () {
var currentTime = state.videoPlayer.currentTime;
spyOn(state.videoPlayer, 'log');
state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 2 }
);
});
waitsFor(function () {
return state.videoPlayer.currentTime >= 2;
}, 'currentTime is less than 2 seconds', WAIT_TIMEOUT);
runs(function () {
expect(state.videoPlayer.log).toHaveBeenCalledWith(
'seek_video',
{
old_time: currentTime,
old_time: jasmine.any(Number),
new_time: 2,
type: 'onSlideSeek'
}
@@ -388,25 +391,37 @@ function (VideoPlayer) {
it('seek the player', function () {
runs(function () {
spyOn(state.videoPlayer.player, 'seekTo');
spyOn(state.videoPlayer.player, 'seekTo').andCallThrough();
state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 60 }
jQuery.Event('slide'), { value: 30 }
);
});
waitsFor(function () {
return state.videoPlayer.currentTime >= 30;
}, 'currentTime is less than 30 seconds', WAIT_TIMEOUT);
runs(function () {
expect(state.videoPlayer.player.seekTo)
.toHaveBeenCalledWith(60, true);
.toHaveBeenCalledWith(30, true);
});
});
it('call updatePlayTime on player', function () {
runs(function () {
spyOn(state.videoPlayer, 'updatePlayTime');
spyOn(state.videoPlayer, 'updatePlayTime').andCallThrough();
state.videoProgressSlider.onSlide(
jQuery.Event('slide'), { value: 60 }
jQuery.Event('slide'), { value: 30 }
);
});
waitsFor(function () {
return state.videoPlayer.currentTime >= 30;
}, 'currentTime is less than 30 seconds', WAIT_TIMEOUT);
runs(function () {
expect(state.videoPlayer.updatePlayTime)
.toHaveBeenCalledWith(60);
.toHaveBeenCalledWith(jasmine.any(Number));
});
});
@@ -1070,6 +1085,7 @@ function (VideoPlayer) {
youtubeId: jasmine.createSpy().andReturn('videoId'),
isFlashMode: jasmine.createSpy().andReturn(false),
isHtml5Mode: jasmine.createSpy().andReturn(true),
isYoutubeType: jasmine.createSpy().andReturn(true),
setPlayerMode: jasmine.createSpy(),
videoPlayer: {
currentTime: 60,
@@ -1104,12 +1120,12 @@ function (VideoPlayer) {
});
it('in HTML5 mode', function () {
state.isYoutubeType.andReturn(false);
VideoPlayer.prototype.setPlaybackRate.call(state, '0.75');
expect(state.videoPlayer.player.setPlaybackRate).toHaveBeenCalledWith('0.75');
});
it('Youtube video in FF, with new speed equal 1.0', function () {
state.videoType = 'youtube';
state.browserIsFirefox = true;
state.videoPlayer.isPlaying.andReturn(false);

View File

@@ -68,6 +68,7 @@ function (VideoPlayer, VideoStorage) {
initialize: initialize,
isHtml5Mode: isHtml5Mode,
isFlashMode: isFlashMode,
isYoutubeType: isYoutubeType,
parseSpeed: parseSpeed,
parseVideoSources: parseVideoSources,
parseYoutubeStreams: parseYoutubeStreams,
@@ -847,6 +848,10 @@ function (VideoPlayer, VideoStorage) {
return this.getPlayerMode() === 'html5';
}
function isYoutubeType() {
return this.videoType === 'youtube';
}
function speedToString(speed) {
return parseFloat(speed).toFixed(2).replace(/\.00$/, '.0');
}

View File

@@ -275,12 +275,13 @@ function () {
// Register the 'play' event.
this.video.addEventListener('play', function () {
_this.playerState = HTML5Video.PlayerState.PLAYING;
_this.playerState = HTML5Video.PlayerState.BUFFERING;
_this.callStateChangeCallback();
}, false);
this.video.addEventListener('playing', function () {
_this.playerState = HTML5Video.PlayerState.PLAYING;
_this.callStateChangeCallback();
}, false);
// Register the 'pause' event.

View File

@@ -17,13 +17,24 @@ function (HTML5Video, Resizer) {
methodsDict = {
duration: duration,
handlePlaybackQualityChange: handlePlaybackQualityChange,
// Added for finer graded seeking control.
// Please see:
// https://developers.google.com/youtube/js_api_reference#Events
isBuffering: isBuffering,
// https://developers.google.com/youtube/js_api_reference#cueVideoById
isCued: isCued,
isEnded: isEnded,
isPlaying: isPlaying,
isUnstarted: isUnstarted,
log: log,
onCaptionSeek: onSeek,
onEnded: onEnded,
onPause: onPause,
onPlay: onPlay,
runTimer: runTimer,
stopTimer: stopTimer,
onPlaybackQualityChange: onPlaybackQualityChange,
onReady: onReady,
onSlideSeek: onSeek,
@@ -54,7 +65,17 @@ function (HTML5Video, Resizer) {
// Functions which will be accessible via 'state' object. When called,
// these functions will get the 'state' object as a context.
function _makeFunctionsPublic(state) {
var debouncedF = _.debounce(
function (params) {
return onSeek.call(this, params);
}.bind(state),
300
);
state.bindTo(methodsDict, state.videoPlayer, state);
state.videoPlayer.onSlideSeek = debouncedF;
state.videoPlayer.onCaptionSeek = debouncedF;
}
// function _initialize(state)
@@ -79,10 +100,10 @@ function (HTML5Video, Resizer) {
state.videoPlayer.player.setVolume(state.currentVolume);
});
if (state.videoType === 'youtube') {
if (state.isYoutubeType()) {
state.videoPlayer.PlayerState = YT.PlayerState;
state.videoPlayer.PlayerState.UNSTARTED = -1;
} else { // if (state.videoType === 'html5') {
} else {
state.videoPlayer.PlayerState = HTML5Video.PlayerState;
}
@@ -139,7 +160,7 @@ function (HTML5Video, Resizer) {
_updateVcrAndRegion(state);
}, false);
} else { // if (state.videoType === 'youtube') {
} else {
youTubeId = state.youtubeId();
state.videoPlayer.player = new YT.Player(state.id, {
@@ -305,8 +326,8 @@ function (HTML5Video, Resizer) {
// This function gets the video's current play position in time
// (currentTime) and its duration.
// It is called at a regular interval when the video is playing.
function update() {
this.videoPlayer.currentTime = this.videoPlayer.player.getCurrentTime();
function update(time) {
this.videoPlayer.currentTime = time || this.videoPlayer.player.getCurrentTime();
if (isFinite(this.videoPlayer.currentTime)) {
this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime);
@@ -339,7 +360,7 @@ function (HTML5Video, Resizer) {
!(
this.browserIsFirefox &&
newSpeed === '1.0' &&
this.videoType === 'youtube'
this.isYoutubeType()
)
) {
this.videoPlayer.player.setPlaybackRate(newSpeed);
@@ -404,6 +425,7 @@ function (HTML5Video, Resizer) {
return;
}
this.el.off('play.seek');
this.videoPlayer.log(
'seek_video',
{
@@ -420,24 +442,49 @@ function (HTML5Video, Resizer) {
this.videoPlayer.stopAtEndTime = false;
}
this.videoPlayer.player.seekTo(newTime, true);
if (this.videoPlayer.isPlaying()) {
clearInterval(this.videoPlayer.updateInterval);
this.videoPlayer.updateInterval = setInterval(
this.videoPlayer.update, 200
);
setTimeout(this.videoPlayer.update, 0);
this.videoPlayer.stopTimer();
} else {
this.videoPlayer.currentTime = newTime;
}
var isUnplayed = this.videoPlayer.isUnstarted() ||
this.videoPlayer.isCued();
this.videoPlayer.updatePlayTime(newTime);
// Use `cueVideoById` method for youtube video that is not played before.
if (isUnplayed && this.isYoutubeType()) {
this.videoPlayer.player.cueVideoById(this.youtubeId(), newTime);
} else {
// Youtube video cannot be rewinded during bufferization, so wait to
// finish bufferization and then rewind the video.
if (this.isYoutubeType() && this.videoPlayer.isBuffering()) {
this.el.on('play.seek', function () {
this.videoPlayer.player.seekTo(newTime, true);
}.bind(this));
} else {
// Otherwise, just seek the video
this.videoPlayer.player.seekTo(newTime, true);
}
}
this.videoPlayer.updatePlayTime(newTime, true);
this.el.trigger('seek', arguments);
}
function runTimer() {
if (!this.videoPlayer.updateInterval) {
this.videoPlayer.updateInterval = window.setInterval(
this.videoPlayer.update, 200
);
this.videoPlayer.update();
}
}
function stopTimer() {
window.clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval;
}
function onEnded() {
var time = this.videoPlayer.duration();
@@ -466,8 +513,7 @@ function (HTML5Video, Resizer) {
}
);
clearInterval(this.videoPlayer.updateInterval);
delete this.videoPlayer.updateInterval;
this.videoPlayer.stopTimer();
this.trigger('videoControl.pause', null);
this.saveState(true);
@@ -482,14 +528,7 @@ function (HTML5Video, Resizer) {
}
);
if (!this.videoPlayer.updateInterval) {
this.videoPlayer.updateInterval = setInterval(
this.videoPlayer.update, 200
);
this.videoPlayer.update();
}
this.videoPlayer.runTimer();
this.trigger('videoControl.play', null);
this.trigger('videoProgressSlider.notifyThroughHandleEnd', {
end: false
@@ -558,7 +597,7 @@ function (HTML5Video, Resizer) {
// https://github.com/edx/edx-platform/pull/2841
if (
(this.isHtml5Mode() || availablePlaybackRates.length > 1) &&
this.videoType === 'youtube'
this.isYoutubeType()
) {
if (availablePlaybackRates.length === 1 && !this.isTouch) {
// This condition is needed in cases when Firefox version is
@@ -620,20 +659,34 @@ function (HTML5Video, Resizer) {
}
function onStateChange(event) {
this.el.removeClass([
'is-unstarted', 'is-playing', 'is-paused', 'is-buffered',
'is-ended', 'is-cued'
].join(' '));
switch (event.data) {
case this.videoPlayer.PlayerState.UNSTARTED:
this.el.addClass('is-unstarted');
this.videoPlayer.onUnstarted();
break;
case this.videoPlayer.PlayerState.PLAYING:
this.el.addClass('is-playing');
this.videoPlayer.onPlay();
break;
case this.videoPlayer.PlayerState.PAUSED:
this.el.addClass('is-paused');
this.videoPlayer.onPause();
break;
case this.videoPlayer.PlayerState.BUFFERING:
this.el.addClass('is-buffered');
this.el.trigger('buffering');
break;
case this.videoPlayer.PlayerState.ENDED:
this.el.addClass('is-ended');
this.videoPlayer.onEnded();
break;
case this.videoPlayer.PlayerState.CUED:
this.el.addClass('is-cued');
this.videoPlayer.player.seekTo(this.videoPlayer.seekToTimeOnCued, true);
// We need to call play() explicitly because after the call
// to functions cueVideoById() followed by seekTo() the video
@@ -711,12 +764,12 @@ function (HTML5Video, Resizer) {
return time;
}
function updatePlayTime(time) {
function updatePlayTime(time, skip_seek) {
var videoPlayer = this.videoPlayer,
duration = this.videoPlayer.duration(),
youTubeId;
if (duration > 0 && videoPlayer.goToStartTime) {
if (duration > 0 && videoPlayer.goToStartTime && !skip_seek) {
videoPlayer.goToStartTime = false;
// The duration might have changed. Update the start-end time region to
@@ -746,7 +799,7 @@ function (HTML5Video, Resizer) {
//
// HTML5 video sources play fine from start-time in both Chrome
// and Firefox.
if (this.browserIsFirefox && this.videoType === 'youtube') {
if (this.browserIsFirefox && this.isYoutubeType()) {
youTubeId = this.youtubeId();
// When we will call cueVideoById() for some strange reason
@@ -794,10 +847,27 @@ function (HTML5Video, Resizer) {
}
function isPlaying() {
var playerState = this.videoPlayer.player.getPlayerState(),
PLAYING = this.videoPlayer.PlayerState.PLAYING;
var playerState = this.videoPlayer.player.getPlayerState();
return playerState === PLAYING;
return playerState === this.videoPlayer.PlayerState.PLAYING;
}
function isBuffering() {
var playerState = this.videoPlayer.player.getPlayerState();
return playerState === this.videoPlayer.PlayerState.BUFFERING;
}
function isCued() {
var playerState = this.videoPlayer.player.getPlayerState();
return playerState === this.videoPlayer.PlayerState.CUED;
}
function isUnstarted() {
var playerState = this.videoPlayer.player.getPlayerState();
return playerState === this.videoPlayer.PlayerState.UNSTARTED;
}
/*
@@ -844,7 +914,7 @@ function (HTML5Video, Resizer) {
// might differ by one or two seconds against the actual time as will
// be reported later on by the player.getDuration() API function.
if (!isFinite(dur) || dur <= 0) {
if (this.videoType === 'youtube') {
if (this.isYoutubeType()) {
dur = this.getDuration();
}
}
@@ -873,9 +943,9 @@ function (HTML5Video, Resizer) {
});
}
if (this.videoType === 'youtube') {
if (this.isYoutubeType()) {
logInfo.code = this.youtubeId();
} else if (this.videoType === 'html5') {
} else {
logInfo.code = 'html5';
}

View File

@@ -177,15 +177,26 @@ function () {
}
function onSlide(event, ui) {
var time = ui.value,
duration = this.videoPlayer.duration();
this.videoProgressSlider.frozen = true;
// Remember the seek to value so that we don't repeat ourselves on the
// 'stop' slider event.
this.videoProgressSlider.lastSeekValue = ui.value;
this.videoProgressSlider.lastSeekValue = time;
this.trigger(
'videoControl.updateVcrVidTime',
{
time: time,
duration: duration
}
);
this.trigger(
'videoPlayer.onSlideSeek',
{'type': 'onSlideSeek', 'time': ui.value}
{'type': 'onSlideSeek', 'time': time}
);
// ARIA