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.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,27 @@
'use strict';
/* jshint node: true, mocha: true */
/* global suiteSetup */
var helper = require('helper');
suite('Video tests', function() {
suiteSetup(helper.cleanupWorkspace);
teardown(helper.cleanupWorkspace);
test('APP=video make', function(done) {
helper.exec('APP=video make', function(error, stdout, stderr) {
helper.checkError(error, stdout, stderr);
var zipPath = process.cwd() +
'/profile/webapps/video.gaiamobile.org/application.zip';
var checkList = ['manifest.webapp'];
checkList.forEach(function(filePath) {
helper.checkFilePathInZip(zipPath, filePath);
});
done();
});
});
});

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"marionette",
"marionetteScriptFinished",
"require",
"define",
"requireApp",
"sinon"
]
}

View file

@ -0,0 +1,67 @@
'use strict';
var Video = require('./lib/video'),
assert = require('assert'),
VideoOptionsView = require('./lib/video_options_view.js'),
VideoInfoView = require('./lib/video_info_view.js');
marionette('video info view', function() {
var client = marionette.client({
profile: {
prefs: {
'device.storage.enabled': true,
'device.storage.testing': true,
'device.storage.prompt.testing': true
}
},
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var app, selectors, optionsView, infoView;
setup(function() {
// Remove all files in temp device storage.
client.fileManager.removeAllFiles();
// Add file into the videos directory
client.fileManager.add({
type: 'videos',
filePath: 'test_media/Movies/elephants-dream.webm'
});
selectors = Video.Selector;
app = new Video(client);
optionsView = new VideoOptionsView(client);
infoView = new VideoInfoView(client);
});
test('open video and check video info', function() {
// tap thumbnail to open video
app.launch();
app.waitForThumbnails(1);
app.tapThumbnail(0);
app.waitForVideoToStartPlaying();
// tap screen to show menu, then tap option
app.tapOption();
assert.equal(optionsView.waitForOptionsViewDisplayed(), true);
// tap more info from options menu
optionsView.tapMoreInfo();
assert.equal(infoView.waitForInfoViewDisplayed(), true);
// check displayed values
assert.equal(infoView.displayedName, 'Elephants-dream');
assert.equal(infoView.displayedLength, '01:58');
assert.equal(infoView.displayedSize, '8.2 MB');
assert.equal(infoView.displayedType, 'webm');
var today = new Date();
assert.equal(infoView.displayedDate, today.getMonth() + 1 +
'/' + today.getDate() + '/' + today.getFullYear());
assert.equal(infoView.displayedResolution, '540x360');
// tap close, go back to the player view, check it still shows the file
infoView.tapClose();
assert.equal(app.waitForControls(), true);
assert.equal(app.videoTitle(),'Elephants-dream');
});
});

View file

@ -0,0 +1,25 @@
/* global Components,Services */
'use strict';
Components.utils.import('resource://gre/modules/Services.jsm');
Services.obs.addObserver(function(document) {
if (!document || !document.location) {
return;
}
var window = document.defaultView.wrappedJSObject;
var mozActivityData = {};
window.__defineGetter__('MozActivity', function() {
return function fakeMozActivity(data) {
mozActivityData = data;
};
});
window.__defineGetter__('getMozActivityData', function() {
return function getMozActivityData() {
return mozActivityData;
};
});
}, 'document-element-inserted', false);

View file

@ -0,0 +1,294 @@
/* global module */
'use strict';
/**
* @constructor
* @param {Marionette.Client} client Marionette client to use.
*/
function Video(client) {
this.client = client.scope({
searchTimeout: 20000
});
}
/**
* @const {string}
*/
Video.ORIGIN = 'app://video.gaiamobile.org';
module.exports = Video;
/**
* @const {Object}
*/
Video.Selector = Object.freeze({
thumbnail: 'li.thumbnail',
fullscreenView: '#player-view',
infoView: '#info-view',
thumbnailsSingleInfoButton: 'a.button.single-info-button',
thumbnailsSingleDeleteButton: 'a.button.single-delete-button',
thumbnailsSingleShareButton: 'a.button.single-share-button',
player: 'video#player',
optionsButton: '#options',
videoTitle: '#video-title',
overlay: '#overlay',
elapsedTime: '#elapsed-text',
durationText: '#duration-text',
thumbnailsSelectButton: '#thumbnails-select-button',
thumbnailSelectTop: '#thumbnail-select-top',
thumbnailsShareButton: '#thumbnails-share-button',
thumbnailsVideoButton: '#thumbnails-video-button',
thumbnailsDeleteButton: '#thumbnails-delete-button',
confirmOkButton: '.modal-dialog-confirm-ok',
playerHeader: '#player-header',
gaiaHeaderActionButton: '.action-button'
});
Video.prototype = {
/**
* Marionette client to use
* @type {Marionette.Client}
*/
client: null,
/**
* @return {Marionette.Element} Container for fullscreen view of an video.
*/
get fullscreenView() {
return this.client.findElement(Video.Selector.fullscreenView);
},
/**
* @return {Marionette.Element} Container for info view of an video.
*/
get infoView() {
return this.client.findElement(Video.Selector.infoView);
},
/**
* @return {Marionette.Element} overlay that displays message load videos
* to get started.
*/
get overlay() {
return this.client.findElement(Video.Selector.overlay);
},
/**
* @return {Marionette.Element} that share videos in selection mode.
*/
get thumbnailsShare() {
return this.client.findElement(Video.Selector.thumbnailsShareButton);
},
/**
* @return {Marionette.Element} that opens camera view.
*/
get thumbnailsVideo() {
return this.client.findElement(Video.Selector.thumbnailsVideoButton);
},
/**
* @return {Marionette.Element} that opens select view.
*/
get thumbnailsSelectButton() {
return this.client.findElement(Video.Selector.thumbnailsSelectButton);
},
/**
* @return {Marionette.Element} that delete videos in selection mode.
*/
get thumbnailsDelete() {
return this.client.findElement(Video.Selector.thumbnailsDeleteButton);
},
/**
* @return {Marionette.Element} to view info about the playing current video.
*/
get thumbnailsSingleInfo() {
return this.client.findElement(Video.Selector.thumbnailsSingleInfoButton);
},
/**
* @return {Marionette.Element} to delete the playing current video.
*/
get thumbnailsSingleDelete() {
return this.client.findElement(Video.Selector.thumbnailsSingleDeleteButton);
},
/**
* @return {Marionette.Element} to share the playing current video.
*/
get thumbnailsSingleShare() {
return this.client.findElement(Video.Selector.thumbnailsSingleShareButton);
},
/**
* @return {Marionette.Element} ok button in confirm delete files dialog.
*/
get confirmOk() {
// selector specific to the out-of-app confirm dialog
// found in System App
return this.client.findElement(Video.Selector.confirmOkButton);
},
/**
* @return {Marionette.Element} First element of all thumbnail images.
*/
get thumbnail() {
return this.client.helper.waitForElement(Video.Selector.thumbnail);
},
/**
* @return {Marionette.Element} video player element.
*/
get player() {
return this.client.helper.waitForElement(Video.Selector.player);
},
/**
* @return {Marionette.Element} video player option button element.
*/
get optionsButton() {
return this.client.helper.waitForElement(Video.Selector.optionsButton);
},
/**
* Called after tapping on a thumbnail to wait for the video to
* begin playing.
*/
waitForVideoToStartPlaying: function() {
var elapsedElem = this.client.findElement(Video.Selector.elapsedTime);
// Ensure when we tap on the player view the video controls are not
// displayed -- they are displayed when the video first plays and then
// they are automatically hidden.
//
// Wait for controls to appear as the video begins to play. Specify
// a timeout to account for the controls having already been displayed
// and auto-hidden.
this.client.waitFor(function() { return elapsedElem.displayed(); },
{ interval: 10, timeout: 5000 }
);
// Either the controls are now displayed or they have already been
// automatically hidden. In the case where they have been displayed,
// wait for them to be automatically hidden.
this.client.waitFor(function() { return !elapsedElem.displayed(); });
// Tap to display the controls and wait for them to be displayed.
this.player.tap();
this.waitForControls();
// Wait for the video to play for at least one second (to allow
// for validation checks).
this.client.waitFor(() => {
return this.getElapsedTimeSeconds() > 0;
});
},
waitForControls: function() {
return this.client.helper.waitForElement(
Video.Selector.elapsedTime).displayed();
},
/**
* Taps 'back' button on gaia-header to return from the video player view
* to the thumbnails view.
*/
back: function() {
var playerHeader = this.client.findElement(Video.Selector.playerHeader);
this.client.switchToShadowRoot(playerHeader);
var actionButton =
this.client.helper.waitForElement(Video.Selector.gaiaHeaderActionButton);
actionButton.tap();
this.client.switchToShadowRoot();
},
getElapsedTime: function() {
var elapsedTimeElem = this.client.findElement(Video.Selector.elapsedTime);
return elapsedTimeElem;
},
getElapsedTimeSeconds: function() {
return parseInt(this.getElapsedTime().text().split(':').pop());
},
videoTitle: function() {
var titleElem =
this.client.findElement(Video.Selector.videoTitle);
return titleElem.text();
},
/**
* @return {Marionette.Element} List of elements of thumbnail videos.
*/
getThumbnails: function() {
return this.client.findElements(Video.Selector.thumbnail);
},
get thumbnails() {
return this.client.findElements(Video.Selector.thumbnail);
},
tapThumbnail: function(n) {
var thumbnail = this.thumbnails[n];
thumbnail.click();
},
waitForThumbnails: function(num) {
var getThumbnails = this.getThumbnails.bind(this);
this.client.helper.waitFor(function() {
return getThumbnails().length === num;
});
},
/**
* Click select button in the video gallery view footer to enter
* selection mode to share or delete multiple videos.
*/
enterSelectionMode: function() {
this.client.helper.waitForElement(Video.Selector.thumbnailsSelectButton)
.click();
this.client.helper.waitForElement(Video.Selector.thumbnailSelectTop);
},
/**
* Click first thumbnail in gallery view to play the video
*/
playVideoFromGalleryView: function() {
this.client.helper.waitForElement(Video.Selector.thumbnail).click();
},
/**
* Select first element of thumbnail videos
* list in selection mode.
*/
selectThumbnail: function() {
var firstItem = this.client.helper.waitForElement(Video.Selector.thumbnail);
firstItem.click();
// Wait for element is shown as selected, otherwise error
this.client.waitFor(function() {
var classList = firstItem.getAttribute('class').split(' ');
return classList.indexOf('selected') > -1;
});
},
/**
* Click option button from player view and wait for button selection
*/
tapOption: function() {
this.optionsButton.click();
},
/**
* Start the Video, save the client for future ops, and wait for the
* Video to finish an initial render.
*/
launch: function() {
this.client.apps.launch(Video.ORIGIN);
this.client.apps.switchToApp(Video.ORIGIN);
// Wait for the document body to know we're really 'launched'.
this.client.helper.waitForElement('body');
}
};

View file

@ -0,0 +1,115 @@
/* global module */
(function(module) {
'use strict';
/**
* @constructor
* @param {Marionette.Client} client Marionette client to use.
*/
function VideoInfoView(client) {
this.client = client.scope({
searchTimeout: 20000
});
}
module.exports = VideoInfoView;
/**
* @const {Object}
*/
VideoInfoView.Selector = Object.freeze({
infoView: '#info-view',
infoName: '#info-name',
infoLength: '#info-length',
infoSize: '#info-size',
infoType: '#info-type',
infoDate: '#info-date',
infoResolution: '#info-resolution',
closeButton: '#info-close-button'
});
VideoInfoView.prototype = {
client: null,
/**
* @return {String} Currently displayed video name text.
*/
get displayedName() {
return this.client.findElement(
VideoInfoView.Selector.infoName).text();
},
/**
* @return {String} Currently displayed video length.
*/
get displayedLength() {
return this.client.findElement(
VideoInfoView.Selector.infoLength).text();
},
/**
* @return {String} Currently displayed video size.
*/
get displayedSize() {
var size = this.client.findElement(
VideoInfoView.Selector.infoSize).text();
return this._trimTextContent(size);
},
/**
* @return {String} Currently displayed video type.
*/
get displayedType() {
return this.client.findElement(
VideoInfoView.Selector.infoType).text();
},
/**
* @return {String} Currently displayed date (taken) for video.
*/
get displayedDate() {
return this.client.findElement(
VideoInfoView.Selector.infoDate).text();
},
/**
* @return {String} Currently displayed video resolution.
*/
get displayedResolution() {
return this.client.findElement(
VideoInfoView.Selector.infoResolution).text();
},
/**
* @return {Marionette.Element} close button.
*/
get closeBtn() {
return this.client.findElement(
VideoInfoView.Selector.closeButton);
},
// strip l10n delimiters
_trimTextContent: function(text) {
// for some reason these unicode delimiters are inserted
// in the text node
return text.trim().replace(/[\u2068\u2069]/g, '');
},
/**
* @return {Boolean} display status of info view.
*/
waitForInfoViewDisplayed: function() {
return this.client.helper.waitForElement(
VideoInfoView.Selector.infoView).displayed();
},
/**
* Close the info view and waits for the player view
*/
tapClose: function() {
this.closeBtn.click();
}
};
})(module);

View file

@ -0,0 +1,89 @@
/* global module */
(function(module) {
'use strict';
/**
* @constructor
* @param {Marionette.Client} client Marionette client to use.
*/
function VideoOptionsView(client) {
this.client = client.scope({
searchTimeout: 20000
});
}
module.exports = VideoOptionsView;
/**
* @const {Object}
*/
VideoOptionsView.Selector = Object.freeze({
optionsView: '#options-view',
shareButton: '.single-share-button',
infoButton: '.single-info-button',
deleteButton: '.single-delete-button',
cancelButton: '.gaia-menu-cancel'
});
VideoOptionsView.prototype = {
client: null,
/**
* @return {Marionette.element} share button object
*/
get shareButton() {
return this.client.findElement(
VideoOptionsView.Selector.shareButton);
},
/**
* @return {Marionette.element} more info button object
*/
get infoButton() {
return this.client.findElement(
VideoOptionsView.Selector.infoButton);
},
/**
* @return {Marionette.element} delete button object
*/
get deleteButton() {
return this.client.findElement(
VideoOptionsView.Selector.deleteButton);
},
/**
* @return {Marionette.element} cancel button object
*/
get cancelButton() {
return this.client.findElement(
VideoOptionsView.Selector.cancelButton);
},
/**
* @return {boolean} the display status of the options view
*/
waitForOptionsViewDisplayed: function() {
return this.client.helper.waitForElement(
VideoOptionsView.Selector.optionsView).displayed();
},
tapShare: function() {
this.shareButton.click();
},
tapMoreInfo: function() {
this.infoButton.click();
},
tapDelete: function() {
this.deleteButton.click();
},
tapCancel: function() {
this.cancelButton.click();
}
};
})(module);

View file

@ -0,0 +1,29 @@
'use strict';
var Video = require('./lib/video');
marionette('video overlay', function() {
var assert = require('assert');
var client = marionette.client({
profile: {
prefs: {
'device.storage.enabled': true,
'device.storage.testing': true,
'device.storage.prompt.testing': true
}
}
});
var app;
setup(function() {
// Remove all files in temp device storage.
client.fileManager.removeAllFiles();
app = new Video(client);
app.launch();
});
test('check for empty overlay', function() {
var overlay = client.helper.waitForElement(Video.Selector.overlay);
assert.ok(overlay.displayed(), 'The list should be empty');
});
});

View file

@ -0,0 +1,68 @@
'use strict';
var assert = require('assert'),
Marionette = require('marionette-client'),
Video = require('./lib/video');
marionette('play various types of videos', function() {
var app, client, actions;
client = marionette.client({
profile: {
prefs: {
'device.storage.enabled': true,
'device.storage.testing': true,
'device.storage.prompt.testing': true
}
},
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
setup(function() {
// Remove all files in temp device storage.
client.fileManager.removeAllFiles();
app = new Video(client);
actions = new Marionette.Actions(client);
});
test ('test various videos', function() {
var types = ['mp4', 'ogg', 'ogv', 'webm'];
types.forEach(function(type) {
var filename = 'test_' + type + '_video.' + type;
client.fileManager.add({
type: 'videos',
filePath: 'apps/video/test/videos/' + filename
});
});
app.launch();
app.waitForThumbnails(types.length);
var thumbnails = app.getThumbnails();
for (var i = 0; i < thumbnails.length; i++) {
app.tapThumbnail(i);
app.waitForVideoToStartPlaying();
// Add some context to the output in case of test failure.
console.log('playing video:', app.videoTitle());
// Verify the video has begun playing and the
// title has been read properly.
assert(app.getElapsedTimeSeconds() > 0);
var expectedTitle = 'Test ' + types[i] + ' video';
assert(app.videoTitle(), expectedTitle);
// Go back to the thumbnail view.
app.back();
// Scroll the thumbnails a bit after each video is played
// to ensure the thumbnails are displayed when they are
// 'tapped'.
actions.flick(thumbnails[i], 0, 0, 0, -30).perform();
}
});
});

View file

@ -0,0 +1,89 @@
/* global __dirname */
'use strict';
var Video = require('./lib/video'),
assert = require('assert');
marionette('video list', function() {
var app, client, selectors;
client = marionette.client({
profile: {
prefs: {
'device.storage.enabled': true,
'device.storage.testing': true,
'device.storage.prompt.testing': true
}
}
});
function getActivityData(client) {
var data;
client.waitFor(function() {
data = client.executeScript(function() {
return window.wrappedJSObject.getMozActivityData();
});
return data;
});
return data;
}
setup(function() {
// inject MozActivity to get activity details
client.contentScript.inject(__dirname +
'/lib/mocks/mock_window_mozactivity.js');
// Remove all files in temp device storage.
client.fileManager.removeAllFiles();
// Add file into the videos directory
client.fileManager.add({
type: 'videos',
filePath: 'test_media/Movies/elephants-dream.webm'
});
selectors = Video.Selector;
app = new Video(client);
app.launch();
client.helper.waitForElement(selectors.thumbnail);
});
test('check video lists', function() {
assert.ok(app.getThumbnails().length > 0, 'the list is not empty');
assert.ok(app.getThumbnails()[0].displayed(),
'first item should be shown');
});
test('open camera app', function() {
app.thumbnailsVideo.click();
var activityObj = getActivityData(client);
assert.equal(
activityObj.name,
'record',
'the activity name should be record.'
);
assert.equal(
activityObj.data.type,
'videos',
'type in data should be videos.'
);
});
/*
test('share video', function() {
app.enterSelectionMode();
app.selectThumbnail();
app.thumbnailsShare.click();
var activityObj = getActivityData(client);
assert.equal(
activityObj.name,
'share',
'the activity name should be share.'
);
assert.equal(activityObj.data.type,
'video/*',
'type data in share activity is video/*.'
);
});
*/
});

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML"
]
}

View file

@ -0,0 +1,65 @@
/* global Captions */
'use strict';
requireApp('/video/js/captions.js');
suite('captions.js unit tests', function() {
var player, captions, realGetDeviceStorage;
var mockVTTFile = new Blob(['fake vtt file'], { type: 'text/vtt' });
var mockGetRequest;
function mockGetDeviceStorage(type) {
return {
get: function(filename) {
mockGetRequest.requestedFilename = filename;
return mockGetRequest;
}
};
}
setup(function() {
player = document.createElement('video');
captions = new Captions(player);
realGetDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = mockGetDeviceStorage;
mockGetRequest = { result: mockVTTFile };
});
teardown(function() {
navigator.getDeviceStorage = realGetDeviceStorage;
});
test('remove() removes track elements from player', function() {
player.appendChild(document.createElement('track'));
player.appendChild(document.createElement('track'));
assert.equal(player.querySelectorAll('track').length, 2);
captions.remove();
assert.equal(player.querySelectorAll('track').length, 0);
});
test('findAndDisplay() looks for a .vtt file', function() {
captions.findAndDisplay('foo/bar.mp4');
assert.equal(mockGetRequest.requestedFilename, 'foo/bar.vtt');
});
test('findAndDisplay() adds a track element if captions exist', function() {
captions.findAndDisplay('foo.mp4');
mockGetRequest.onsuccess(); // A captions file was found
assert.equal(player.querySelectorAll('track').length, 1);
});
test('findAndDisplay() adds a showing track element', function() {
captions.findAndDisplay('foo.mp4');
mockGetRequest.onsuccess(); // A captions file was found
assert.equal(player.querySelector('track').track.mode, 'showing');
});
test('findAndDisplay() adds no track element if no captions', function() {
captions.findAndDisplay('foo.mp4');
if (mockGetRequest.onerror) {
mockGetRequest.onerror(); // No captions file was found
}
assert.equal(player.querySelectorAll('track').length, 0);
});
});

Binary file not shown.

View file

@ -0,0 +1,15 @@
/* exported MockForwardRewindController */
'use strict';
var MockForwardRewindController = (function() {
function init(video, forward, backward) {}
function uninit(video, forward, backward) {}
return {
init: init,
uninit: uninit
};
}());

View file

@ -0,0 +1,32 @@
/* exported MockMediaDB, MediaDB */
'use strict';
var MediaDB;
var MockMediaDB = (function() {
function MockMediaDB(blob) {
this.blob = blob;
this.OPENING = 'opening'; // this is initializing itself
this.UPGRADING = 'upgrading'; // this is upgrading database
this.READY = 'ready'; // this is available and ready for use
this.NOCARD = 'nocard'; // Unavailable because there is no sd card
this.UNMOUNTED = 'unmounted'; // Unavailable because card unmounted
this.CLOSED = 'closed'; // Unavailable because closed() was called
}
MockMediaDB.prototype = {
getFile: function getFile(filename, callback) {
this.blob.name = filename;
callback(this.blob);
},
updateMetadata: function updateMetadata() {
}
};
return MockMediaDB;
}());

View file

@ -0,0 +1,19 @@
/* exported startParsingMetadata, stopParsingMetadata */
/* exported metadataQueue, processingQueue, captureFrame */
'use strict';
var metadataQueue;
var processingQueue;
function startParsingMetadata() {
}
function stopParsingMetadata() {
}
// We need a mock captureFrame function as opposed to simply
// using a spy because the function needs to have an implementation,
// it needs to invoke the callback function.
function captureFrame(player, metadata, callback) {
callback();
}

View file

@ -0,0 +1,48 @@
'use strict';
var MockThumbnailGroup = function(item) {
var counter = 0;
var dummyNode = document.createElement('div');
dummyNode.textContent = MockThumbnailGroup._GroupID;
dummyNode.htmlNode = dummyNode;
dummyNode.updateTitleText = function() {};
function addItem() {
counter++;
return dummyNode;
}
function removeItem() {
counter--;
}
function localize() {}
MockThumbnailGroup._GroupMap[MockThumbnailGroup._GroupID] = {
// api
addItem: addItem,
getCount: function() {return counter;},
removeItem: removeItem,
localize: localize,
// properties.
groupID: MockThumbnailGroup._GroupID,
htmlNode: dummyNode
};
return MockThumbnailGroup._GroupMap[MockThumbnailGroup._GroupID];
};
MockThumbnailGroup.getGroupID = function(video) {
return MockThumbnailGroup._GroupID;
};
MockThumbnailGroup.compareGroupID = function(id1, id2) {
return id1 > id2 ? 1 : (id1 < id2 ? -1 : 0);
};
MockThumbnailGroup._GroupMap = {};
MockThumbnailGroup.reset = function() {
MockThumbnailGroup._GroupMap = {};
delete MockThumbnailGroup._GroupID;
};

View file

@ -0,0 +1,15 @@
/* exported MockThumbnailItem */
'use strict';
var MockThumbnailItem = function(video) {
var dummyNode = document.createElement('div');
dummyNode.textContent = video.date;
var titleMaxLines;
return {
htmlNode: dummyNode,
data: video,
titleMaxLines: titleMaxLines,
updateTitleText: function() {}
};
};

View file

@ -0,0 +1,27 @@
'use strict';
function VideoLoadingChecker(player, overlay, overlayTitle, overlayText) {
this.player = player;
this.overlay = overlay;
this.overlayTitle = overlayTitle;
this.overlayText = overlayText;
this.callback = null;
}
VideoLoadingChecker.prototype = {
ensureVideoLoads: function vpc_ensureVideoLoads(callback) {
this.callback = callback;
},
invokeLoadedMetadataCallback: function vpc_invokeLoadedMetadataCallback() {
if (this.callback) {
this.callback();
}
},
resetLoadedMetadataCallback: function vpc_resetLoadedMetadataCallback() {
this.callback = null;
},
ensureVideoPlays: function() {},
cancelEnsureVideoPlays: function() {},
};

View file

@ -0,0 +1,70 @@
'use strict';
function MockVideoPlayer() {
MockVideoPlayer.instances.push(this);
this.src = null;
this.readyState = 0;
this.seeking = false;
this.duration = 0;
this.currentTime = 0;
this.classList = [];
}
MockVideoPlayer.instances = [];
MockVideoPlayer.mSetup = function() {
MockVideoPlayer.instances = [];
};
MockVideoPlayer.mTeardown = function() {
MockVideoPlayer.instances = [];
};
MockVideoPlayer.prototype.play = function() {
};
MockVideoPlayer.prototype.pause = function() {
};
MockVideoPlayer.prototype.load = function() {
};
MockVideoPlayer.prototype.setSeeking = function(seeking) {
this.seeking = seeking;
};
MockVideoPlayer.prototype.setDuration = function(duration) {
this.duration = duration;
};
MockVideoPlayer.prototype.hasAttribute = function(attr) {
if (attr === 'src') {
return (this.src !== undefined && this.src !== null);
}
return (this.classList.indexOf(attr) !== -1);
};
MockVideoPlayer.prototype.removeAttribute = function(attr) {
if (attr === 'src') {
this.src = null;
}
else {
var index = this.classList.indexOf(attr);
if (index >= 0) {
this.classList.splice(index, 1);
}
}
};
MockVideoPlayer.prototype.fastSeek = function(seekTime) {
this.currentTime = seekTime;
};
MockVideoPlayer.prototype.cloneNode = function() {
return this;
};
MockVideoPlayer.prototype.querySelectorAll = function(selector) {
return [];
};

View file

@ -0,0 +1,136 @@
/* global Seeker, performance */
'use strict';
requireApp('/video/js/seeker.js');
// We need a simple mock <video> element for these testsx
function MockVideo() {
this.fastSeek = sinon.spy();
this.seekedHandlers = [];
this.addEventListener = function(type, handler) {
if (type === 'seeked') {
this.seekedHandlers.push(handler);
}
};
this._fireSeekedEvent = function() {
this.seekedHandlers.forEach((f) => {
typeof f === 'object' ? f.handleEvent() : f();
});
};
}
suite('seeker.js unit tests', function() {
var player, seeker;
setup(function() {
player = new MockVideo();
seeker = new Seeker(player);
});
test('it calls fastSeek()', function() {
seeker.seekTo(1);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
});
test('it doesn\'t seek twice to same spot', function() {
seeker.seekTo(1);
seeker.seekTo(1);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
});
test('it defers rapidly repeated seeks', function() {
seeker.seekTo(1);
seeker.seekTo(2);
seeker.seekTo(3);
seeker.seekTo(4);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(4));
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledTwice);
});
test('it does not defer seeks if previous seek has ended', function() {
seeker.seekTo(1);
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
seeker.seekTo(2);
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(2));
player._fireSeekedEvent();
seeker.seekTo(3);
assert.ok(player.fastSeek.calledThrice);
assert.ok(player.fastSeek.calledWith(3));
});
test('it does not defer seeks to 0', function() {
seeker.seekTo(4);
seeker.seekTo(3);
seeker.seekTo(2);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(4));
seeker.seekTo(0);
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(0));
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledTwice);
});
test('it does not defer long-distance seeks', function() {
seeker.seekTo(1);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
Seeker.SEEK_DISTANCE = 5;
seeker.seekTo(7);
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(7));
Seeker.SEEK_DISTANCE = 50;
seeker.seekTo(17); // not far enough, it gets deferred
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(7));
seeker.seekTo(58);
assert.ok(player.fastSeek.calledThrice);
assert.ok(player.fastSeek.calledWith(58));
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledThrice);
});
test('it does not defer infrequent seeks', function() {
// We need to fake out performance.now() for this test
var realPerformanceNow = performance.now;
var time = 0;
performance.now = function() { return time; };
try {
seeker.seekTo(1);
assert.ok(player.fastSeek.calledOnce);
assert.ok(player.fastSeek.calledWith(1));
Seeker.SEEK_INTERVAL = 10;
time = 12;
seeker.seekTo(2);
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(2));
Seeker.SEEK_INTERVAL = 100;
time = 50;
seeker.seekTo(3); // not enough fake time has passed, this is deferred
assert.ok(player.fastSeek.calledTwice);
assert.ok(player.fastSeek.calledWith(2));
time = 113;
seeker.seekTo(4);
assert.ok(player.fastSeek.calledThrice);
assert.ok(player.fastSeek.calledWith(4));
player._fireSeekedEvent();
assert.ok(player.fastSeek.calledThrice);
}
finally {
performance.now = realPerformanceNow;
}
});
});

