VulpesOS/gaia/apps/video/test/unit/video_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

2301 lines
76 KiB
JavaScript
Executable file

/* global clearTimeout:true,currentVideo:true,storageState:true,
thumbnailList:true,LazyLoader:true,pendingPick:true,isPhone:true,
firstScanEnded:true,metadataQueue:true,MediaDB:true,isPortrait:true,
currentLayoutMode:true,playerShowing:true,processingQueue:true,
pendingUpdateTitleText:true,controlShowing:true,touchStartID:true,
selectedFileNames:true,selectedFileNamesToBlobs:true,dragging:true,
sliderRect:true,updateVideoControlSlider:true,setControlsVisibility:true,
controlFadeTimeout:true,handleSliderTouchMove:true,seeker:true */
/* global handleScreenLayoutChange,HAVE_NOTHING,handleSliderKeypress,
hideOptionsView,MockLazyLoader,MockVideoPlayer,ThumbnailList,
toggleVideoControls,showOptionsView,ScreenLayout,MocksHelper,dom,
MockThumbnailGroup,l10nAssert,showInfoView,hideInfoView,
updateSelection,setButtonPaused,handleSliderTouchStart,MockMediaDB,
handleSliderTouchEnd,loadingChecker,MockL10n,VideoUtils,
updateDialog,LAYOUT_MODE,showPlayer,hidePlayer,MockThumbnailItem,
MockThumbnailGroup,MockForwardRewindController,MockIntlHelper,captions */
'use strict';
require('/shared/js/lazy_loader.js');
require('/shared/js/media/media_utils.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_screen_layout.js');
require('/shared/test/unit/mocks/mock_video_stats.js');
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/mocks//mock_intl_helper.js');
require('/shared/test/unit/mocks//mock_manifest_helper.js');
requireApp('/video/js/video_utils.js');
requireApp('/video/test/unit/mock_metadata.js');
requireApp('/video/test/unit/mock_mediadb.js');
requireApp('/video/test/unit/mock_forward_rewind_controller.js');
requireApp('/video/test/unit/mock_thumbnail_group.js');
requireApp('/video/test/unit/mock_thumbnail_item.js');
requireApp('/video/test/unit/mock_video_loading_checker.js');
requireApp('/video/test/unit/mock_video_player.js');
requireApp('/video/js/thumbnail_list.js');
requireApp('/video/js/seeker.js');
// Declare variables used in video.js that are declared in other
// javascript files (that we don't want to pull in).
var videodb; // Declared in db.js
var startParsingMetadata; // Declared in metadata.js
var ThumbnailItem;
var ThumbnailDateGroup;
var ForwardRewindController;
var IntlHelper;
var initDB;
var init;
function containsClass(element, value) {
return element.classList.contains(value);
}
var mocksForVideo = new MocksHelper([
'ScreenLayout'
]);
function getAsset(filename, loadCallback) {
var req = new XMLHttpRequest();
req.open('GET', filename, true);
req.responseType = 'blob';
req.onload = function() {
loadCallback(req.response);
};
req.send();
}
function testOverlayVisibility(expected) {
assert.equal(document.body.classList.contains('overlay'), expected);
assert.equal(dom.overlay.classList.contains('hidden'), !expected);
}
function MockCaptions(player) {
this.player = player;
this.remove = sinon.stub();
this.findAndDisplay = sinon.stub();
}
suite('Video App Unit Tests', function() {
var nativeMozL10n;
var videoName = 'video name';
suiteSetup(function(done) {
mocksForVideo.suiteSetup();
// Create DOM structure
loadBodyHTML('/index.html');
nativeMozL10n = document.l10n;
document.l10n = MockL10n;
IntlHelper = MockIntlHelper;
ThumbnailItem = MockThumbnailItem;
ThumbnailDateGroup = MockThumbnailGroup;
init = sinon.stub();
initDB = sinon.stub();
ForwardRewindController = MockForwardRewindController;
window.Captions = MockCaptions;
requireApp('/video/js/video.js', done);
});
suiteTeardown(function() {
document.l10n = nativeMozL10n;
});
suite('#Video Info Populate Data', function() {
suiteSetup(function() {
currentVideo = {
metadata: {
title: 'Small webm',
duration: 5.568,
width: 560,
height: 320
},
type: 'video/webm',
date: 1375873140000,
size: 229455
};
});
test('#Test Video details', function(done) {
showInfoView().then(() => {
assert.equal(document.getElementById('info-name').textContent,
'Small webm');
assert.equal(document.getElementById('info-length').textContent,
'00:06');
assert.equal(document.getElementById('info-type').textContent,
'webm');
assert.equal(document.getElementById('info-date').textContent,
'8/7/2013');
assert.equal(document.getElementById('info-resolution').textContent,
'560x320');
}).then(done, done);
});
test('#Test show info view', function() {
assert.isFalse(dom.infoView.classList[0] === 'hidden');
assert.isTrue(document.body.classList.contains('info-view'));
});
test('#Test hide info view', function() {
hideInfoView();
assert.isTrue(dom.infoView.classList[0] === 'hidden');
assert.isFalse(document.body.classList.contains('info-view'));
});
});
suite('#Video Action Menu Test', function() {
test('#Test show option view', function() {
showOptionsView();
assert.isFalse(dom.optionsView.classList[0] === 'hidden');
assert.isTrue(document.body.classList.contains('options-view'));
});
test('#Test hide option view', function() {
hideOptionsView();
assert.isTrue(dom.optionsView.hasAttribute('hidden'));
assert.isFalse(document.body.classList.contains('options-view'));
});
});
suite('#Video Format Date', function() {
test('#Test Video created date', function(done) {
currentVideo = {
metadata: {
title: 'My test video'
},
date: 1376909940000
};
showInfoView().then(() => {
assert.equal(document.getElementById('info-date').textContent,
'8/19/2013');
}).then(done, done);
});
test('#Test Video date null', function(done) {
currentVideo = {
metadata: {
title: 'Lorem Ipsum'
},
date: null
};
showInfoView().then(() => {
assert.equal(document.getElementById('info-date').textContent, '');
}).then(done, done);
});
test('#Test Video date empty', function() {
currentVideo = {
metadata: {
title: 'Video test'
},
date: ''
};
showInfoView();
assert.equal(document.getElementById('info-date').textContent, '');
});
});
suite('#Video Format Size', function() {
test('#Test Video size', function(done) {
currentVideo = {
metadata: {
title: 'My test video'
},
size: 229455
};
showInfoView().then(() => {
l10nAssert(
document.getElementById('info-size'),
'fileSize',
{size: '224', unit: 'KB'}
);
}).then(done, done);
});
test('#Test Video size null', function() {
currentVideo = {
metadata: {
title: 'Video title'
},
size: null
};
showInfoView();
assert.equal(document.getElementById('info-size').textContent, '');
});
test('#Test Video size 3.9MB', function(done) {
currentVideo = {
metadata: {
title: 'Video large size'
},
size: 4110000
};
showInfoView().then(() => {
l10nAssert(
document.getElementById('info-size'),
'fileSize',
{size: '3.9', unit: 'MB'}
);
}).then(done, done);
});
test('#Test Video size 4MB', function(done) {
currentVideo = {
metadata: {
title: 'Video large size'
},
size: 4 * 1024 * 1024
};
showInfoView().then(() => {
l10nAssert(
document.getElementById('info-size'),
'fileSize',
{size: '4', unit: 'MB'}
);
}).then(done, done);
});
});
suite('#Update dialog tests', function() {
var thumbnailItemName = 'dummy-file-name-09.3gp';
var realLazyLoader;
suiteSetup(function() {
MediaDB = new MockMediaDB();
MockThumbnailGroup.reset();
var dummyContainer = document.createElement('div');
thumbnailList = new ThumbnailList(MockThumbnailGroup, dummyContainer);
dom.overlay = document.createElement('overlay');
dom.overlayMenu = document.createElement('overlay-menu');
dom.overlayActionButton = document.createElement('overlay-action-button');
dom.overlayMenu = document.createElement('overlay-menu');
dom.overlayTitle = document.createElement('overlay-title');
dom.overlayText = document.createElement('overlay-text');
realLazyLoader = LazyLoader;
LazyLoader = MockLazyLoader;
});
setup(function() {
dom.overlayTitle.setAttribute('data-l10n-id', '');
dom.overlayText.setAttribute('data-l10n-id', '');
dom.overlay.classList.add('hidden');
document.body.classList.remove('overlay');
});
suiteTeardown(function() {
LazyLoader = realLazyLoader;
});
/**
* If there is at least one thumbnail loaded, hide overlay
*/
test('#Update dialog, hide overlay', function(done) {
thumbnailList.addItem({'name': thumbnailItemName});
updateDialog();
setTimeout(function() {
testOverlayVisibility(false);
done();
}, 1);
});
/**
* DB being upraded
*/
test('#Update dialog, db upgrade, \'upgrade\' title and text',
function(done) {
thumbnailList.removeItem(thumbnailItemName);
storageState = MediaDB.UPGRADING;
dom.overlayMenu.classList.remove('hidden');
dom.overlayActionButton.classList.remove('hidden');
updateDialog();
// Allow lazy loader to load
setTimeout(function() {
testOverlayVisibility(true);
assert.isTrue(dom.overlayMenu.classList.contains('hidden'));
assert.isTrue(dom.overlayActionButton.classList.contains('hidden'));
assert.equal(dom.overlayTitle.getAttribute('data-l10n-id'),
'upgrade-title');
assert.equal(dom.overlayText.getAttribute('data-l10n-id'),
'upgrade-text');
done();
}, 1);
});
test('#Update dialog, empty list, \'empty\' overlay menu',
function(done) {
pendingPick = null;
storageState = null;
dom.overlayMenu.classList.add('hidden');
dom.overlayActionButton.classList.add('hidden');
dom.overlayTitle.setAttribute('data-l10n-id', '');
dom.overlayText.setAttribute('data-l10n-id', '');
firstScanEnded = true;
metadataQueue = {'length': 0};
updateDialog();
// Allow lazy loader to load
setTimeout(function() {
testOverlayVisibility(true);
assert.isFalse(dom.overlayMenu.classList.contains('hidden'));
assert.isFalse(dom.overlayActionButton.classList.contains('hidden'));
assert.equal(dom.overlayActionButton.getAttribute('data-l10n-id'),
'overlay-camera-button');
assert.equal(dom.overlayTitle.getAttribute('data-l10n-id'),
'empty-title');
assert.equal(dom.overlayText.getAttribute('data-l10n-id'),
'empty-text');
done();
}, 1);
});
test('#Update dialog, no card, \'no card\' title and text',
function(done) {
thumbnailList.removeItem(thumbnailItemName);
storageState = MediaDB.NOCARD;
dom.overlayMenu.classList.remove('hidden');
dom.overlayActionButton.classList.remove('hidden');
dom.overlayTitle.setAttribute('data-l10n-id', '');
dom.overlayText.setAttribute('data-l10n-id', '');
updateDialog();
// Allow lazy loader to load
setTimeout(function() {
testOverlayVisibility(true);
assert.isTrue(dom.overlayMenu.classList.contains('hidden'));
assert.isTrue(dom.overlayActionButton.classList.contains('hidden'));
assert.equal(dom.overlayTitle.getAttribute('data-l10n-id'),
'nocard2-title');
assert.equal(dom.overlayText.getAttribute('data-l10n-id'),
'nocard3-text');
done();
}, 1);
});
test('#Update dialog, media no mnt, \'no mount\' title/text',
function(done) {
thumbnailList.removeItem(thumbnailItemName);
storageState = MediaDB.UNMOUNTED;
dom.overlayMenu.classList.remove('hidden');
dom.overlayActionButton.classList.remove('hidden');
dom.overlayTitle.setAttribute('data-l10n-id', '');
dom.overlayText.setAttribute('data-l10n-id', '');
updateDialog();
// Allow lazy loader to load
setTimeout(function() {
testOverlayVisibility(true);
assert.isTrue(dom.overlayMenu.classList.contains('hidden'));
assert.isTrue(dom.overlayActionButton.classList.contains('hidden'));
assert.equal(dom.overlayTitle.getAttribute('data-l10n-id'),
'pluggedin-title');
assert.equal(dom.overlayText.getAttribute('data-l10n-id'),
'pluggedin-text');
done();
}, 1);
});
});
suite('#showPlayer flows', function() {
var selectedVideo;
var videoDuration;
var selectedThumbnail;
var playerPlaySpy;
var playerPauseSpy;
var fakeTimer;
function getTitle(autoPlay, enterFullscreen, keepControls, misc) {
var appendage;
var title = '#showPlayer: ';
// All three boolean arguments need to be passed or none.
// If none, misc argument can be passed by itself or together
// with the booleans.
if (arguments.length > 1) {
if (!autoPlay) {
title += '!';
}
title += 'play';
title += ', ';
if (!enterFullscreen) {
title += '!';
}
title += 'fullscreen';
title += ', ';
if (!keepControls) {
title += '!';
}
title += 'show controls';
if (arguments.length === 4) {
title += ' - ';
appendage = misc;
}
}
else {
appendage = autoPlay;
}
if (arguments.length === 4 || arguments.length === 1) {
title += appendage;
}
return title;
}
suiteSetup(function(done) {
dom.player = new MockVideoPlayer();
playerPlaySpy = sinon.spy(dom.player, 'play');
playerPauseSpy = sinon.spy(dom.player, 'pause');
fakeTimer = sinon.useFakeTimers();
videoDuration = 1.25;
selectedVideo = {
'name': 'test-video.webm',
'type': 'video\/webm',
'size': '19565',
'date': '1395088917000',
'metadata': {
'isVideo': 'true',
'title': 'test-video1',
'duration': videoDuration,
'width': '640',
'height': '360',
'rotation': '0',
'currentTime': 0
}
};
MockThumbnailGroup.reset();
var dummyContainer = document.createElement('div');
thumbnailList = new ThumbnailList(MockThumbnailGroup, dummyContainer);
// Thumbnail being selected is not focused
MockThumbnailGroup._GroupID = '2014-03_selected';
thumbnailList.addItem(selectedVideo);
selectedThumbnail =
thumbnailList.thumbnailMap[selectedVideo.name];
isPhone = true; // setControlsVisibility
getAsset('/test/unit/media/test.webm', function(blob) {
videodb = new MockMediaDB(blob);
done();
});
});
suiteTeardown(function() {
dom.player = null;
});
setup(function() {
selectedThumbnail.htmlNode.classList.remove('focused');
dom.player.setDuration(0);
dom.player.currentTime = -1;
loadingChecker.resetLoadedMetadataCallback();
});
teardown(function() {
playerPlaySpy.reset();
playerPauseSpy.reset();
});
function testTimeSliderAccessibility() {
assert.equal(dom.timeSlider.getAttribute('data-l10n-id'), 'seek-bar');
assert.deepEqual(
JSON.parse(dom.timeSlider.getAttribute('data-l10n-args')),
{ duration: '00:01' });
assert.equal(dom.timeSlider.getAttribute('aria-valuemin'), '0');
assert.equal(dom.timeSlider.getAttribute('aria-valuemax'),
dom.player.duration);
assert.equal(dom.timeSlider.getAttribute('aria-valuenow'),
dom.player.currentTime);
assert.equal(dom.timeSlider.getAttribute('aria-valuetext'), '00:00');
}
/**
* autoPlay=false, enterFullscreen=false, keepControls=true, current
* video exists, video is NOT seeking
* + show, do not play, current video
* + do not show fullscreen
* + do not auto-hide controls
* + change focus from 'old' thumbnail to newly selected
* + 'doneSeeking' function is called synchronously
* + video player is paused
*/
test(getTitle(false, false, true,
'curr video !exist, video !seeking'),
function() {
currentVideo = {
'name': 'current video',
'type': 'video\/webm',
'size': '1048576',
'date': '1395088917000',
'metadata': {
'isVideo': 'true',
'title': 'test-video2',
'duration': '5:00',
'width': '640',
'height': '360',
'rotation': '0',
'currentTime': 1.2
}
};
// Mock that the 'current' thumbnail is focused
MockThumbnailGroup._GroupID = '2014-03_current';
thumbnailList.addItem(currentVideo);
var currentThumbnail = thumbnailList.thumbnailMap[currentVideo.name];
currentThumbnail.htmlNode.classList.add('focused');
setButtonPaused(true);
document.body.classList.add(LAYOUT_MODE.list); // Stage list layout
// Stage controls being hidden
dom.playerView.classList.add('video-controls-hidden');
dom.videoContainer.setAttribute('data-l10n-id', 'show-controls-button');
dom.player.setSeeking(false);
dom.player.setDuration(videoDuration);
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
assert.equal(containsClass(currentThumbnail.htmlNode,
'focused'), false);
assert.equal(containsClass(selectedThumbnail.htmlNode,
'focused'), true);
assert.equal(dom.player.preload, 'metadata');
assert.isTrue(dom.player.hidden);
//
// showPlayer calls setVideoUrl which sets 'src' of
// video element. Except that we're using a mock video
// object so onloadedmetadata is not going to be called
// automatically -- invoke it manually
//
loadingChecker.invokeLoadedMetadataCallback();
//
// enterFullscreen is false -- did not enter fullscreen
//
assert.equal(containsClass(document.body,
LAYOUT_MODE.list), true);
assert.equal(containsClass(document.body,
LAYOUT_MODE.fullscreenPlayer), false);
assert.equal(dom.durationText.textContent, '00:01');
assert.equal(dom.player.currentTime, currentVideo.metadata.currentTime);
testTimeSliderAccessibility();
//
// video is not seeking -- 'doneSeeking' is called syncronously
// * autoPlay is false -- video is paused
// * keepControls is true -- controls not hidden
//
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(containsClass(dom.playerView, 'video-controls-hidden'));
assert.equal(dom.videoContainer.getAttribute('data-l10n-id'),
'hide-controls-button');
assert.isNull(dom.player.onseeked);
});
/**
* This test is the same as 'test1' except that in test1
* the video is not seeking whereas in this test the video
* is seeking.
*
* autoPlay=false, enterFullscreen=false, keepControls=true, current
* video exists, video IS seeking
* + show, do not play, current video
* + do not show fullscreen
* + do not auto-hide controls
* + change focus from 'old' thumbnail to newly selected
* + 'doneSeeking' function is not called synchronously
* + video player is paused when 'doneSeeking' is called
*/
test(getTitle(false, false, true,
'curr video !exist, video seeking'),
function() {
dom.player.setSeeking(true);
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
//
// showPlayer calls setVideoUrl which sets 'src' of
// video element. Except that we're using a mock video
// object so onloadedmetadata is not going to be called
// automatically -- invoke it manually
//
loadingChecker.invokeLoadedMetadataCallback();
//
// video is seeking -- 'doneSeeking' is not called syncronously
// * dom.player.onseeked is set to 'doneSeeking' function
//
assert.isTrue(dom.player.onseeked.toString().search('doneSeeking') > 0);
assert.equal(playerPauseSpy.callCount, 0);
dom.player.onseeked();
//
// * autoPlay is false -- video is paused
// * keepControls is true -- controls not hidden
//
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(containsClass(dom.playerView, 'video-controls-hidden'));
assert.equal(dom.videoContainer.getAttribute('data-l10n-id'),
'hide-controls-button');
assert.isNull(dom.player.onseeked);
});
/**
* autoPlay=false, enterFullscreen=false, keepControls=true, current
* video does NOT exist, video is not seeking
* + show, do not play, current video
* + do not show fullscreen
* + do not auto-hide controls
* + current video is set to selected video
* + selected video becomes focused
* + 'doneSeeking' function is called synchronously
* + video player is paused
*/
test(getTitle(false, false, true,
'curr video does not exist, video not seeking'),
function() {
currentVideo = null;
dom.player.setSeeking(false);
dom.player.setDuration(videoDuration);
document.body.classList.add(LAYOUT_MODE.list); // Stage list layout
// Stage controls being hidden
dom.playerView.classList.add('video-controls-hidden');
dom.videoContainer.setAttribute('data-l10n-id', 'show-controls-button');
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
// currentVideo should be same as selectedVideo
assert.equal(currentVideo.name, selectedVideo.name);
assert.equal(currentVideo.type, selectedVideo.type);
assert.equal(currentVideo.date, selectedVideo.date);
assert.equal(currentVideo.metadata.isVideo,
selectedVideo.metadata.isVideo);
assert.equal(currentVideo.metadata.title,
selectedVideo.metadata.title);
assert.equal(currentVideo.metadata.duration,
selectedVideo.metadata.duration);
assert.equal(currentVideo.metadata.width,
selectedVideo.metadata.width);
assert.equal(currentVideo.metadata.height,
selectedVideo.metadata.height);
assert.equal(currentVideo.metadata.rotation,
selectedVideo.metadata.rotation);
assert.equal(currentVideo.metadata.currentTime,
selectedVideo.metadata.currentTime);
assert.isTrue(containsClass(selectedThumbnail.htmlNode, 'focused'));
assert.equal(dom.player.preload, 'metadata');
assert.isTrue(dom.player.hidden);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.durationText.textContent, '00:01');
assert.equal(dom.player.currentTime, 0);
testTimeSliderAccessibility();
assert.equal(containsClass(document.body,
LAYOUT_MODE.list), true);
assert.equal(containsClass(document.body,
LAYOUT_MODE.fullscreenPlayer), false);
assert.isTrue(dom.play.classList.contains('paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isTrue(playerPauseSpy.calledOnce);
});
/**
* This test tests that the video player is playing after
* showPlayer is called.
*
* autoPlay=true, enterFullscreen=false, keepControls=true, current
* video does not exist, video is NOT seeking
* + 'doneSeeking' function is called synchronously
* + video player is playing
*
* This test does not test the body of show player as
* that has already been tested fully by the previous
* tests.
*/
test(getTitle(true, false, true), function() {
currentVideo = null;
dom.player.setSeeking(false);
document.body.classList.add(LAYOUT_MODE.list); // Not fullscreen
dom.playerView.classList.add('video-controls-hidden');
dom.videoContainer.setAttribute('data-l10n-id', 'show-controls-button');
showPlayer(selectedVideo,
true, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.isTrue(containsClass(document.body, LAYOUT_MODE.list));
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPlaySpy.calledOnce);
assert.isFalse(containsClass(dom.playerView, 'video-controls-hidden'));
assert.equal(dom.videoContainer.getAttribute('data-l10n-id'),
'hide-controls-button');
});
/**
* This test tests that the video player is shown fullscreen.
*
* autoPlay=true, enterFullscreen=true, keepControls=true, current
* video does not exist, video is NOT seeking
* + 'doneSeeking' function is called synchronously
* + video player is playing
* + video player is fullscreen
*
* This test does not test the body of show player as
* that has already been tested fully by the previous
* tests.
*/
test(getTitle(true, true, true), function() {
currentVideo = null;
dom.player.setSeeking(false);
//
// Not fullscreen, prepare to change layout made to fullscreen
//
currentLayoutMode = LAYOUT_MODE.list;
document.body.classList.add(LAYOUT_MODE.list);
dom.playerView.classList.add('video-controls-hidden');
dom.videoContainer.setAttribute('data-l10n-id', 'show-controls-button');
showPlayer(selectedVideo,
true, /* autoPlay */
true, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(containsClass(document.body,
LAYOUT_MODE.list), false);
assert.equal(containsClass(document.body,
LAYOUT_MODE.fullscreenPlayer), true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPlaySpy.calledOnce);
assert.equal(playerPauseSpy.callCount, 0);
assert.isFalse(containsClass(dom.playerView, 'video-controls-hidden'));
assert.equal(dom.videoContainer.getAttribute('data-l10n-id'),
'hide-controls-button');
});
/**
* This test tests that the video controls are not shown.
*
* autoPlay=true, enterFullscreen=true, keepControls=false, current
* video does not exist, video is NOT seeking
* + 'doneSeeking' function is called synchronously
* + video player is playing
* + video player is fullscreen
* + controls are not shown
*
* This test does not test the body of show player as
* that has already been tested fully by the previous
* tests.
*/
test(getTitle(true, true, false), function() {
currentVideo = null;
dom.player.setSeeking(false);
//
// Not fullscreen, prepare to change layout made to fullscreen
//
currentLayoutMode = LAYOUT_MODE.list;
document.body.classList.add(LAYOUT_MODE.list);
dom.playerView.classList.remove('video-controls-hidden');
dom.videoContainer.setAttribute('data-l10n-id', 'hide-controls-button');
showPlayer(selectedVideo,
true, /* autoPlay */
true, /* enterFullscreen */
false /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(containsClass(document.body,
LAYOUT_MODE.list), false);
assert.equal(containsClass(document.body,
LAYOUT_MODE.fullscreenPlayer), true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPlaySpy.calledOnce);
assert.equal(playerPauseSpy.callCount, 0);
fakeTimer.tick(300);
assert.isTrue(containsClass(dom.playerView, 'video-controls-hidden'));
assert.equal(dom.videoContainer.getAttribute('data-l10n-id'),
'show-controls-button');
});
/**
* Tests whether the appropriate video-related elements
* are set properly based on the metadata of the selected video:
*
* - video position is not at end
*/
test(getTitle('selected video has metadata, video not at end'),
function() {
currentVideo = null;
selectedVideo.metadata.currentTime = 0;
dom.player.setSeeking(false);
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.player.currentTime,
selectedVideo.metadata.currentTime);
assert.equal(dom.videoTitle.textContent,
selectedVideo.metadata.title);
});
/**
* Tests whether the appropriate video-related elements
* are set properly based on the metadata of the selected video:
*
* - video position is at end
*/
test(getTitle('selected video has metadata, video at end'),
function() {
currentVideo = null;
dom.player.setDuration(videoDuration);
selectedVideo.metadata.currentTime = videoDuration;
dom.player.setSeeking(false);
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.player.currentTime, 0);
assert.equal(dom.videoTitle.textContent, currentVideo.metadata.title);
});
/**
* Tests whether the appropriate video-related elements
* are set properly based on the metadata of the selected video:
*
* - video has not been played before (metadata.currentTime
* has no value)
*/
test(getTitle('selected video has metadata, first time played'),
function() {
dom.player.setSeeking(false);
currentVideo = null;
selectedVideo = {
'name': 'test-video.webm',
'type': 'video\/webm',
'size': '19565',
'date': '1395088917000',
'metadata': {
'isVideo': 'true',
'title': 'test-video1',
'duration': videoDuration,
'width': '640',
'height': '360',
'rotation': '0'
}
};
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.player.currentTime, 0);
assert.equal(dom.videoTitle.textContent, currentVideo.metadata.title);
});
/**
* Tests whether the appropriate video-related elements
* are set properly based on the metadata of the selected video:
*
* - video does not have metadata
* - video does not have a title
*/
test(getTitle('selected video has no metadata, no title'),
function() {
dom.player.setSeeking(false);
currentVideo = null;
selectedVideo = {
'name': 'test-video.webm',
'type': 'video\/webm',
'size': '19565',
'date': '1395088917000'
};
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.player.currentTime, 0);
assert.equal(dom.videoTitle.textContent, '');
});
/**
* Tests whether the appropriate video-related elements
* are set properly based on the metadata of the selected video:
*
* - video does not have metadata
* - video does have a title
*/
test(getTitle('selected video has no metadata, has title'),
function() {
dom.player.setSeeking(false);
currentVideo = null;
selectedVideo = {
'name': 'test-video.webm',
'type': 'video\/webm',
'size': '19565',
'date': '1395088917000',
'title': 'test-video1'
};
showPlayer(selectedVideo,
false, /* autoPlay */
false, /* enterFullscreen */
true /* keepControls */);
loadingChecker.invokeLoadedMetadataCallback();
assert.equal(dom.player.currentTime, 0);
assert.equal(dom.videoTitle.textContent, selectedVideo.title);
});
test('#showPlayer() removes captions and looks for new ones', function() {
selectedVideo = {
'name': 'test-video.webm',
'type': 'video\/webm',
'size': '19565',
'date': '1395088917000',
'title': 'test-video1'
};
captions.remove.reset();
captions.findAndDisplay.reset();
showPlayer(selectedVideo, false, false, true);
assert.ok(captions.remove.calledOnce);
assert.ok(captions.findAndDisplay.calledOnce);
assert.equal(captions.findAndDisplay.args[0][0], selectedVideo.name);
});
});
suite('hidePlayer flows', function() {
var playerPauseSpy;
var playerLoadSpy;
var startParsingMetadataSpy;
var updateMetadataDbSpy;
var updatePosterSpy;
var setWatchedSpy;
suiteSetup(function() {
dom.player = new MockVideoPlayer();
playerPauseSpy = sinon.spy(dom.player, 'pause');
playerLoadSpy = sinon.spy(dom.player, 'load');
startParsingMetadataSpy = sinon.spy();
startParsingMetadata = startParsingMetadataSpy;
updateMetadataDbSpy = sinon.spy();
updatePosterSpy = sinon.spy();
setWatchedSpy = sinon.spy();
videodb = new MockMediaDB();
updateMetadataDbSpy = sinon.spy(videodb, 'updateMetadata');
currentVideo = {
'name': videoName
};
thumbnailList.addItem(currentVideo);
var thumbnail = thumbnailList.thumbnailMap[currentVideo.name];
thumbnail.updatePoster = updatePosterSpy;
thumbnail.setWatched = setWatchedSpy;
});
setup(function() {
playerPauseSpy.reset();
playerLoadSpy.reset();
startParsingMetadataSpy.reset();
updateMetadataDbSpy.reset();
updatePosterSpy.reset();
setWatchedSpy.reset();
});
test('#hidePlayer: !update metadata, playerShowing, !callback',
function() {
pendingPick = false;
playerShowing = true;
// hidePlayer pauses the video and removes the 'paused' class; TODO: why?
// In the 'playerShowing' flow, dom.play has 'paused' attribute,
// which gets removed during the flow
setButtonPaused(true);
// In the 'playerShowing' flow, dom.player has 'src' attribute,
// which gets removed during the flow
dom.player.src = 'about:blank';
hidePlayer(false);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
});
/**
* updateVideoMetadata is true and currentVideo.metadata is not null,
* pendingPick is false -- updateMetadata function should be invoked
* (not first time watched)
*/
test('#hidePlayer: update metadata (1), playerShowing, !callback',
function() {
playerShowing = true;
pendingPick = false;
// hidePlayer pauses video and then removes 'paused' class - TODO: why?
setButtonPaused(true);
dom.player.src = 'about:blank';
//
// Initialize dom.player.currentTime in order to test
// that currentVideo.metadata.currentTime is set to
// dom.player.currentTime
//
dom.player.currentTime = 1;
currentVideo = {
'name': videoName,
'metadata': {
'title': 'video title',
'watched': true
}
};
hidePlayer(true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isTrue(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isTrue(currentVideo.metadata.watched);
assert.isTrue(updateMetadataDbSpy.calledOnce);
assert.equal(currentVideo.metadata.currentTime, dom.player.currentTime);
});
/**
* updateVideoMetadata is true and currentVideo.metadata is not null,
* pendingPick is false -- updateMetadata function should be invoked
* (first time watched)
*/
test('#hidePlayer: update metadata (2), playerShowing, !callback',
function() {
playerShowing = true;
pendingPick = false;
setButtonPaused(true);
dom.player.src = 'about:blank';
//
// Initialize dom.player.currentTime in order to test
// that currentVideo.metadata.currentTime is set to
// dom.player.currentTime
//
dom.player.currentTime = 1;
currentVideo = {
'name': videoName,
'metadata': {
'title': 'video title',
'watched': false
}
};
hidePlayer(true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isTrue(updatePosterSpy.calledOnce);
assert.isTrue(setWatchedSpy.calledOnce);
assert.isTrue(currentVideo.metadata.watched);
assert.isTrue(updateMetadataDbSpy.calledOnce);
assert.equal(currentVideo.metadata.currentTime, dom.player.currentTime);
});
/**
* updateVideoMetadata is true and currentVideo.metadata is not null,
* pendingPick is true -- updateMetadata function should not be invoked.
*/
test('#hidePlayer: update metadata (3), playerShowing, !callback',
function() {
playerShowing = true;
pendingPick = true;
setButtonPaused(true);
dom.player.src = 'about:blank';
//
// Initialize dom.player.currentTime in order to test
// that currentVideo.metadata.currentTime is NOT set to
// dom.player.currentTime (since updateMetadata should
// not be called).
//
dom.player.currentTime = 1;
var currentTime = 10;
currentVideo = {
'name': videoName,
'metadata': {
'title': 'video title',
'watched': false,
'currentTime': currentTime
}
};
hidePlayer(true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(dom.player.hasAttribute('src'));
assert.isTrue(playerLoadSpy.calledOnce);
assert.isFalse(playerShowing);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(currentVideo.metadata.watched);
assert.isFalse(updateMetadataDbSpy.calledOnce);
assert.equal(currentVideo.metadata.currentTime, currentTime);
});
/**
* updateVideoMetadata is true, currentVideo.metadata is null --
* updateMetadata function should not be invoked.
*/
test('#hidePlayer: update metadata (4), playerShowing, !callback',
function() {
playerShowing = true;
pendingPick = false;
setButtonPaused(true);
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// the current video for this test has no metadata therefore
// there is no assertion regarding the value of
// currentVideo.metadata.currentTime
currentVideo = {
'name': videoName
};
hidePlayer(true);
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
});
/**
* updateVideoMetadata is true and currentVideo.metadata is not null,
* pendingPick is false -- updateMetadata function should be invoked
* (first time watched). hidePlayer callback is specified.
*/
test('#hidePlayer: update metadata (5), playerShowing, callback',
function(done) {
playerShowing = true;
pendingPick = false;
setButtonPaused(true);
dom.player.src = 'about:blank';
//
// Initialize dom.player.currentTime in order to test
// that currentVideo.metadata.currentTime is set to
// dom.player.currentTime
//
dom.player.currentTime = 1;
currentVideo = {
'name': videoName,
'metadata': {
'title': 'video title',
'watched': false
}
};
hidePlayer(true, function() {
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isTrue(updatePosterSpy.calledOnce);
assert.isTrue(setWatchedSpy.calledOnce);
assert.isTrue(currentVideo.metadata.watched);
assert.isTrue(updateMetadataDbSpy.calledOnce);
assert.equal(currentVideo.metadata.currentTime, dom.player.currentTime);
done();
});
});
/**
* updateVideoMetadata is false -- updateMetadata function should not
* be invoked. hidePlayer callback is specified and should be called.
*/
test('#hidePlayer: !update metadata, playerShowing, callback',
function(done) {
pendingPick = false;
playerShowing = true;
// In the 'playerShowing' flow, dom.play has 'paused' attribute,
// which gets removed during the flow
setButtonPaused(true);
// In the 'playerShowing' flow, dom.player has 'src' attribute,
// which gets removed during the flow
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// 'updateVideoMetadata' is false and the current video for
// this test has no metadata. Therefore, there is no assertion
// regarding the value of currentVideo.metadata.currentTime
hidePlayer(false, function() {
assert.isFalse(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'pause-button');
assert.isTrue(playerPauseSpy.calledOnce);
assert.isFalse(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isTrue(playerLoadSpy.calledOnce);
assert.isTrue(startParsingMetadataSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
done();
});
});
/**
* updateMetadata is true but playerShowing is false, hidePlayer should be
* a noop other than to call the hidePlayer callback if specified, which
* it is for this test.
*/
test('#hidePlayer: !update metadata, !playerShowing, callback',
function(done) {
// In the 'playerShowing' flow, dom.play has 'paused' attribute
// and dom.player has 'src' attribute. Set these to test that
// in the !playerShowing flow these attributes are not changed.
setButtonPaused(true);
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// 'updateVideoMetadata' is false and the current video for
// this test has no metadata. Therefore, there is no assertion
// regarding the value of currentVideo.metadata.currentTime
playerShowing = false;
hidePlayer(false, function() {
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isFalse(playerPauseSpy.calledOnce);
assert.isTrue(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isFalse(playerLoadSpy.calledOnce);
assert.isFalse(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
done();
});
});
/**
* updateMetadata is false and playerShowing is false, hidePlayer should be
* a noop other than to call the hidePlayer callback if specified, which
* it is not for this test.
*/
test('#hidePlayer: !update metadata, !playerShowing, !callback',
function() {
// In the 'playerShowing' flow, dom.play has 'paused' attribute
// and dom.player has 'src' attribute. Set these to test that
// in the !playerShowing flow these attributes are not changed.
setButtonPaused(true);
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// 'updateVideoMetadata' is false and the current video for
// this test has no metadata. Therefore, there is no assertion
// regarding the value of currentVideo.metadata.currentTime
playerShowing = false;
hidePlayer(false);
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isFalse(playerPauseSpy.calledOnce);
assert.isTrue(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isFalse(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
});
/**
* updateMetadata is true, playerShowing is false: hidePlayer should be
* a noop other than to call the hidePlayer callback if specified, which
* it is for this test.
*/
test('#hidePlayer: update metadata, !playerShowing, callback',
function(done) {
// In the 'playerShowing' flow, dom.play has 'paused' attribute
// and dom.player has 'src' attribute. Set these to test that
// in the !playerShowing flow these attributes are not changed.
setButtonPaused(true);
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// 'playerShowing' is false and the current video for
// this test has no metadata. Therefore, there is no assertion
// regarding the value of currentVideo.metadata.currentTime
playerShowing = false;
hidePlayer(true, function() {
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isFalse(playerPauseSpy.calledOnce);
assert.isTrue(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isFalse(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
done();
});
});
/**
* updateMetadata is true, playerShowing is false: hidePlayer should be
* a noop other than to call the hidePlayer callback if specified, which
* it is not for this test.
*/
test('#hidePlayer: update metadata, !playerShowing, !callback',
function() {
// In the 'playerShowing' flow, dom.play has 'paused' attribute
// and dom.player has 'src' attribute. Set these to test that
// in the !playerShowing flow these attributes are not changed.
setButtonPaused(true);
dom.player.src = 'about:blank';
// dom.player.currentTime is not set for this test because
// 'playerShowing' is false and the current video for
// this test has no metadata. Therefore, there is no assertion
// regarding the value of currentVideo.metadata.currentTime
playerShowing = false;
hidePlayer(true);
assert.isTrue(containsClass(dom.play, 'paused'));
assert.equal(dom.play.getAttribute('data-l10n-id'), 'play-button');
assert.isFalse(playerPauseSpy.calledOnce);
assert.isTrue(dom.player.hasAttribute('src'));
assert.isFalse(playerShowing);
assert.isFalse(startParsingMetadataSpy.calledOnce);
assert.isFalse(updatePosterSpy.calledOnce);
assert.isFalse(setWatchedSpy.calledOnce);
assert.isFalse(updateMetadataDbSpy.calledOnce);
});
test('#hidePlayer: removes captions', function(done) {
playerShowing = true;
captions.remove.reset();
hidePlayer(false, function() {
assert.ok(captions.remove.calledOnce);
done();
});
});
});
suite('handleScreenLayoutChange flows', function() {
var updateAllThumbnailTitlesSpy;
var rescaleSpy;
var HAVE_METADATA = 1;
suiteSetup(function(done) {
dom.player = new MockVideoPlayer();
ThumbnailItem = MockThumbnailItem;
MockThumbnailGroup.reset();
var dummyContainer = document.createElement('div');
thumbnailList = new ThumbnailList(MockThumbnailGroup, dummyContainer);
thumbnailList.addItem({'name': videoName});
updateAllThumbnailTitlesSpy = sinon.spy(thumbnailList,
'updateAllThumbnailTitles');
rescaleSpy = sinon.spy(VideoUtils, 'fitContainer');
getAsset('/test/unit/media/test.webm', function(blob) {
videodb = new MockMediaDB(blob);
done();
});
});
teardown(function() {
updateAllThumbnailTitlesSpy.reset();
rescaleSpy.reset();
});
test('#handleScreenLayoutChange: tablet, landscape, everything is waiting',
function() {
// In tablet, the landscape mode will show player and list at the same
// time. To keep only one hardware codec be used, we shows loading icon
// at the player view before first batch of files scanned and all their
// metadata are parsed.
ScreenLayout.setCurrentLayout('landscape');
isPhone = false;
firstScanEnded = false;
dom.spinnerOverlay.classList.add('hidden'); // stage spinner being hidden
handleScreenLayoutChange();
assert.isFalse(containsClass(dom.spinnerOverlay, 'hidden'));
assert.isTrue(containsClass(dom.playerView, 'disabled'));
assert.equal(dom.playerView.getAttribute('aria-disabled'), 'true',
'aria-disabled attribute should be set to true');
assert.equal(ThumbnailItem.titleMaxLines, 2);
});
test('#handleScreenLayoutChange: tablet, landscape, scan has ended',
function() {
ScreenLayout.setCurrentLayout('landscape');
isPhone = false;
firstScanEnded = true;
processingQueue = false;
dom.spinnerOverlay.classList.remove('hidden'); // stage spinner shown
dom.playerView.classList.add('disabled'); // stage player view
handleScreenLayoutChange();
assert.isTrue(containsClass(dom.spinnerOverlay, 'hidden'));
assert.isFalse(containsClass(dom.playerView, 'disabled'));
assert.equal(dom.playerView.getAttribute('aria-disabled'), 'false',
'aria-disabled attribute should be set to false');
assert.equal(ThumbnailItem.titleMaxLines, 2);
});
test('#handleScreenLayoutChange: tablet rotating to portrait, list view',
function() {
isPhone = false;
currentLayoutMode = LAYOUT_MODE.list;
ScreenLayout.setCurrentLayout('portrait');
playerShowing = true;
//
// currentVideo without metadata will cause hidePlayer to execute
// faster (won't try to update metadata).
//
currentVideo = {
'name': videoName
};
handleScreenLayoutChange();
assert.isFalse(playerShowing);
assert.equal(ThumbnailItem.titleMaxLines, 4);
});
test('#handleScreenLayoutChange: tablet rotating to landscape, list view',
function() {
isPhone = false;
currentLayoutMode = LAYOUT_MODE.list;
ScreenLayout.setCurrentLayout('landscape');
playerShowing = false;
//
// handleScreenLayoutChange invokes showPlayer with 'currentVideo',
//
currentVideo = {
'name': videoName
};
dom.player.onloadedmetadata = null;
handleScreenLayoutChange();
//
// showPlayer calls setVideoUrl which sets 'src' of
// video element. Except that we're using a mock video
// object so onloadedmetadata is not going to be called
// automatically -- invoke it manually
//
loadingChecker.invokeLoadedMetadataCallback();
assert.isTrue(playerShowing);
assert.equal(ThumbnailItem.titleMaxLines, 2);
});
test('#handleScreenLayoutChange: update thumbnail title text immediately',
function() {
isPhone = true;
currentLayoutMode = LAYOUT_MODE.list;
handleScreenLayoutChange();
assert.isTrue(updateAllThumbnailTitlesSpy.calledOnce);
});
test('#handleScreenLayoutChange: update thumbnail title text later',
function() {
isPhone = true;
currentLayoutMode = LAYOUT_MODE.fullscreenPlayer;
pendingUpdateTitleText = false;
handleScreenLayoutChange();
assert.equal(updateAllThumbnailTitlesSpy.callCount, 0);
assert.isTrue(pendingUpdateTitleText);
});
test('#handleScreenLayoutChange: update thumbnail title text -- abort',
function() {
isPhone = true;
currentLayoutMode = LAYOUT_MODE.list;
pendingUpdateTitleText = false;
thumbnailList = null;
handleScreenLayoutChange();
// Shouldn't have invoked updateAllThumbnailTitles nor
// should have set pendingUpdateTitleText
assert.equal(updateAllThumbnailTitlesSpy.callCount, 0);
assert.isFalse(pendingUpdateTitleText);
});
test('#handleScreenLayoutChange: phone, rescale',
function() {
currentVideo = {
'name': videoName,
'metadata': {
'rotation': '90'
}
};
isPhone = true;
currentLayoutMode = LAYOUT_MODE.fullscreenPlayer;
ScreenLayout.setCurrentLayout('portrait');
dom.player.readyState = HAVE_METADATA;
handleScreenLayoutChange();
assert.isTrue(rescaleSpy.calledOnce);
});
test('#handleScreenLayoutChange: phone, no rescale',
function() {
currentVideo = {
'name': videoName,
'metadata': {
'rotation': '90'
}
};
isPhone = true;
currentLayoutMode = LAYOUT_MODE.fullscreenPlayer;
ScreenLayout.setCurrentLayout('portrait');
dom.player.readyState = HAVE_NOTHING;
handleScreenLayoutChange();
assert.equal(rescaleSpy.callCount, 0);
});
});
suite('handleSliderKeypress', function() {
var keyEvent;
var realSeeker;
setup(function() {
dom.player.currentTime = 0;
dom.player.duration = 10;
dom.player.currentTime = 5;
keyEvent = document.createEvent('KeyboardEvent');
realSeeker = seeker;
seeker = { seekTo: sinon.spy() };
});
teardown(function() {
seeker = realSeeker;
});
test('test seek up and seek down based on key up and key down', function() {
[{ key: 38 /* DOM_VK_UP */, step: 2 },
{ key: 40 /* DOM_VK_UP */, step: -2 }].forEach(function(testSpec) {
var currentTime = dom.player.currentTime;
keyEvent.initKeyEvent('keypress', true, true, window, false, false,
false, false, testSpec.key, 0);
handleSliderKeypress(keyEvent);
assert.ok(seeker.seekTo.calledWith(currentTime + testSpec.step));
});
});
});
suite('setControlsVisibility flows', function() {
var updateVideoControlSliderSpy;
var nativeUpdateVideoControlSlider;
suiteSetup(function() {
nativeUpdateVideoControlSlider = updateVideoControlSlider;
updateVideoControlSliderSpy = sinon.spy();
updateVideoControlSlider = updateVideoControlSliderSpy;
});
suiteTeardown(function() {
updateVideoControlSlider = nativeUpdateVideoControlSlider;
});
teardown(function() {
updateVideoControlSliderSpy.reset();
});
/**
* On tablet in landscape mode, we always shows the video controls in list
* layout. Therefore, in this use case setControlsVisibility will update
* video controls slider.
*/
test('#setControlsVisibility: tablet in landscape mode, list view',
function() {
isPhone = false;
isPortrait = false;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(false);
assert.isTrue(updateVideoControlSliderSpy.calledOnce);
});
/**
* On tablet in portrait mode, regardless of the view,
* setControlsVisibility will show video controls based on the 'visible'
* argument. And therefore, setControlsVisibility will update the video
* controls slider based on the value of the 'visible' argument.
*/
test('#setControlsVisibility: tablet in portrait mode, show controls',
function() {
isPhone = false;
isPortrait = true;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(true);
assert.isTrue(updateVideoControlSliderSpy.calledOnce);
});
/**
* On tablet in portrait mode, regardless of the view,
* setControlsVisibility will show video controls based on the 'visible'
* argument. And therefore, setControlsVisibility will update the video
* controls slider based on the value of the 'visible' argument.
*/
test('#setControlsVisibility: tablet in portrait mode, dont show controls',
function() {
isPhone = false;
isPortrait = true;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(false);
assert.equal(updateVideoControlSliderSpy.callCount, 0);
});
/**
* On phone, regardless of the view, setControlsVisibility will show
* video controls based on the 'visible' argument. And therefore,
* setControlsVisibility will update the video controls slider based on
* the value of the 'visible' argument.
*/
test('#setControlsVisibility: phone, landscape, list view, show controls',
function() {
isPhone = true;
isPortrait = false;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(true);
assert.isTrue(updateVideoControlSliderSpy.calledOnce);
});
test('#setControlsVisibility: phone, portrait, list view, show controls',
function() {
isPhone = true;
isPortrait = true;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(true);
assert.isTrue(updateVideoControlSliderSpy.calledOnce);
});
test('#setControlsVisibility: phone, portrait, fullscreen, show controls',
function() {
isPhone = true;
isPortrait = false;
currentLayoutMode = LAYOUT_MODE.fullscreen;
setControlsVisibility(true);
assert.isTrue(updateVideoControlSliderSpy.calledOnce);
});
test('#setControlsVisibility: phone, landscape, list, no show controls',
function() {
isPhone = true;
isPortrait = false;
currentLayoutMode = LAYOUT_MODE.list;
setControlsVisibility(false);
assert.equal(updateVideoControlSliderSpy.callCount, 0);
});
});
suite('toggleVideoControls flows', function() {
var clearTimeoutSpy;
var setControlsVisibilitySpy;
var nativeSetControlsVisibility;
var event = {};
suiteSetup(function() {
clearTimeoutSpy = sinon.spy(window, 'clearTimeout');
clearTimeout = clearTimeoutSpy;
nativeSetControlsVisibility = setControlsVisibility;
setControlsVisibilitySpy = sinon.spy();
setControlsVisibility = setControlsVisibilitySpy;
});
suiteTeardown(function() {
clearTimeoutSpy.restore();
});
teardown(function() {
clearTimeoutSpy.reset();
setControlsVisibilitySpy.reset();
});
test('#toggleVideoControls: control fade timeout', function() {
controlFadeTimeout = 1;
toggleVideoControls(event);
assert.isTrue(clearTimeoutSpy.calledOnce);
assert.equal(controlFadeTimeout, null);
});
test('#toggleVideoControls: no control fade timeout', function() {
controlFadeTimeout = null;
toggleVideoControls(event);
assert.equal(clearTimeoutSpy.callCount, 0);
assert.equal(controlFadeTimeout, null);
});
test('#toggleVideoControls: toggle control showing', function() {
pendingPick = false;
controlShowing = false;
toggleVideoControls(event);
assert.isTrue(setControlsVisibilitySpy.calledOnce);
assert.isTrue(setControlsVisibilitySpy.calledWith(true));
assert.isTrue(event.cancelBubble);
});
test('#toggleVideoControls: toggle control not showing',
function() {
pendingPick = false;
controlShowing = true;
toggleVideoControls(event);
assert.isTrue(setControlsVisibilitySpy.calledOnce);
assert.isTrue(setControlsVisibilitySpy.calledWith(false));
assert.isFalse(event.cancelBubble);
});
test('#toggleVideoControls: pending pick, no toggle',
function() {
pendingPick = true;
toggleVideoControls(event);
assert.equal(setControlsVisibilitySpy.callCount, 0);
});
});
/*
* updateSelection is called when entering thumbnail selection mode, or
* when the selection changes. The function updates the message the top
* of the screen and enables or disables the Delete and Share buttons.
*/
suite('updateSelection flows', function() {
var videodata = {};
var videoBlob;
suiteSetup(function(done) {
videodata = {'name': videoName};
MockThumbnailGroup.reset();
var dummyContainer = document.createElement('div');
thumbnailList = new ThumbnailList(MockThumbnailGroup, dummyContainer);
thumbnailList.addItem({'name': videodata.name});
getAsset('/test/unit/media/test.webm', function(blob) {
videoBlob = blob;
videodb = new MockMediaDB(videoBlob);
done();
});
});
setup(function() {
selectedFileNames = [];
selectedFileNamesToBlobs = {};
});
test('#updateSelection: toggle thumbnail as selected and add to selected ' +
'filenames', function() {
var thumbnail = thumbnailList.thumbnailMap[videodata.name];
thumbnail.htmlNode.classList.remove('selected'); // not selected
thumbnail.htmlNode.setAttribute('aria-selected', false);
updateSelection(videodata);
assert.isTrue(thumbnail.htmlNode.classList.contains('selected'),
'thumbnail should contain \'selected\' class');
assert.equal(thumbnail.htmlNode.getAttribute('aria-selected'), 'true',
'thumbnail aria-selected attribute should be set to true');
assert.equal(selectedFileNames.length, 1,
'should be one selected file');
assert.equal(selectedFileNames[0], videodata.name,
'name of selected file should be name of videodata');
assert.equal(selectedFileNamesToBlobs[videodata.name], videoBlob,
'blob associated with videodata name should be video blob');
});
test('#updateSelection: toggle thumbnail as not selected and remove ' +
'selected filenames', function() {
var thumbnail = thumbnailList.thumbnailMap[videodata.name];
thumbnail.htmlNode.classList.add('selected'); // selected
thumbnail.htmlNode.setAttribute('aria-selected', true);
updateSelection(videodata);
assert.isFalse(thumbnail.htmlNode.classList.contains('selected'),
'thumbnail should not contain \'selected\' class');
assert.equal(thumbnail.htmlNode.getAttribute('aria-selected'), 'false',
'thumbnail aria-selected attribute should be set to false');
assert.equal(selectedFileNames.length, 0,
'shouldnt be any selected files');
assert.equal(selectedFileNamesToBlobs[videodata.name], undefined,
'no blob associated with videodata name');
});
test('#updateSelection: update UI, thumbnail is selected', function() {
dom.thumbnailsDeleteButton.classList.add('disabled');
dom.thumbnailsShareButton.classList.add('disabled');
dom.thumbnailsDeleteButton.setAttribute('aria-disabled', true);
dom.thumbnailsShareButton.setAttribute('aria-disabled', true);
var thumbnail = thumbnailList.thumbnailMap[videodata.name];
thumbnail.htmlNode.classList.remove('selected'); // not selected
thumbnail.htmlNode.setAttribute('aria-selected', false);
updateSelection(videodata);
l10nAssert(
dom.thumbnailsNumberSelected,
'number-selected2',
{n: 1}
);
assert.isFalse(containsClass(dom.thumbnailsDeleteButton, 'disabled'),
'thumbnail delete button should be enabled');
assert.isFalse(containsClass(dom.thumbnailsShareButton, 'disabled'),
'thumbnail share button should be enabled');
assert.equal(dom.thumbnailsDeleteButton.getAttribute('aria-disabled'),
'false', 'aria-disabled attribute should be set to false');
assert.equal(dom.thumbnailsShareButton.getAttribute('aria-disabled'),
'false', 'aria-disabled attribute should be set to false');
});
test('#updateSelection: update UI, no thumbnail is selected', function() {
var thumbnail = thumbnailList.thumbnailMap[videodata.name];
thumbnail.htmlNode.classList.add('selected'); // selected
thumbnail.htmlNode.setAttribute('aria-selected', true);
updateSelection(videodata);
l10nAssert(
dom.thumbnailsNumberSelected,
'number-selected2',
{n: 0}
);
assert.isTrue(containsClass(dom.thumbnailsDeleteButton, 'disabled'),
'thumbnail delete button should be disabled');
assert.isTrue(containsClass(dom.thumbnailsShareButton, 'disabled'),
'thumbnail share button should be disabled');
assert.equal(dom.thumbnailsDeleteButton.getAttribute('aria-disabled'),
'true', 'aria-disabled attribute should be set to true');
assert.equal(dom.thumbnailsShareButton.getAttribute('aria-disabled'),
'true', 'aria-disabled attribute should be set to true');
});
});
suite('handleSliderTouchStart flows', function() {
var handleSliderTouchMoveSpy;
var nativeHandleSliderTouchMove;
var existingTouchStartEventId = 1;
var touchStartIdFromEvent = 10;
var event = { 'changedTouches': [{'identifier': touchStartIdFromEvent}] };
var playerPauseSpy;
var width, height;
suiteSetup(function() {
nativeHandleSliderTouchMove = handleSliderTouchMove;
handleSliderTouchMoveSpy = sinon.spy();
handleSliderTouchMove = handleSliderTouchMoveSpy;
playerPauseSpy = sinon.spy(dom.player, 'pause');
width = 50;
height = 100;
});
setup(function() {
dragging = false;
touchStartID = null;
handleSliderTouchMoveSpy.reset();
playerPauseSpy.reset();
dom.sliderWrapper.style.width = width + 'px';
dom.sliderWrapper.style.height = height + 'px';
});
teardown(function() {
dom.sliderWrapper.style.width = 0 + 'px';
dom.sliderWrapper.style.height = 0 + 'px';
});
suiteTeardown(function() {
handleSliderTouchMove = nativeHandleSliderTouchMove;
playerPauseSpy.restore();
});
test('#handleSliderTouchStart: already have touch start event',
function() {
// stage data to indicate there has already been a touch start event
touchStartID = existingTouchStartEventId;
handleSliderTouchStart();
assert.equal(touchStartID, existingTouchStartEventId);
assert.equal(handleSliderTouchMoveSpy.callCount, 0);
assert.isFalse(dragging);
assert.equal(sliderRect, undefined);
});
test('#handleSliderTouchStart: dont know video duration',
function() {
dom.player.duration = Infinity;
handleSliderTouchStart(event);
assert.isTrue(dragging);
assert.equal(touchStartID, touchStartIdFromEvent,
'touch start id should come from event');
assert.equal(handleSliderTouchMoveSpy.callCount, 0,
'function returns before calling handleSliderTouchMove');
assert.equal(sliderRect.width, width);
assert.equal(sliderRect.height, height);
assert.equal(sliderRect.right, sliderRect.left + width);
assert.equal(sliderRect.bottom, sliderRect.top + height);
});
test('#handleSliderTouchStart: paused while dragging',
function() {
dom.player.duration = 100;
dom.player.paused = true;
handleSliderTouchStart(event);
assert.isTrue(dragging);
assert.equal(touchStartID, touchStartIdFromEvent,
'touch start id should come from event');
assert.isTrue(handleSliderTouchMoveSpy.calledOnce,
'handleSliderTouchMove is called once');
assert.equal(playerPauseSpy.callCount, 0,
'dom.player.pause is not called');
assert.equal(sliderRect.width, width);
assert.equal(sliderRect.height, height);
assert.equal(sliderRect.right, sliderRect.left + width);
assert.equal(sliderRect.bottom, sliderRect.top + height);
});
test('#handleSliderTouchStart: not paused while dragging',
function() {
dom.player.duration = 100;
dom.player.paused = false;
handleSliderTouchStart(event);
assert.isTrue(dragging);
assert.equal(touchStartID, touchStartIdFromEvent,
'touch start id should come from event');
assert.isTrue(handleSliderTouchMoveSpy.calledOnce,
'handleSliderTouchMove is called once');
assert.equal(playerPauseSpy.callCount, 0,
'dom.player.pause is not called');
assert.equal(sliderRect.width, width);
assert.equal(sliderRect.height, height);
assert.equal(sliderRect.right, sliderRect.left + width);
assert.equal(sliderRect.bottom, sliderRect.top + height);
});
});
suite('handleSliderTouchMove flows', function() {
var seekerStub;
var clientX = 110;
var touch = {'clientX': clientX};
var identifiedTouchSuccess = function(touchStartID) {
return touch;
};
var identifiedTouchFailure = function(touchStartID) {
return null;
};
var successEvent = { 'changedTouches':
{'identifiedTouch': identifiedTouchSuccess} };
var failureEvent = { 'changedTouches':
{'identifiedTouch': identifiedTouchFailure} };
suiteSetup(function() {
seekerStub = sinon.stub(seeker, 'seekTo');
sliderRect = {'left': 10,
'width': 200,
'right': 190};
});
setup(function() {
seekerStub.reset();
dragging = true;
dom.playHead.classList.remove('active');
});
suiteTeardown(function() {
seekerStub.restore();
});
test('#handleSliderTouchMove: update the slider (ltr)', function() {
document.documentElement.dir = 'ltr';
dom.playHead.style.left = '';
dom.playHead.style.right = '';
handleSliderTouchMove(successEvent);
assert.isTrue(containsClass(dom.playHead, 'active'));
assert.equal(dom.playHead.style.left, '50%');
assert.equal(dom.playHead.style.right, '');
assert.equal(dom.elapsedTime.style.width, '50%');
assert.isTrue(seekerStub.calledOnce);
});
test('#handleSliderTouchMove: update the slider (rtl)', function() {
document.documentElement.dir = 'rtl';
dom.playHead.style.left = '';
dom.playHead.style.right = '';
handleSliderTouchMove(successEvent);
assert.isTrue(containsClass(dom.playHead, 'active'));
assert.equal(dom.playHead.style.right, '40%');
assert.equal(dom.playHead.style.left, '');
assert.equal(dom.elapsedTime.style.width, '40%');
assert.isTrue(seekerStub.calledOnce);
});
test('#handleSliderTouchMove: not dragging', function() {
dragging = false;
handleSliderTouchMove(successEvent);
assert.isFalse(containsClass(dom.playHead, 'active'));
assert.equal(seekerStub.callCount, 0);
});
test('#handleSliderTouchMove: no touch start event', function() {
dragging = false;
handleSliderTouchMove(failureEvent);
assert.isFalse(containsClass(dom.playHead, 'active'));
assert.equal(seekerStub.callCount, 0);
});
});
suite('handleSliderTouchEnd flows', function() {
var playerPlaySpy;
var playerPauseSpy;
var existingTouchEventId = 1;
var identifiedTouchSuccess = function(touchStartID) {
return true;
};
var identifiedTouchFailure = function(touchStartID) {
return false;
};
var touchEvent = { 'changedTouches':
{'identifiedTouch': identifiedTouchSuccess} };
var unrelatedTouchEvent = { 'changedTouches':
{'identifiedTouch': identifiedTouchFailure} };
suiteSetup(function() {
playerPlaySpy = sinon.spy(dom.player, 'play');
playerPauseSpy = sinon.spy(dom.player, 'pause');
});
setup(function() {
playerPlaySpy.reset();
playerPauseSpy.reset();
dragging = true;
dom.playHead.classList.add('active');
});
suiteTeardown(function() {
playerPlaySpy.restore();
playerPauseSpy.restore();
});
test('#handleSliderTouchEnd: no touch start event', function() {
touchStartID = existingTouchEventId;
handleSliderTouchEnd(unrelatedTouchEvent);
assert.equal(touchStartID, existingTouchEventId);
assert.isTrue(containsClass(dom.playHead, 'active'));
});
test('#handleSliderTouchEnd: not dragging', function() {
touchStartID = existingTouchEventId;
dragging = false;
handleSliderTouchEnd(touchEvent);
assert.isNull(touchStartID);
assert.isTrue(containsClass(dom.playHead, 'active'));
});
test('#handleSliderTouchEnd: video at end', function() {
touchStartID = existingTouchEventId;
dragging = true;
dom.player.currentTime = dom.player.duration = 10;
handleSliderTouchEnd(touchEvent);
assert.isNull(touchStartID);
assert.isFalse(containsClass(dom.playHead, 'active'));
assert.isTrue(playerPauseSpy.calledOnce);
});
test('#handleSliderTouchEnd: video not at end', function() {
touchStartID = existingTouchEventId;
dragging = true;
dom.player.currentTime = 5;
dom.player.duration = 10;
handleSliderTouchEnd(touchEvent);
assert.isNull(touchStartID);
assert.isFalse(containsClass(dom.playHead, 'active'));
assert.equal(playerPauseSpy.callCount, 0);
assert.equal(playerPlaySpy.callCount, 0);
});
});
});