Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2301 lines
76 KiB
JavaScript
Executable file
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);
|
|
});
|
|
});
|
|
});
|