View file

@ -0,0 +1,3 @@
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');

View file

@ -0,0 +1,181 @@
/* global ThumbnailItem: true,MockThumbnailItem,MockL10n,
ThumbnailDateGroup,MockIntlHelper */
/*
* Thumbnail Date Group tests
*/
'use strict';
require('/shared/js/sanitizer.js');
require('/shared/js/media/media_utils.js');
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/mocks/mock_intl_helper.js');
requireApp('/video/test/unit/mock_thumbnail_item.js');
requireApp('/video/js/thumbnail_date_group.js');
requireApp('/video/js/thumbnail_item.js');
suite('Thumbnail Date Group Unit Tests', function() {
var nativeMozL10n;
var nativeThumbnailItem;
var nativeIntlHelper;
suiteSetup(function() {
nativeThumbnailItem = ThumbnailItem;
nativeMozL10n = document.l10n;
document.l10n = MockL10n;
nativeIntlHelper = window.IntlHelper;
window.IntlHelper = MockIntlHelper;
ThumbnailItem = MockThumbnailItem;
window.IntlHelper.define('date-group', 'datetime', {
month: 'long',
year: 'numeric',
});
});
suiteTeardown(function() {
document.l10n = nativeMozL10n;
window.IntlHelper = nativeIntlHelper;
ThumbnailItem = nativeThumbnailItem;
});
suite('#static functions', function() {
test('#getGroupID', function() {
assert.equal(ThumbnailDateGroup.getGroupID({date: 1375873140000}),
'group_2013-08');
assert.equal(ThumbnailDateGroup.getGroupID({date: 1381967318275}),
'group_2013-10');
});
test('#compareGroupID', function() {
assert.equal(ThumbnailDateGroup.compareGroupID('group_2013-08',
'group_2013-08'),
0);
assert.equal(ThumbnailDateGroup.compareGroupID('group_2013-08',
'group_2013-07'),
1);
assert.equal(ThumbnailDateGroup.compareGroupID('group_2013-08',
'group_2013-09'),
-1);
assert.equal(ThumbnailDateGroup.compareGroupID('group_2013-09',
'group_2013-10'),
-1);
});
});
suite('#test object creation', function() {
test('#empty video object', function() {
try {
new ThumbnailDateGroup(); // jshint ignore:line
assert.fail('undefined or null videoitem should not be ok.');
} catch (ex) {
assert.ok('correct behavior caught');
}
});
});
suite('#rendering', function() {
var dateGroup;
var videoData;
var domNode;
suiteSetup(function() {
videoData = {date: 1375873140000};
dateGroup = new ThumbnailDateGroup(videoData, 'test-id');
domNode = dateGroup.htmlNode;
});
test('#normal', function() {
assert.equal(domNode.firstElementChild.textContent, 'August 2013');
});
});
suite('#API', function() {
var dateGroup;
var videoData;
var domNode;
var groupContainerNode;
setup(function() {
videoData = {date: 1375873140000};
dateGroup = new ThumbnailDateGroup(videoData);
domNode = dateGroup.htmlNode;
groupContainerNode = domNode.querySelector('.thumbnail-group-container');
});
test('#addItem, simple', function() {
assert.isUndefined(dateGroup.addItem());
var thumbnail = dateGroup.addItem({date: 1375873140005});
assert.isDefined(thumbnail);
assert.equal(dateGroup.getCount(), 1);
assert.equal(groupContainerNode.children[0], thumbnail.htmlNode);
});
test('#addItem, first, middle, last', function() {
// addItem, to empty
var thumbnail2 = dateGroup.addItem({date: 1375873140003});
assert.equal(dateGroup.getCount(), 1);
assert.equal(groupContainerNode.children[0], thumbnail2.htmlNode);
// addItem, to first
var thumbnail1 = dateGroup.addItem({date: 1375873140004});
assert.equal(dateGroup.getCount(), 2);
assert.equal(groupContainerNode.children[0], thumbnail1.htmlNode);
assert.equal(groupContainerNode.children[1], thumbnail2.htmlNode);
// addItem, to last
var thumbnail4 = dateGroup.addItem({date: 1375873140001});
assert.equal(dateGroup.getCount(), 3);
assert.isNull(thumbnail4.htmlNode.nextSibling);
assert.equal(groupContainerNode.children[2], thumbnail4.htmlNode);
// addItem to middle
var thumbnail3 = dateGroup.addItem({date: 1375873140002});
assert.equal(dateGroup.getCount(), 4);
assert.equal(groupContainerNode.children[2], thumbnail3.htmlNode);
assert.equal(groupContainerNode.children[3], thumbnail4.htmlNode);
// check from dummyContainer
assert.equal(groupContainerNode.children.length, 4);
});
test('#groupID', function() {
assert.equal(dateGroup.groupID, 'group_2013-08');
});
test('#htmlNode', function() {
assert.equal(dateGroup.htmlNode, domNode);
});
test('#removeItem, unknown thumbnail', function() {
dateGroup.addItem({date: 1375873140004});
dateGroup.addItem({date: 1375873140003});
var dummyThumbnail = new ThumbnailItem({});
dateGroup.removeItem(dummyThumbnail);
assert.equal(dateGroup.getCount(), 2);
});
test('#removeItem, remove all', function() {
var thumbnail1 = dateGroup.addItem({date: 1375873140004});
var thumbnail2 = dateGroup.addItem({date: 1375873140003});
var thumbnail3 = dateGroup.addItem({date: 1375873140002});
var thumbnail4 = dateGroup.addItem({date: 1375873140001});
dateGroup.removeItem(thumbnail1);
assert.equal(dateGroup.getCount(), 3);
assert.equal(groupContainerNode.children[0], thumbnail2.htmlNode);
assert.isUndefined(groupContainerNode.children[3]);
dateGroup.removeItem(thumbnail4);
assert.equal(dateGroup.getCount(), 2);
assert.equal(groupContainerNode.children[1], thumbnail3.htmlNode);
assert.isUndefined(groupContainerNode.children[2]);
dateGroup.removeItem(thumbnail3);
assert.equal(dateGroup.getCount(), 1);
assert.equal(groupContainerNode.children[0], thumbnail2.htmlNode);
assert.isUndefined(groupContainerNode.children[1]);
dateGroup.removeItem(thumbnail2);
assert.equal(dateGroup.getCount(), 0);
assert.isUndefined(groupContainerNode.children[0]);
});
});
});

