* fix: eslint operator-linebreak issue * fix: eslint quotes issue * fix: react jsx indent and props issues * fix: eslint trailing spaces issues * fix: eslint line around directives issue * fix: eslint semi rule * fix: eslint newline per chain rule * fix: eslint space infix ops rule * fix: eslint space-in-parens issue * fix: eslint space before function paren issue * fix: eslint space before blocks issue * fix: eslint arrow body style issue * fix: eslint dot-location issue * fix: eslint quotes issue * fix: eslint quote props issue * fix: eslint operator assignment issue * fix: eslint new line after import issue * fix: indent issues * fix: operator assignment issue * fix: all autofixable eslint issues * fix: all react related fixable issues * fix: autofixable eslint issues * chore: remove all template literals * fix: remaining autofixable issues * chore: apply amnesty on all existing issues * fix: failing xss-lint issues * refactor: apply amnesty on remaining issues * refactor: apply amnesty on new issues * fix: remove file level suppressions * refactor: apply amnesty on new issues
1102 lines
51 KiB
JavaScript
1102 lines
51 KiB
JavaScript
/* global YT */
|
|
|
|
// eslint-disable-next-line no-shadow-restricted-names
|
|
(function(require, define, undefined) {
|
|
'use strict';
|
|
|
|
require(
|
|
['video/03_video_player.js', 'hls', 'underscore'],
|
|
function(VideoPlayer, HLS, _) {
|
|
describe('VideoPlayer', function() {
|
|
var STATUS = window.STATUS,
|
|
state,
|
|
oldOTBD,
|
|
emptyArguments;
|
|
|
|
(function() {
|
|
emptyArguments = arguments;
|
|
}());
|
|
|
|
beforeEach(function() {
|
|
oldOTBD = window.onTouchBasedDevice;
|
|
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
|
|
.and.returnValue(null);
|
|
});
|
|
|
|
afterEach(function() {
|
|
$('source').remove();
|
|
window.onTouchBasedDevice = oldOTBD;
|
|
window.Video.previousState = null;
|
|
if (state.storage) {
|
|
state.storage.clear();
|
|
}
|
|
if (state.videoPlayer) {
|
|
_.result(state.videoPlayer, 'destroy');
|
|
}
|
|
});
|
|
|
|
describe('constructor', function() {
|
|
describe('always', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
state.videoEl = $('video, iframe');
|
|
});
|
|
|
|
it('instanticate current time to zero', function() {
|
|
expect(state.videoPlayer.currentTime).toEqual(0);
|
|
});
|
|
|
|
it('set the element', function() {
|
|
expect(state.el).toHaveId('video_id');
|
|
});
|
|
|
|
it('create video control', function() {
|
|
expect(state.videoControl).toBeDefined();
|
|
expect(state.videoControl.el).toHaveClass('video-controls');
|
|
});
|
|
|
|
it('create video caption', function() {
|
|
expect(state.videoCaption).toBeDefined();
|
|
expect(state.speed).toEqual(1.5);
|
|
expect(state.config.transcriptTranslationUrl)
|
|
.toEqual('/transcript/translation/__lang__');
|
|
});
|
|
|
|
it('create video speed control', function() {
|
|
expect(state.videoSpeedControl).toBeDefined();
|
|
expect(state.videoSpeedControl.el).toHaveClass('speeds');
|
|
expect(state.speed).toEqual(1.5);
|
|
});
|
|
|
|
it('create video progress slider', function() {
|
|
expect(state.videoProgressSlider).toBeDefined();
|
|
expect(state.videoProgressSlider.el).toHaveClass('slider');
|
|
});
|
|
|
|
// All the toHandleWith() expect tests are not necessary for
|
|
// this version of Video. jQuery event system is not used to
|
|
// trigger and invoke methods. This is an artifact from
|
|
// previous version of Video.
|
|
});
|
|
|
|
it('create Youtube player', function() {
|
|
var events;
|
|
|
|
jasmine.stubRequests();
|
|
spyOn(window.YT, 'Player').and.callThrough();
|
|
state = jasmine.initializePlayerYouTube();
|
|
state.videoEl = $('video, iframe');
|
|
|
|
events = {
|
|
onReady: state.videoPlayer.onReady,
|
|
onStateChange: state.videoPlayer.onStateChange,
|
|
onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange,
|
|
onError: state.videoPlayer.onError
|
|
};
|
|
|
|
expect(YT.Player).toHaveBeenCalledWith('id', {
|
|
playerVars: {
|
|
controls: 0,
|
|
wmode: 'transparent',
|
|
rel: 0,
|
|
showinfo: 0,
|
|
enablejsapi: 1,
|
|
modestbranding: 1,
|
|
html5: 1,
|
|
cc_load_policy: 0
|
|
},
|
|
videoId: 'cogebirgzzM',
|
|
events: events
|
|
});
|
|
});
|
|
|
|
it('create Flash player', function() {
|
|
var player;
|
|
|
|
spyOn($.fn, 'trigger');
|
|
state = jasmine.initializePlayerYouTube();
|
|
state.videoEl = state.el.find('video, iframe').width(100);
|
|
player = state.videoPlayer.player;
|
|
player.getAvailablePlaybackRates.and.returnValue([1]);
|
|
state.currentPlayerMode = 'html5';
|
|
spyOn(window.YT, 'Player').and.callThrough();
|
|
state.videoPlayer.onReady();
|
|
|
|
expect(YT.Player).toHaveBeenCalledWith('id', {
|
|
playerVars: {
|
|
controls: 0,
|
|
wmode: 'transparent',
|
|
rel: 0,
|
|
showinfo: 0,
|
|
enablejsapi: 1,
|
|
modestbranding: 1,
|
|
cc_load_policy: 0
|
|
},
|
|
videoId: 'abcdefghijkl',
|
|
events: jasmine.any(Object)
|
|
});
|
|
|
|
expect(state.resizer.setElement).toHaveBeenCalled();
|
|
expect(state.resizer.align).toHaveBeenCalled();
|
|
});
|
|
|
|
// We can't test the invocation of HTML5Video because it is not
|
|
// available globally. It is defined within the scope of Require
|
|
// JS.
|
|
|
|
describe('when on a touch based device', function() {
|
|
$.each(['iPad', 'Android'], function(index, device) {
|
|
it('create video volume control on' + device, function() {
|
|
window.onTouchBasedDevice.and.returnValue([device]);
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
expect(state.el.find('.volume')).not.toExist();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when not on a touch based device', function() {
|
|
// eslint-disable-next-line no-shadow
|
|
var oldOTBD;
|
|
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
});
|
|
|
|
it('controls are in paused state', function() {
|
|
expect(state.videoPlayer.isPlaying()).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onReady', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
spyOn(state.videoPlayer, 'play').and.callThrough();
|
|
state.videoPlayer.onReady();
|
|
});
|
|
|
|
it('autoplay the first video', function() {
|
|
expect(state.videoPlayer.play).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('invalid endTime is reset to null', function() {
|
|
expect(state.videoPlayer.endTime).toBe(null);
|
|
});
|
|
});
|
|
|
|
describe('onReady YouTube', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayerYouTube();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
});
|
|
|
|
it('multiple speeds and flash mode, change back to html5 mode', function() {
|
|
var playbackRates = state.videoPlayer.player.getAvailablePlaybackRates();
|
|
|
|
state.currentPlayerMode = 'flash';
|
|
state.videoPlayer.onReady();
|
|
expect(playbackRates.length).toBe(4);
|
|
expect(state.currentPlayerMode).toBe('html5');
|
|
});
|
|
});
|
|
|
|
describe('onStateChange Youtube', function() {
|
|
describe('when the video is ended', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayerYouTube();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.ENDED
|
|
});
|
|
});
|
|
|
|
it('pause the video control', function() {
|
|
expect($('.video_control')).toHaveClass('play');
|
|
});
|
|
|
|
it('trigger pause and ended events', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('pause', emptyArguments);
|
|
expect($.fn.trigger).toHaveBeenCalledWith('ended', emptyArguments);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onStateChange', function() {
|
|
describe('when the video is unstarted', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
state.videoEl = $('video, iframe');
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.PAUSED
|
|
});
|
|
});
|
|
|
|
it('pause the video control', function() {
|
|
expect($('.video_control')).toHaveClass('play');
|
|
});
|
|
|
|
it('pause the video caption', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('pause', emptyArguments);
|
|
});
|
|
});
|
|
|
|
describe('when the video is playing', function() {
|
|
var oldState;
|
|
|
|
beforeEach(function() {
|
|
// Create the first instance of the player.
|
|
state = jasmine.initializePlayer();
|
|
oldState = state;
|
|
|
|
spyOn(oldState.videoPlayer, 'onPause').and.callThrough();
|
|
|
|
// Now initialize a second instance.
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(window, 'setInterval').and.returnValue(100);
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.PLAYING
|
|
});
|
|
});
|
|
|
|
it('set update interval', function() {
|
|
expect(window.setInterval).toHaveBeenCalledWith(
|
|
state.videoPlayer.update, 200
|
|
);
|
|
expect(state.videoPlayer.updateInterval).toEqual(100);
|
|
});
|
|
|
|
it('play the video control', function() {
|
|
expect($('.video_control')).toHaveClass('pause');
|
|
});
|
|
|
|
it('play the video caption', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('play', emptyArguments);
|
|
});
|
|
});
|
|
|
|
describe('when the video is paused', function() {
|
|
var currentUpdateIntrval;
|
|
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.PLAYING
|
|
});
|
|
|
|
currentUpdateIntrval = state.videoPlayer.updateInterval;
|
|
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.PAUSED
|
|
});
|
|
});
|
|
|
|
it('clear update interval', function() {
|
|
expect(state.videoPlayer.updateInterval).toBeUndefined();
|
|
});
|
|
|
|
it('pause the video control', function() {
|
|
expect($('.video_control')).toHaveClass('play');
|
|
});
|
|
|
|
it('pause the video caption', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('pause', emptyArguments);
|
|
});
|
|
});
|
|
|
|
describe('when the video is ended', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.ENDED
|
|
});
|
|
});
|
|
|
|
it('pause the video control', function() {
|
|
expect($('.video_control')).toHaveClass('play');
|
|
});
|
|
|
|
it('pause the video caption', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('ended', emptyArguments);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onSeek Youtube', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayerYouTube();
|
|
state.videoEl = $('video, iframe');
|
|
});
|
|
|
|
describe('when the video is playing', function() {
|
|
beforeEach(function() {
|
|
state.videoPlayer.onStateChange({
|
|
data: YT.PlayerState.PLAYING
|
|
});
|
|
});
|
|
|
|
it('Video has started playing', function() {
|
|
expect($('.video_control')).toHaveClass('pause');
|
|
});
|
|
|
|
it('seek the player', function() {
|
|
state.videoPlayer.seekTo(10);
|
|
expect(state.videoPlayer.currentTime).toBe(10);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onSeek', function() {
|
|
beforeEach(function() {
|
|
// jasmine.Clock can't be used to fake out debounce with newer versions of underscore
|
|
spyOn(_, 'debounce').and.callFake(function(func) {
|
|
return function() {
|
|
func.apply(this, arguments);
|
|
};
|
|
});
|
|
state = jasmine.initializePlayer();
|
|
state.videoEl = $('video, iframe');
|
|
spyOn(state.videoPlayer, 'duration').and.returnValue(120);
|
|
});
|
|
|
|
describe('when the video is playing', function() {
|
|
it('call runTimer in seekTo on player', function(done) {
|
|
state.videoPlayer.play();
|
|
jasmine.waitUntil(function() {
|
|
return state.videoPlayer.isPlaying();
|
|
}).then(function() {
|
|
spyOn(state.videoPlayer, 'stopTimer').and.callThrough();
|
|
spyOn(state.videoPlayer, 'runTimer').and.callThrough();
|
|
state.videoPlayer.seekTo(10);
|
|
expect(state.videoPlayer.currentTime).toBe(10);
|
|
expect(state.videoPlayer.stopTimer).toHaveBeenCalled();
|
|
expect(state.videoPlayer.runTimer).toHaveBeenCalled();
|
|
}).always(done);
|
|
});
|
|
|
|
it('seek the player', function() {
|
|
spyOn(state.videoPlayer.player, 'seekTo').and.callThrough();
|
|
state.videoProgressSlider.onSlide(
|
|
jQuery.Event('slide'), {value: 30}
|
|
);
|
|
expect(state.videoPlayer.currentTime).toBe(30);
|
|
expect(state.videoPlayer.player.seekTo).toHaveBeenCalledWith(30, true);
|
|
});
|
|
|
|
it('call updatePlayTime on player', function() {
|
|
spyOn(state.videoPlayer, 'updatePlayTime').and.callThrough();
|
|
state.videoProgressSlider.onSlide(
|
|
jQuery.Event('slide'), {value: 30}
|
|
);
|
|
expect(state.videoPlayer.currentTime).toBe(30);
|
|
expect(state.videoPlayer.updatePlayTime).toHaveBeenCalledWith(30, true);
|
|
});
|
|
});
|
|
|
|
it('when the player is not playing: set the current time', function() {
|
|
state.videoProgressSlider.onSlide(
|
|
jQuery.Event('slide'), {value: 20}
|
|
);
|
|
state.videoPlayer.pause();
|
|
expect(state.videoPlayer.currentTime).toBe(20);
|
|
state.videoProgressSlider.onSlide(
|
|
jQuery.Event('slide'), {value: 10}
|
|
);
|
|
expect(state.videoPlayer.currentTime).toBe(10);
|
|
});
|
|
|
|
describe('when the video is not playing', function() {
|
|
beforeEach(function() {
|
|
spyOn(state.videoPlayer, 'setPlaybackRate')
|
|
.and.callThrough();
|
|
});
|
|
|
|
it('video has a correct speed', function() {
|
|
state.speed = '2.0';
|
|
state.videoPlayer.onPlay();
|
|
expect(state.videoPlayer.setPlaybackRate)
|
|
.toHaveBeenCalledWith('2.0');
|
|
state.videoPlayer.onPlay();
|
|
expect(state.videoPlayer.setPlaybackRate.calls.count())
|
|
.toEqual(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onVolumeChange', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
state.videoPlayer.onReady();
|
|
state.videoEl = $('video, iframe');
|
|
});
|
|
|
|
it('set the volume on player', function() {
|
|
spyOn(state.videoPlayer.player, 'setVolume');
|
|
state.videoPlayer.onVolumeChange(60);
|
|
expect(state.videoPlayer.player.setVolume)
|
|
.toHaveBeenCalledWith(60);
|
|
});
|
|
|
|
describe('when the video is not playing', function() {
|
|
beforeEach(function() {
|
|
state.videoPlayer.player.setVolume('1');
|
|
});
|
|
|
|
it('video has a correct volume', function() {
|
|
spyOn(state.videoPlayer.player, 'setVolume');
|
|
state.videoVolumeControl.volume = 26;
|
|
state.el.trigger('play');
|
|
expect(state.videoPlayer.player.setVolume)
|
|
.toHaveBeenCalledWith(26);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('update', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state.videoPlayer, 'updatePlayTime').and.callThrough();
|
|
});
|
|
|
|
describe(
|
|
'when the current time is unavailable from the player',
|
|
function() {
|
|
beforeEach(function() {
|
|
state.videoPlayer.player.getCurrentTime = function() {
|
|
return NaN;
|
|
};
|
|
state.videoPlayer.update();
|
|
});
|
|
|
|
it('does not trigger updatePlayTime event', function() {
|
|
expect(state.videoPlayer.updatePlayTime)
|
|
.not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe(
|
|
'when the current time is available from the player',
|
|
function() {
|
|
beforeEach(function() {
|
|
state.videoPlayer.player.getCurrentTime = function() {
|
|
return 60;
|
|
};
|
|
state.videoPlayer.update();
|
|
});
|
|
|
|
it('trigger updatePlayTime event', function() {
|
|
expect(state.videoPlayer.updatePlayTime)
|
|
.toHaveBeenCalledWith(60);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Disabled 1/13/14 due to flakiness observed in master
|
|
xdescribe('update with start & end time', function() {
|
|
var START_TIME = 1,
|
|
END_TIME = 2;
|
|
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer(
|
|
{
|
|
start: START_TIME,
|
|
end: END_TIME
|
|
}
|
|
);
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state.videoPlayer, 'update').and.callThrough();
|
|
spyOn(state.videoPlayer, 'pause').and.callThrough();
|
|
spyOn(state.videoProgressSlider, 'notifyThroughHandleEnd')
|
|
.and.callThrough();
|
|
});
|
|
|
|
it(
|
|
'video is paused on first endTime, start & end time are reset',
|
|
function(done) {
|
|
var duration;
|
|
|
|
state.videoProgressSlider.notifyThroughHandleEnd.calls.reset();
|
|
state.videoPlayer.pause.calls.reset();
|
|
state.videoPlayer.play();
|
|
|
|
jasmine.waitUntil(function() {
|
|
duration = Math.round(state.videoPlayer.currentTime);
|
|
return state.videoPlayer.pause.calls.count() === 1;
|
|
}).then(function() {
|
|
expect(state.videoPlayer.startTime).toBe(0);
|
|
expect(state.videoPlayer.endTime).toBe(null);
|
|
|
|
expect(duration).toBe(END_TIME);
|
|
|
|
expect(state.videoProgressSlider.notifyThroughHandleEnd)
|
|
.toHaveBeenCalledWith({end: true});
|
|
}).always(done);
|
|
});
|
|
});
|
|
|
|
describe('updatePlayTime', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayerYouTube();
|
|
state.videoEl = $('video, iframe');
|
|
spyOn(state.videoCaption, 'updatePlayTime').and.callThrough();
|
|
spyOn(state.videoProgressSlider, 'updatePlayTime').and.callThrough();
|
|
});
|
|
|
|
it('update the video playback time', function(done) {
|
|
var duration = 0;
|
|
|
|
jasmine.waitUntil(function() {
|
|
duration = state.videoPlayer.duration();
|
|
|
|
if (duration > 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}).then(function() {
|
|
state.videoPlayer.goToStartTime = false;
|
|
state.videoPlayer.updatePlayTime(60);
|
|
|
|
expect($('.vidtime')).toHaveHtml('1:00 / 1:00');
|
|
}).always(done);
|
|
});
|
|
|
|
it('update the playback time on caption', function(done) {
|
|
jasmine.waitUntil(function() {
|
|
return state.videoPlayer.duration() > 0;
|
|
}, 1000).then(function() {
|
|
state.videoPlayer.goToStartTime = false;
|
|
state.videoPlayer.updatePlayTime(60);
|
|
|
|
expect(state.videoCaption.updatePlayTime)
|
|
.toHaveBeenCalledWith(60);
|
|
}).always(done);
|
|
});
|
|
|
|
it('update the playback time on progress slider', function(done) {
|
|
var duration = 0;
|
|
|
|
jasmine.waitUntil(function() {
|
|
duration = state.videoPlayer.duration();
|
|
|
|
return duration > 0;
|
|
}, 1000).then(function() {
|
|
state.videoPlayer.goToStartTime = false;
|
|
state.videoPlayer.updatePlayTime(60);
|
|
|
|
expect(state.videoProgressSlider.updatePlayTime)
|
|
.toHaveBeenCalledWith({
|
|
time: 60,
|
|
duration: duration
|
|
});
|
|
}).always(done);
|
|
});
|
|
});
|
|
|
|
// Disabled 1/13/14 due to flakiness observed in master
|
|
xdescribe(
|
|
'updatePlayTime when start & end times are defined',
|
|
function() {
|
|
var START_TIME = 1,
|
|
END_TIME = 2;
|
|
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer(
|
|
{
|
|
start: START_TIME,
|
|
end: END_TIME
|
|
}
|
|
);
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state.videoPlayer, 'updatePlayTime').and.callThrough();
|
|
spyOn(state.videoPlayer.player, 'seekTo').and.callThrough();
|
|
spyOn(state.videoProgressSlider, 'updateStartEndTimeRegion')
|
|
.and.callThrough();
|
|
});
|
|
|
|
it(
|
|
'when duration becomes available, updatePlayTime() is called',
|
|
function(done) {
|
|
var duration;
|
|
|
|
expect(state.videoPlayer.initialSeekToStartTime).toBeTruthy();
|
|
expect(state.videoPlayer.seekToStartTimeOldSpeed).toBe('void');
|
|
|
|
state.videoPlayer.play();
|
|
|
|
jasmine.waitUntil(function() {
|
|
duration = state.videoPlayer.duration();
|
|
|
|
return state.videoPlayer.isPlaying()
|
|
&& state.videoPlayer.initialSeekToStartTime === false;
|
|
}).then(function() {
|
|
expect(state.videoPlayer.startTime).toBe(START_TIME);
|
|
expect(state.videoPlayer.endTime).toBe(END_TIME);
|
|
|
|
expect(state.videoPlayer.player.seekTo)
|
|
.toHaveBeenCalledWith(START_TIME);
|
|
|
|
expect(state.videoProgressSlider.updateStartEndTimeRegion)
|
|
.toHaveBeenCalledWith({duration: duration});
|
|
|
|
expect(state.videoPlayer.seekToStartTimeOldSpeed)
|
|
.toBe(state.speed);
|
|
}).always(done);
|
|
});
|
|
});
|
|
|
|
describe('updatePlayTime with invalid endTime', function() {
|
|
beforeEach(function() {
|
|
state = {
|
|
el: $('#video_id'),
|
|
videoPlayer: {
|
|
duration: function() {
|
|
// The video will be 60 seconds long.
|
|
return 60;
|
|
},
|
|
goToStartTime: true,
|
|
startTime: undefined,
|
|
endTime: undefined,
|
|
player: {
|
|
seekTo: function() {}
|
|
},
|
|
figureOutStartEndTime: jasmine.createSpy(),
|
|
figureOutStartingTime: jasmine.createSpy().and.returnValue(0)
|
|
},
|
|
config: {
|
|
savedVideoPosition: 0,
|
|
startTime: 0,
|
|
|
|
// We are setting the end-time to 10000 seconds. The
|
|
// video will be less than this, the code will reset
|
|
// the end time to `null` - i.e. to the end of the video.
|
|
// This is the expected behavior we will test for.
|
|
endTime: 10000
|
|
},
|
|
currentPlayerMode: 'html5',
|
|
trigger: function() {},
|
|
browserIsFirefox: false,
|
|
isFlashMode: jasmine.createSpy().and.returnValue(false)
|
|
};
|
|
});
|
|
});
|
|
|
|
describe('toggleFullScreen', function() {
|
|
describe('when the video player is not full screen', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
jasmine.mockFullscreenAPI();
|
|
state.videoEl = $('video, iframe');
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
$('.add-fullscreen').click();
|
|
});
|
|
|
|
it('add the video-fullscreen class', function() {
|
|
expect(state.el).toHaveClass('video-fullscreen');
|
|
});
|
|
|
|
it('tell VideoCaption to resize', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('fullscreen', [true]);
|
|
expect(state.resizer.setMode).toHaveBeenCalledWith('both');
|
|
expect(state.resizer.delta.substract).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when the video player already full screen', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
jasmine.mockFullscreenAPI();
|
|
state.videoEl = $('video, iframe');
|
|
spyOn($.fn, 'trigger').and.callThrough();
|
|
state.videoFullScreen.enter();
|
|
$('.add-fullscreen').click();
|
|
});
|
|
|
|
it('remove the video-fullscreen class', function() {
|
|
expect(state.el).not.toHaveClass('video-fullscreen');
|
|
});
|
|
|
|
it('tell VideoCaption to resize', function() {
|
|
expect($.fn.trigger).toHaveBeenCalledWith('fullscreen', [false]);
|
|
expect(state.resizer.setMode)
|
|
.toHaveBeenCalledWith('width');
|
|
expect(state.resizer.delta.reset).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('duration', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state.videoPlayer.player, 'getDuration').and.callThrough();
|
|
state.videoPlayer.duration();
|
|
});
|
|
|
|
it('delegate to the player', function() {
|
|
expect(state.videoPlayer.player.getDuration).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('getDuration', function() {
|
|
beforeEach(function() {
|
|
// We need to make sure that metadata is returned via an AJAX
|
|
// request. Without the jasmine.stubRequests() below we will
|
|
// get the error:
|
|
//
|
|
// this.metadata[this.youtubeId(...)] is undefined
|
|
jasmine.stubRequests();
|
|
|
|
state = jasmine.initializePlayerYouTube();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state, 'getDuration').and.callThrough();
|
|
|
|
// When `state.videoPlayer.player.getDuration()` returns a `0`,
|
|
// the fall-back function `state.getDuration()` will be called.
|
|
state.videoPlayer.player.getDuration.and.returnValue(0);
|
|
});
|
|
|
|
it('getDuration is called as a fall-back', function() {
|
|
state.videoPlayer.duration();
|
|
|
|
expect(state.getDuration).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('volume', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
spyOn(state.videoPlayer.player, 'getVolume').and.callThrough();
|
|
});
|
|
|
|
it('set the player volume', function() {
|
|
var expectedValue = 60,
|
|
realValue;
|
|
|
|
state.videoPlayer.player.setVolume(60);
|
|
realValue = Math.round(state.videoPlayer.player.getVolume() * 100);
|
|
|
|
expect(realValue).toEqual(expectedValue);
|
|
});
|
|
});
|
|
|
|
describe('on Touch devices', function() {
|
|
it('`is-touch` class name is added to container', function() {
|
|
$.each(
|
|
['iPad', 'Android', 'iPhone'],
|
|
function(index, device) {
|
|
window.onTouchBasedDevice.and.returnValue([device]);
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
expect(state.el).toHaveClass('is-touch');
|
|
});
|
|
});
|
|
|
|
it('modules are not initialized on iPhone', function() {
|
|
window.onTouchBasedDevice.and.returnValue(['iPhone']);
|
|
state = jasmine.initializePlayer();
|
|
|
|
state.videoEl = $('video, iframe');
|
|
|
|
var modules = [
|
|
state.videoControl, state.videoCaption, state.videoProgressSlider,
|
|
state.videoSpeedControl, state.videoVolumeControl
|
|
];
|
|
|
|
$.each(modules, function(index, module) {
|
|
expect(module).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
$.each(['iPad', 'Android'], function(index, device) {
|
|
var message = 'controls become visible after playing starts '
|
|
+ 'on ' + device;
|
|
|
|
it(message, function(done) {
|
|
var controls;
|
|
|
|
window.onTouchBasedDevice.and.returnValue([device]);
|
|
|
|
state = jasmine.initializePlayer();
|
|
state.videoEl = $('video, iframe');
|
|
controls = state.el.find('.video-controls');
|
|
|
|
jasmine.waitUntil(function() {
|
|
return state.el.hasClass('is-initialized');
|
|
}).then(function() {
|
|
expect(controls).toHaveClass('is-hidden');
|
|
state.videoPlayer.play();
|
|
jasmine.waitUntil(function() {
|
|
// Firefox does not return duration for videos until they have reached the end.
|
|
// var duration = state.videoPlayer.duration();
|
|
// return duration > 0 && state.videoPlayer.isPlaying();
|
|
return state.videoPlayer.isPlaying();
|
|
}).then(function() {
|
|
expect(controls).not.toHaveClass('is-hidden');
|
|
}).always(done);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onSpeedChange', function() {
|
|
beforeEach(function() {
|
|
state = {
|
|
el: $(document),
|
|
speed: '1.50',
|
|
setSpeed: jasmine.createSpy(),
|
|
saveState: jasmine.createSpy(),
|
|
videoPlayer: {
|
|
currentTime: 60,
|
|
updatePlayTime: jasmine.createSpy(),
|
|
setPlaybackRate: jasmine.createSpy(),
|
|
player: jasmine.createSpyObj('player', ['setPlaybackRate'])
|
|
},
|
|
isFlashMode: jasmine.createSpy().and.returnValue(false)
|
|
};
|
|
});
|
|
|
|
describe('always', function() {
|
|
it('convert the current time to the new speed', function() {
|
|
state.isFlashMode.and.returnValue(true);
|
|
VideoPlayer.prototype.onSpeedChange.call(state, '0.75', false);
|
|
expect(state.videoPlayer.currentTime).toBe('120.000');
|
|
});
|
|
|
|
it('set video speed to the new speed', function() {
|
|
VideoPlayer.prototype.onSpeedChange.call(state, '0.75', false);
|
|
expect(state.setSpeed).toHaveBeenCalledWith(0.75);
|
|
expect(state.videoPlayer.setPlaybackRate)
|
|
.toHaveBeenCalledWith(0.75);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('setPlaybackRate', function() {
|
|
beforeEach(function() {
|
|
state = {
|
|
youtubeId: jasmine.createSpy().and.returnValue('videoId'),
|
|
isFlashMode: jasmine.createSpy().and.returnValue(false),
|
|
isHtml5Mode: jasmine.createSpy().and.returnValue(true),
|
|
isYoutubeType: jasmine.createSpy().and.returnValue(true),
|
|
setPlayerMode: jasmine.createSpy(),
|
|
trigger: jasmine.createSpy(),
|
|
videoPlayer: {
|
|
currentTime: 60,
|
|
isPlaying: jasmine.createSpy(),
|
|
seekTo: jasmine.createSpy(),
|
|
duration: jasmine.createSpy().and.returnValue(60),
|
|
updatePlayTime: jasmine.createSpy(),
|
|
setPlaybackRate: jasmine.createSpy(),
|
|
player: jasmine.createSpyObj('player', [
|
|
'setPlaybackRate', 'loadVideoById', 'cueVideoById'
|
|
])
|
|
}
|
|
};
|
|
});
|
|
|
|
it('in Flash mode and video is playing', function() {
|
|
state.isFlashMode.and.returnValue(true);
|
|
state.isHtml5Mode.and.returnValue(false);
|
|
state.videoPlayer.isPlaying.and.returnValue(true);
|
|
VideoPlayer.prototype.setPlaybackRate.call(state, '0.75');
|
|
expect(state.videoPlayer.player.setPlaybackRate)
|
|
.toHaveBeenCalledWith('0.75');
|
|
});
|
|
|
|
it('in Flash mode and video not started', function() {
|
|
state.isFlashMode.and.returnValue(true);
|
|
state.isHtml5Mode.and.returnValue(false);
|
|
state.videoPlayer.isPlaying.and.returnValue(false);
|
|
VideoPlayer.prototype.setPlaybackRate.call(state, '0.75');
|
|
expect(state.videoPlayer.player.setPlaybackRate).toHaveBeenCalledWith('0.75');
|
|
});
|
|
|
|
it('in HTML5 mode', function() {
|
|
state.isYoutubeType.and.returnValue(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.browserIsFirefox = true;
|
|
|
|
state.videoPlayer.isPlaying.and.returnValue(false);
|
|
VideoPlayer.prototype.setPlaybackRate.call(state, '1.0');
|
|
expect(state.videoPlayer.player.setPlaybackRate).toHaveBeenCalledWith('1.0');
|
|
});
|
|
});
|
|
|
|
describe('HLS Video', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializeHLSPlayer();
|
|
});
|
|
|
|
it('does not show error message if hls is supported', function() {
|
|
expect($('.video-hls-error')).toHaveClass('is-hidden');
|
|
});
|
|
|
|
it('can extract hls video sources correctly', function() {
|
|
expect(state.HLSVideoSources).toEqual(['/base/fixtures/hls/hls.m3u8']);
|
|
expect(state.videoPlayer.player.hls).toBeDefined();
|
|
});
|
|
|
|
describe('on safari', function() {
|
|
beforeEach(function() {
|
|
spyOn(HLS, 'isSupported').and.returnValue(false);
|
|
state = jasmine.initializeHLSPlayer();
|
|
state.canPlayHLS = true;
|
|
state.browserIsSafari = true;
|
|
});
|
|
|
|
it('can use native hls playback support', function() {
|
|
expect(state.videoPlayer.player.hls).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('HLS Video Errors', function() {
|
|
beforeEach(function() {
|
|
spyOn(HLS, 'isSupported').and.returnValue(false);
|
|
state = jasmine.initializeHLSPlayer({sources: ['/base/fixtures/hls/hls.m3u8']});
|
|
});
|
|
|
|
it('shows error message if hls is not supported', function() {
|
|
expect($('.video-hls-error')).not.toHaveClass('is-hidden');
|
|
expect($('.video-hls-error').text().trim()).toEqual(
|
|
'Your browser does not support this video format. Try using a different browser.'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Video duration', function() {
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
spyOn(state.videoPlayer, 'duration').and.returnValue(61);
|
|
});
|
|
|
|
it('overrides the duration if not set', function(done) {
|
|
jasmine.waitUntil(function() {
|
|
return state.duration !== undefined;
|
|
}).then(function() {
|
|
expect(state.duration).toEqual(61);
|
|
}).always(done);
|
|
});
|
|
});
|
|
|
|
describe('Overlay Play Button', function() {
|
|
var playButtonOverlaySelector = '.video-wrapper .btn-play.fa.fa-youtube-play.fa-2x';
|
|
beforeEach(function() {
|
|
state = jasmine.initializePlayer();
|
|
});
|
|
|
|
it('shows the play button after player is ready', function(done) {
|
|
jasmine.waitUntil(function() {
|
|
return state.videoPlayer.player.getPlayerState() !== STATUS.UNSTARTED;
|
|
}).then(function() {
|
|
expect($(playButtonOverlaySelector)).not.toHaveClass('is-hidden');
|
|
}).always(done);
|
|
});
|
|
|
|
it('hides the play button on play', function(done) {
|
|
$(state.videoPlayer.player.videoEl).trigger('click'); // play
|
|
jasmine.waitUntil(function() {
|
|
return state.videoPlayer.player.getPlayerState() === STATUS.PLAYING;
|
|
}).then(function() {
|
|
expect($(playButtonOverlaySelector)).toHaveClass('is-hidden');
|
|
}).always(done);
|
|
});
|
|
|
|
it('plays the video when overlay button is clicked', function() {
|
|
$('.video-wrapper .btn-play').trigger('click'); // play
|
|
expect(state.videoPlayer.player.getPlayerState()).toEqual(STATUS.PLAYING);
|
|
expect($(playButtonOverlaySelector)).toHaveClass('is-hidden');
|
|
});
|
|
|
|
it('shows the play button on pause', function(done) {
|
|
$(state.videoPlayer.player.videoEl).trigger('click'); // play
|
|
expect(state.videoPlayer.player.getPlayerState()).toEqual(STATUS.PLAYING);
|
|
$(state.videoPlayer.player.videoEl).trigger('click'); // pause
|
|
expect(state.videoPlayer.player.getPlayerState()).toEqual(STATUS.PAUSED);
|
|
jasmine.waitUntil(function() {
|
|
return $(playButtonOverlaySelector).attr('class').split(' ')
|
|
.indexOf('is-hidden') === -1;
|
|
}).then(function() {
|
|
expect($(playButtonOverlaySelector)).not.toHaveClass('is-hidden');
|
|
}).always(done);
|
|
});
|
|
});
|
|
|
|
describe('HLS Primary Playback', function() {
|
|
beforeEach(function() {
|
|
spyOn(window.YT, 'Player').and.callThrough();
|
|
});
|
|
|
|
afterEach(function() {
|
|
YT.Player.calls.reset();
|
|
});
|
|
|
|
it('loads youtube if flag is disabled', function() {
|
|
state = jasmine.initializePlayer('video_all.html', {
|
|
prioritizeHls: false,
|
|
streams: '0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl'
|
|
});
|
|
expect(state.config.prioritizeHls).toBeFalsy();
|
|
expect(YT.Player).toHaveBeenCalled();
|
|
expect(state.videoPlayer.player.hls).toBeUndefined();
|
|
});
|
|
|
|
it('does not load youtube if flag is enabled', function() {
|
|
state = jasmine.initializePlayer('video_all.html', {
|
|
prioritizeHls: true,
|
|
streams: '0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl',
|
|
sources: ['/base/fixtures/test.mp4', '/base/fixtures/test.webm', '/base/fixtures/hls/hls.m3u8']
|
|
});
|
|
expect(state.config.prioritizeHls).toBeTruthy();
|
|
expect(YT.Player).not.toHaveBeenCalled();
|
|
expect(state.videoPlayer.player.hls).toBeDefined();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}(require, define));
|