View file

@ -0,0 +1,212 @@
/* global MockL10n,ThumbnailItem,l10nAssert */
/*
* Thumbnail Item tests
*/
'use strict';
require('/shared/js/sanitizer.js');
require('/shared/js/media/media_utils.js');
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('/video/js/thumbnail_item.js');
suite('Thumbnail Item Unit Tests', function() {
var nativeMozL10n;
suiteSetup(function() {
nativeMozL10n = document.l10n;
document.l10n = MockL10n;
});
suiteTeardown(function() {
document.l10n = nativeMozL10n;
});
suite('#test object creation', function() {
test('#empty video object', function() {
try {
new ThumbnailItem(); // jshint ignore:line
assert.fail('undefined or null videoitem should not be ok.');
} catch (ex) {
assert.ok('correct behavior caught');
}
});
});
suite('#error handling', function() {
var videodata;
suiteSetup(function() {
videodata = {
metadata: {
title: 'Small webm',
duration: 5.568,
width: 560,
height: 320,
watched: false
},
type: 'video/webm',
date: 1375873140000,
size: 229455,
name: 'dummy-file-name'
};
});
test('#partial fields of template inexistent', function() {
var thumbnail = new ThumbnailItem(videodata);
thumbnail.updatePoster(new Blob(['empty-image'], {'type': 'image/jpeg'}));
assert.ok('it is ok without any fields');
});
});
suite('#rendering', function() {
var videodata;
var thumbnail;
var domNode;
suiteSetup(function() {
videodata = {
metadata: {
title: 'Small webm',
duration: 5.568,
width: 560,
height: 320,
watched: false
},
type: 'video/webm',
date: 1375873140000,
size: 229455,
name: 'dummy-file-name'
};
thumbnail = new ThumbnailItem(videodata);
domNode = thumbnail.htmlNode;
});
test('#unwatched', function() {
assert.isDefined(domNode);
var unwatchedNode = domNode.querySelector('.unwatched');
// it is is unwatched now.
assert.isFalse(unwatchedNode.hidden);
});
test('#title', function() {
var titleNode = domNode.querySelector('.title');
assert.equal(titleNode.textContent, 'Small webm');
});
test('#duration-text', function() {
var durationNode = domNode.querySelector('.duration-text');
assert.equal(durationNode.textContent, '00:06');
});
test('#size-text', function() {
var sizeNode = domNode.querySelector('.size-text');
l10nAssert(
sizeNode,
'fileSize',
{size: 224, unit: 'KB'}
);
});
test('#type-text', function() {
var typeNode = domNode.querySelector('.type-text');
assert.equal(typeNode.textContent, 'webm');
});
});
suite('#api tests', function() {
var videodata;
var thumbnail;
var domNode;
suiteSetup(function() {
videodata = {
metadata: {
title: 'Small webm',
duration: 5.568,
width: 560,
height: 320,
watched: false,
poster: new Blob(['empty-image'], {'type': 'image/jpeg'})
},
type: 'video/webm',
date: 1375873140000,
size: 229455,
name: 'dummy-file-name'
};
thumbnail = new ThumbnailItem(videodata);
domNode = thumbnail.htmlNode;
});
test('#addTapListener and fired', function(done) {
function testFunc(video) {
assert.deepEqual(video, videodata);
thumbnail.removeTapListener(testFunc);
done();
}
thumbnail.addTapListener(testFunc);
domNode.click();
});
test('#removeTapListener and fired', function(done) {
function testFunc(video) {
assert.fail('event should not fired.');
}
thumbnail.addTapListener(testFunc);
thumbnail.removeTapListener(testFunc);
domNode.click();
window.setTimeout(done);
});
test('#addTapListener, empty argument', function() {
thumbnail.addTapListener();
assert.ok('everything should be ok');
});
test('#removeTapListener, empty argument', function() {
thumbnail.removeTapListener();
assert.ok('everything should be ok');
});
test('#addTapListener, object listener', function(done) {
function testFunc(video) {
assert.deepEqual(video, videodata);
thumbnail.removeTapListener(testFunc);
done();
}
thumbnail.addTapListener({handleEvent: testFunc});
domNode.click();
});
test('#overflow', function() {
var evt = new Event('overflow');
thumbnail.detailNode.dispatchEvent(evt);
assert.ok('everything should be ok');
});
test('#htmlNode', function() {
assert.equal(thumbnail.htmlNode, domNode);
});
test('#data', function() {
assert.deepEqual(thumbnail.data, videodata);
});
test('#watched', function() {
thumbnail.setWatched(false);
var node = domNode.querySelector('.unwatched');
assert.isFalse(node.hidden);
thumbnail.setWatched(true);
assert.isTrue(node.hidden);
});
test('#updatePoster', function() {
var blob = new Blob(['empty-image'], {'type': 'image/jpeg'});
thumbnail.updatePoster(blob);
var node = domNode.querySelector('.img');
assert.notEqual(node.src, '');
thumbnail.updatePoster(null);
assert.ok(node.src.endsWith('style/images/default_thumbnail.png'));
});
});
});

View file

@ -0,0 +1,164 @@
/* global MockL10n,MockThumbnailGroup,ThumbnailList */
/*
* Thumbnail List tests
*/
'use strict';
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('/video/test/unit/mock_thumbnail_group.js');
requireApp('/video/js/thumbnail_list.js');
suite('Thumbnail List Unit Tests', function() {
var realL10n;
suiteSetup(function() {
realL10n = document.l10n;
document.l10n = MockL10n;
});
suiteTeardown(function() {
document.l10n = realL10n;
});
suite('#constructor tests', function() {
var dummyContainer;
suiteSetup(function() {
MockThumbnailGroup.reset();
dummyContainer = document.createElement('div');
});
test('normal', function() {
new ThumbnailList(MockThumbnailGroup, dummyContainer); //jshint ignore:line
assert.ok('everything should be ok');
});
test('missing arguments', function() {
try {
new ThumbnailList(MockThumbnailGroup); //jshint ignore:line
assert.fail('it should throws an error.');
} catch (ex) {
assert.ok('everything should be ok');
}
try {
new ThumbnailList(); //jshint ignore:line
assert.fail('it should throws an error.');
} catch (ex) {
assert.ok('everything should be ok');
}
});
});
suite('#api tests', function() {
var dummyContainer;
var thumbnailList;
suiteSetup(function() {
MockThumbnailGroup.reset();
dummyContainer = document.createElement('div');
// we use mock thumbnail group which will not add any thumbnail item to
// container.
thumbnailList = new ThumbnailList(MockThumbnailGroup, dummyContainer);
});
test('#addItem, simple', function() {
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.addItem({'name': 'dummy-file-name-07.3gp'});
assert.equal(dummyContainer.firstChild.textContent, '2013-07');
assert.equal(thumbnailList.count, 1);
});
test('#addItem, first, middle, last', function() {
// first
MockThumbnailGroup._GroupID = '2013-09';
thumbnailList.addItem({'name': 'dummy-file-name-09.3gp'});
assert.equal(dummyContainer.firstChild.textContent, '2013-09');
assert.equal(thumbnailList.count, 2);
// middle
MockThumbnailGroup._GroupID = '2013-08';
thumbnailList.addItem({'name': 'dummy-file-name-08.3gp'});
assert.equal(dummyContainer.children[1].textContent, '2013-08');
assert.equal(thumbnailList.count, 3);
// last
MockThumbnailGroup._GroupID = '2013-06';
thumbnailList.addItem({'name': 'dummy-file-name-06.3gp'});
assert.equal(dummyContainer.children[3].textContent, '2013-06');
assert.equal(thumbnailList.count, 4);
});
test('#addItem, to existed group', function() {
// first
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.addItem({'name': 'dummy-file-name-07-2.3gp'});
assert.equal(thumbnailList.count, 5);
assert.equal(MockThumbnailGroup._GroupMap['2013-07'].getCount(), 2);
assert.equal(dummyContainer.children.length, 4);
});
test('#addItem, empty', function() {
// first
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.addItem(null);
assert.equal(thumbnailList.count, 5);
assert.equal(MockThumbnailGroup._GroupMap['2013-07'].getCount(), 2);
assert.equal(dummyContainer.children.length, 4);
});
test('#addItem, duplicated', function() {
// first
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.addItem({'name': 'dummy-file-name-07-2.3gp'});
assert.equal(thumbnailList.count, 5);
assert.equal(MockThumbnailGroup._GroupMap['2013-07'].getCount(), 2);
assert.equal(dummyContainer.children.length, 4);
});
test('#removeItem', function() {
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.removeItem('dummy-file-name-07-2.3gp');
assert.equal(thumbnailList.count, 4);
assert.equal(MockThumbnailGroup._GroupMap['2013-07'].getCount(), 1);
thumbnailList.removeItem('dummy-file-name-07.3gp');
assert.equal(thumbnailList.count, 3);
assert.equal(MockThumbnailGroup._GroupMap['2013-07'].getCount(), 0);
assert.equal(dummyContainer.children.length, 3);
});
test('#removeItem, empty', function() {
MockThumbnailGroup._GroupID = '2013-07';
thumbnailList.removeItem(null);
assert.equal(thumbnailList.count, 3);
});
test('#thumbnailMap', function() {
assert.isDefined(thumbnailList.thumbnailMap['dummy-file-name-06.3gp']);
});
test('#pick mode', function() {
thumbnailList.setPickMode(true);
assert.isTrue(thumbnailList.isPickMode());
assert.isTrue(dummyContainer.classList.contains('pick'));
thumbnailList.setPickMode(false);
assert.isFalse(thumbnailList.isPickMode());
assert.isFalse(dummyContainer.classList.contains('pick'));
});
test('#select mode', function() {
thumbnailList.setSelectMode(true);
assert.isTrue(thumbnailList.isSelectMode());
assert.isTrue(dummyContainer.classList.contains('select'));
thumbnailList.setSelectMode(false);
assert.isFalse(thumbnailList.isSelectMode());
assert.isFalse(dummyContainer.classList.contains('select'));
});
test('#reset', function() {
thumbnailList.reset();
assert.equal(thumbnailList.count, 0);
assert.isUndefined(thumbnailList.thumbnailMap['dummy-file-name-06.3gp']);
});
});
});

View file

@ -0,0 +1,143 @@
/* globals MockVideoPlayer, ForwardRewindController:true */
'use strict';
requireApp('/video/test/unit/mock_video_player.js');
requireApp('/video/js/forward_rewind_controller.js');
var pause;
suite('Video forward rewind Unit Tests', function() {
var seekForward;
var seekBackward;
var play;
var videoToolBar;
var player;
var videoDuration = 50;
var pauseSpy;
var fakeTimer;
suiteSetup(function() {
videoToolBar = document.createElement('div');
seekForward = document.createElement('button');
seekBackward = document.createElement('button');
play = document.createElement('button');
videoToolBar.appendChild(seekBackward);
videoToolBar.appendChild(play);
videoToolBar.appendChild(seekForward);
player = new MockVideoPlayer();
player.setDuration(videoDuration);
ForwardRewindController.init(player, seekForward,
seekBackward);
pauseSpy = sinon.spy();
pause = pauseSpy;
fakeTimer = sinon.useFakeTimers();
});
suiteTeardown(function() {
ForwardRewindController.uninit(player, seekForward,
seekBackward);
});
suite('#Video Forward/Rewind Test', function() {
var simulateClick = function(button) {
var evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
button.dispatchEvent(evt);
};
var simulateContextMenu = function(button) {
var ev = document.createEvent('MouseEvents');
ev.initMouseEvent('contextmenu', true, false, window, 0, 0, 0, 0, 0,
false, false, false, false, 1, null);
button.dispatchEvent(ev);
};
setup(function() {
pauseSpy.reset();
});
test('#Test Playing Video Forward Button Tap Test', function() {
simulateClick(seekForward);
assert.equal(player.currentTime, 10);
});
test('#Test Playing Video Rewind Button Tap Test', function() {
simulateClick(seekBackward);
assert.equal(player.currentTime, 0);
});
test('#Test Playing Video Play Button Tap Test', function() {
simulateClick(play);
simulateClick(seekForward);
assert.equal(player.currentTime, 10);
simulateClick(seekBackward);
assert.equal(player.currentTime, 0);
});
test('#Test Playing Video Play Button Tap Test, wrap', function() {
player.currentTime = videoDuration - 5;
simulateClick(play);
simulateClick(seekForward);
assert.equal(player.currentTime, videoDuration);
assert.isTrue(pauseSpy.calledOnce);
});
test('#Test Playing Video Rewind Button Tap Test, wrap', function() {
player.currentTime = 5;
simulateClick(play);
simulateClick(seekBackward);
assert.equal(player.currentTime, 0);
assert.equal(pauseSpy.callCount, 0);
});
test('#Test Playing Video Forward Button Hold Test', function() {
var currentTime = player.currentTime;
simulateContextMenu(seekForward);
fakeTimer.tick(1500);
ForwardRewindController.stopFastSeeking();
assert.equal(player.currentTime, currentTime + 10);
assert.equal(pauseSpy.callCount, 0);
});
test('#Test Playing Video Forward Button Hold Test, wrap', function() {
player.currentTime = videoDuration - 5;
simulateContextMenu(seekForward);
fakeTimer.tick(1500);
ForwardRewindController.stopFastSeeking();
assert.equal(player.currentTime, videoDuration);
assert.isTrue(pauseSpy.calledOnce);
});
test('#Test Playing Video Rewind Button Hold Test', function() {
player.currentTime = 20;
simulateContextMenu(seekBackward);
fakeTimer.tick(1500);
ForwardRewindController.stopFastSeeking();
assert.equal(player.currentTime, 10);
assert.equal(pauseSpy.callCount, 0);
});
test('#Test Playing Video Rewind Button Hold Test, wrap', function() {
player.currentTime = 5;
simulateContextMenu(seekBackward);
fakeTimer.tick(1500);
ForwardRewindController.stopFastSeeking();
assert.equal(player.currentTime, 0);
assert.equal(pauseSpy.callCount, 0);
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,303 @@
/* global VideoUtils */
'use strict';
requireApp('/video/js/video_utils.js');
suite('Video Utils Unit Tests', function() {
suite('#fitContiner tests - 4:3 video fit HVGA', function() {
test('fit 640x480 to HVGA, 0deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,120.0000px) scale(0.5000)');
});
test('fit 640x480 to HVGA, 90deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 90);
assert.equal(testPlayer.style.transform,
'translate(320.0000px,26.6667px) rotate(90deg) scale(0.6667)');
});
test('fit 640x480 to HVGA, 180deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 180);
assert.equal(testPlayer.style.transform,
'translate(320.0000px,360.0000px) rotate(180deg) scale(0.5000)');
});
test('fit 640x480 to HVGA, 270deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 270);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,453.3333px) rotate(270deg) scale(0.6667)');
});
test('fit 640x480 to HVGA landscape, 0deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 480, clientHeight: 320},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(26.6667px,0.0000px) scale(0.6667)');
});
test('fit 640x480 to HVGA landscape, 90deg', function() {
var testPlayer = {videoWidth: 640, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 480, clientHeight: 320},
testPlayer, 90);
assert.equal(testPlayer.style.transform,
'translate(360.0000px,0.0000px) rotate(90deg) scale(0.5000)');
});
test('fit 320x240 to HVGA, 0deg, no scale', function() {
var testPlayer = {videoWidth: 320, videoHeight: 240, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,120.0000px) scale(1.0000)');
});
test('fit 240x180 to HVGA, 0deg, scale up', function() {
var testPlayer = {videoWidth: 240, videoHeight: 180, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,120.0000px) scale(1.3333)');
});
test('fit 320x480 to HVGA, 0deg, 100% fit', function() {
var testPlayer = {videoWidth: 320, videoHeight: 480, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,0.0000px) scale(1.0000)');
});
});
suite('#fitContiner tests - 16:9 video fit HVGA', function() {
test('fit 720p to HVGA, 0deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,150.0000px) scale(0.2500)');
});
test('fit 720p to HVGA, 90deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 90);
assert.equal(testPlayer.style.transform,
'translate(295.0000px,0.0000px) rotate(90deg) scale(0.3750)');
});
test('fit 720p to HVGA, 180deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 180);
assert.equal(testPlayer.style.transform,
'translate(320.0000px,330.0000px) rotate(180deg) scale(0.2500)');
});
test('fit 720p to HVGA, 270deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 320, clientHeight: 480},
testPlayer, 270);
assert.equal(testPlayer.style.transform,
'translate(25.0000px,480.0000px) rotate(270deg) scale(0.3750)');
});
test('fit 720p to HVGA landscape, 0deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 480, clientHeight: 320},
testPlayer, 0);
assert.equal(testPlayer.style.transform,
'translate(0.0000px,25.0000px) scale(0.3750)');
});
test('fit 720p to HVGA landscape, 90deg', function() {
var testPlayer = {videoWidth: 1280, videoHeight: 720, style: {}};
VideoUtils.fitContainer({clientWidth: 480, clientHeight: 320},
testPlayer, 90);
assert.equal(testPlayer.style.transform,
'translate(330.0000px,0.0000px) rotate(90deg) scale(0.2500)');
});
});
suite('getTruncated tests', function() {
var _clientHeight;
var titleNode;
var inputTitleText;
suiteSetup(function() {
titleNode = document.createElement('span');
// override native clientHeight
_clientHeight = Object.getOwnPropertyDescriptor(
Element.prototype, 'clientHeight');
Object.defineProperty(Element.prototype, 'clientHeight', {
configurable: true,
enumerable: true,
get: function videoUtilsTestClientHeightStub() {
function getPaddingInt(paddingInput) {
var paddingInt = parseInt(paddingInput, 10);
return Number.isInteger(paddingInt) ? paddingInt : 0;
}
//
// the video_utils getTruncated function uses clientHeight
// to determine the height of the title text. clientHeight
// includes padding, therfore, we have tests that set
// padding on the title node to test that getTruncated
// filters out the padding when determine the line height.
//
var padding = getPaddingInt(this.style.paddingTop) +
getPaddingInt(this.style.paddingBottom);
if (this.textContent.length >= 60) {
return 6 + padding;
}
if (this.textContent.length >= 50) {
return 5 + padding;
}
if (this.textContent.length >= 40) {
return 4 + padding;
}
if (this.textContent.length >= 30) {
return 3 + padding;
}
if (this.textContent.length >= 20) {
return 2 + padding;
}
if (this.textContent === '.') {
return 1 + padding;
}
return 1 + padding;
}
});
});
suiteTeardown(function() {
Object.defineProperty(Element.prototype, 'clientHeight', _clientHeight);
});
setup(function() {
titleNode.style.visibility = 'visible';
titleNode.style.wordBreak = 'normal';
titleNode.style.paddingTop = '';
titleNode.style.paddingBottom = '';
});
test('title fits within max lines, maxLine specified', function() {
inputTitleText = '1111111111222222222233333333334444444444';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode,
maxLine: 4
});
assert.equal(inputTitleText, truncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('title fits within max lines, maxLine specified, padding', function() {
inputTitleText = '1111111111222222222233333333334444444444';
titleNode.style.paddingTop = '4px';
titleNode.style.paddingBottom = '4px';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode,
maxLine: 4
});
assert.equal(inputTitleText, truncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('title fits within max lines, maxLine not specified', function() {
inputTitleText = '11111111112222222222';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode
});
assert.equal(inputTitleText, truncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('title doesnt fit, maxLine not specified', function() {
inputTitleText = '111111111122222222223333333333';
var expectedTruncatedTitle = '11111111112222222222333333...';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode
});
assert.equal(truncatedTitle, expectedTruncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('title doesnt fit, maxLine specified', function() {
inputTitleText = '1111111111222222222233333333334444444444';
var expectedTruncatedTitle = '111111111122222222223333333333444444...';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode,
maxLine: 3
});
assert.equal(truncatedTitle, expectedTruncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('title doesnt fit, maxLine specified, padding', function() {
titleNode.style.paddingTop = '4px';
titleNode.style.paddingBottom = '4px';
inputTitleText = '1111111111222222222233333333334444444444';
var expectedTruncatedTitle = '111111111122222222223333333333444444...';
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: titleNode,
maxLine: 3
});
assert.equal(truncatedTitle, expectedTruncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
test('node is null', function() {
var node = null;
var inputTitleText;
var truncatedTitle = VideoUtils.getTruncated(inputTitleText,
{
node: node,
maxLine: 4,
ellipsisIndex: 0
});
assert.equal(inputTitleText, truncatedTitle);
assert.equal(titleNode.style.visibility, 'visible');
assert.equal(titleNode.style.wordBreak, 'normal');
});
});
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.