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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
27
gaia/dev_apps/music-oga/test/build/integration/music_test.js
Executable file
27
gaia/dev_apps/music-oga/test/build/integration/music_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Music tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=music make', function(done) {
|
||||
helper.exec('APP=music make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/music-oga.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
298
gaia/dev_apps/music-oga/test/marionette/Player_test.js
Executable file
298
gaia/dev_apps/music-oga/test/marionette/Player_test.js
Executable file
|
|
@ -0,0 +1,298 @@
|
|||
/* global require, marionette, setup, suite, test, __dirname */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Music = require('./lib/music.js');
|
||||
var FakeRingtones = require('./lib/fakeringtones.js');
|
||||
var FakeControls = require('./lib/fakecontrols.js');
|
||||
var Statusbar = require('./lib/statusbar.js');
|
||||
var PlayerHelper = require('./lib/playerhelper.js');
|
||||
|
||||
marionette('Music player tests', function() {
|
||||
var apps = {};
|
||||
apps[FakeRingtones.DEFAULT_ORIGIN] = __dirname + '/fakeringtones';
|
||||
apps[FakeControls.DEFAULT_ORIGIN] = __dirname + '/fakecontrols';
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
apps: apps
|
||||
},
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var music;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
// Album = 'A Minute With Brendan'
|
||||
// Artist = 'Minute With'
|
||||
// Title = 'Boot To Gecko (B2G)'
|
||||
{ type: 'music', filePath: 'test_media/samples/Music/b2g.ogg' }
|
||||
]);
|
||||
});
|
||||
|
||||
suite('Audio channels tests', function() {
|
||||
var ringtones, statusbar;
|
||||
setup(function() {
|
||||
ringtones = new FakeRingtones(client);
|
||||
statusbar = new Statusbar(client);
|
||||
});
|
||||
|
||||
// This test is skipped for not failing tpbl, please see bug 997360.
|
||||
test.skip('Interrupted by a higher priority channel', function() {
|
||||
// Launch Music app and wait for the first tile to come out. Switch to
|
||||
// the all songs view then tap on the first song to play, also make sure
|
||||
// the playing icon in the status bar shows up.
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
music.playFirstSong();
|
||||
statusbar.waitForPlayingIndicatorShown(true);
|
||||
|
||||
// Launch the fake ringtones app to interrupt the music app.
|
||||
// Make sure the playing icon in the status bar disappears.
|
||||
ringtones.launch();
|
||||
statusbar.waitForPlayingIndicatorShown(false);
|
||||
|
||||
// Close the fake ringtones app so that the playing icon shows up again
|
||||
// because the channel of music app is no more interrupted.
|
||||
ringtones.close();
|
||||
statusbar.waitForPlayingIndicatorShown(true);
|
||||
|
||||
// Bring the music app to foreground then tap the play button, see if the
|
||||
// play button is still functioning to pause the player. Also check the
|
||||
// style of the play button is PAUSED, and the playing icon disappears.
|
||||
music.switchToMe();
|
||||
music.tapPlayButton();
|
||||
assert.equal(music.isPlaying, false);
|
||||
statusbar.waitForPlayingIndicatorShown(false);
|
||||
|
||||
// Bring the music app to foreground then tap the play button, see if the
|
||||
// play button is still functioning to resume the player. Also check the
|
||||
// style of the play button is PLAYING, and the playing icon shows up.
|
||||
music.switchToMe();
|
||||
music.tapPlayButton();
|
||||
assert.equal(music.isPlaying, true);
|
||||
statusbar.waitForPlayingIndicatorShown(true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Playback tests', function() {
|
||||
var controls;
|
||||
setup(function() {
|
||||
controls = new FakeControls(client);
|
||||
});
|
||||
|
||||
// Disabled See bug 1032037.
|
||||
test.skip('Check that progress bar updates when re-shown', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
music.playFirstSong();
|
||||
|
||||
var dt = 5.0;
|
||||
|
||||
// We want to wait a few seconds while the music app is in the background.
|
||||
controls.launch();
|
||||
client.helper.wait(dt * 1000); // Convert to ms
|
||||
controls.playPause();
|
||||
controls.close();
|
||||
|
||||
// Try to get the songProgress when the music is still in the background
|
||||
music.switchToMe({background: true});
|
||||
var t0 = music.songProgress;
|
||||
|
||||
// Make sure the progress bar got updated when the music app is brought to
|
||||
// the foreground.
|
||||
music.launch();
|
||||
var t1 = music.songProgress;
|
||||
assert(t1 - t0 > 0, 'Progress bar not updated!');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Player icon tests', function() {
|
||||
test('Check the player icon hides before play some song', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.checkPlayerIconShown(false);
|
||||
});
|
||||
|
||||
test('Check the player icon displays after play some song', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
music.playFirstSong();
|
||||
music.waitForPlayerView();
|
||||
|
||||
music.tapHeaderActionButton();
|
||||
music.waitForListView();
|
||||
music.checkPlayerIconShown(true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Status bar', function() {
|
||||
|
||||
var statusbar;
|
||||
setup(function() {
|
||||
statusbar = new Statusbar(client);
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
|
||||
// check the status bar for the hidden play icon
|
||||
client.switchToFrame();
|
||||
assert.equal(statusbar.playingIndicator.getAttribute('hidden'), 'true');
|
||||
|
||||
music.switchToMe();
|
||||
music.playFirstSong();
|
||||
music.waitForPlayerView();
|
||||
});
|
||||
|
||||
test('Check the play icon is in the status bar. moztrap:9742', function() {
|
||||
// check the status bar
|
||||
client.switchToFrame();
|
||||
assert.equal(statusbar.playingIndicator.getAttribute('hidden'), 'false');
|
||||
|
||||
// switch to the homescreen
|
||||
var system = client.loader.getAppClass('system');
|
||||
system.goHome();
|
||||
client.waitFor(function() {
|
||||
return client.findElement(system.Selector.activeHomescreenFrame)
|
||||
.displayed();
|
||||
});
|
||||
|
||||
// check the status bar again
|
||||
assert.equal(statusbar.playingIndicator.getAttribute('hidden'), 'false');
|
||||
});
|
||||
|
||||
test('Check the play icon is hidden after close Music app', function() {
|
||||
music.close();
|
||||
client.switchToFrame();
|
||||
assert.equal(statusbar.playingIndicator.getAttribute('hidden'), 'true');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Rating test', function() {
|
||||
test('Check Rating is saved. moztrap:2683', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
music.playFirstSong();
|
||||
|
||||
var stars;
|
||||
|
||||
// check there is no rating.
|
||||
music.showSongInfo();
|
||||
stars = client.findElement(Music.Selector.ratingBar).
|
||||
findElements('button');
|
||||
assert.equal(stars.length, 5, 'Less than 5 stars found');
|
||||
PlayerHelper.checkEmptyRating(stars);
|
||||
|
||||
var rating_value = 4;
|
||||
|
||||
music.tapRating(rating_value);
|
||||
|
||||
// wait that the rating bar disappear.
|
||||
client.waitFor(function() {
|
||||
return !client.findElement(Music.Selector.ratingBar).displayed();
|
||||
});
|
||||
|
||||
// tap to make the rating bar reappear.
|
||||
music.showSongInfo();
|
||||
|
||||
// find all the stars that are on.
|
||||
stars = client.findElements(Music.Selector.ratingStarsOn);
|
||||
assert.equal(stars.length, rating_value);
|
||||
|
||||
PlayerHelper.checkRatingStarsOrder(stars);
|
||||
|
||||
// switch back and forth
|
||||
music.tapHeaderActionButton();
|
||||
music.playFirstSong();
|
||||
|
||||
stars = client.findElements(Music.Selector.ratingStarsOn);
|
||||
assert.equal(stars.length, rating_value);
|
||||
|
||||
// close the app because we want to test things are saved.
|
||||
music.close();
|
||||
|
||||
// start it over.
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
music.playFirstSong();
|
||||
|
||||
stars = client.findElements(Music.Selector.ratingStarsOn);
|
||||
assert.equal(stars.length, rating_value);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Player navigation. moztrap:2376', function() {
|
||||
test('Check that the back button works', function() {
|
||||
// the navigation test back from the tab
|
||||
function tabNavTest() {
|
||||
music.waitForSubListView();
|
||||
music.tapHeaderActionButton();
|
||||
music.waitForListView();
|
||||
}
|
||||
|
||||
// the actual navigation test
|
||||
// sublist is set to true if we navigate from a sublistView.
|
||||
function navTest(sublist) {
|
||||
|
||||
var title = music.header.findElement('#title-text').text();
|
||||
if (sublist) {
|
||||
music.playFirstSongSublist();
|
||||
} else {
|
||||
music.playFirstSong();
|
||||
}
|
||||
|
||||
// Wait for the player view or the title is not changed yet.
|
||||
music.waitForPlayerView();
|
||||
|
||||
assert.notEqual(title, music.header.findElement('#title-text').text());
|
||||
|
||||
music.tapHeaderActionButton();
|
||||
// firstSong currently *wait* for the element first.
|
||||
if (sublist) {
|
||||
music.firstSongSublist;
|
||||
} else {
|
||||
music.firstSong;
|
||||
}
|
||||
|
||||
assert.equal(title, music.header.findElement('#title-text').text());
|
||||
}
|
||||
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
navTest(false);
|
||||
|
||||
music.switchToAlbumsView();
|
||||
music.selectAlbum('A Minute With Brendan');
|
||||
navTest(true);
|
||||
tabNavTest();
|
||||
|
||||
music.switchToArtistsView();
|
||||
music.selectArtist('Minute With');
|
||||
navTest(true);
|
||||
tabNavTest();
|
||||
|
||||
music.switchToPlaylistsView();
|
||||
music.selectPlaylist('Recently added');
|
||||
navTest(true);
|
||||
tabNavTest();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
16
gaia/dev_apps/music-oga/test/marionette/fakeactivitycaller/index.html
Executable file
16
gaia/dev_apps/music-oga/test/marionette/fakeactivitycaller/index.html
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Fake Remote Controls</title>
|
||||
<script defer src="/js/fakeactivitycaller.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<span>I am fake activity caller</span>
|
||||
<button id="open">Open</button>
|
||||
<button id="pick">Pick</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* global MozActivity */
|
||||
'use strict';
|
||||
|
||||
var FakeActivityCaller = {
|
||||
init: function() {
|
||||
this.openButton = document.getElementById('open');
|
||||
this.pickButton = document.getElementById('pick');
|
||||
|
||||
this.openButton.addEventListener('click', this.openActivity);
|
||||
this.pickButton.addEventListener('click', this.pickActivity);
|
||||
},
|
||||
|
||||
openActivity: function() {
|
||||
// NOOP
|
||||
},
|
||||
|
||||
pickActivity: function() {
|
||||
var activity = new MozActivity({
|
||||
name: 'pick',
|
||||
data: {
|
||||
type: 'audio/*'
|
||||
}
|
||||
});
|
||||
|
||||
activity.onerror = function(e) {
|
||||
console.warn('pick activity error:', activity.error.name);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
FakeActivityCaller.init();
|
||||
});
|
||||
10
gaia/dev_apps/music-oga/test/marionette/fakeactivitycaller/manifest.webapp
Executable file
10
gaia/dev_apps/music-oga/test/marionette/fakeactivitycaller/manifest.webapp
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "Fake Activity Caller",
|
||||
"description": "A fake activity caller app",
|
||||
"type": "certified",
|
||||
"launch_path": "/index.html",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
}
|
||||
}
|
||||
16
gaia/dev_apps/music-oga/test/marionette/fakecontrols/index.html
Executable file
16
gaia/dev_apps/music-oga/test/marionette/fakecontrols/index.html
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Fake Remote Controls</title>
|
||||
<script defer src="/js/fakecontrols.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="controls">
|
||||
<button id="playpause">Play/Pause</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
gaia/dev_apps/music-oga/test/marionette/fakecontrols/js/fakecontrols.js
Executable file
38
gaia/dev_apps/music-oga/test/marionette/fakecontrols/js/fakecontrols.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
/* exported controls */
|
||||
'use strict';
|
||||
|
||||
function RemoteControls() {
|
||||
this.controls = document.getElementById('controls');
|
||||
this.controls.addEventListener('click', this);
|
||||
|
||||
this.playPauseButton = document.getElementById('playpause');
|
||||
|
||||
window.navigator.mozSetMessageHandler('connection', function(request) {
|
||||
if (request.keyword !== 'mediacomms') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.port = request.port;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
RemoteControls.prototype = {
|
||||
handleEvent: function(event) {
|
||||
if (!this.port) {
|
||||
return;
|
||||
}
|
||||
|
||||
var command = null;
|
||||
switch (event.target) {
|
||||
case this.playPauseButton:
|
||||
command = 'playpause';
|
||||
break;
|
||||
}
|
||||
|
||||
if (command) {
|
||||
this.port.postMessage({command: command});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controls = new RemoteControls();
|
||||
16
gaia/dev_apps/music-oga/test/marionette/fakecontrols/manifest.webapp
Executable file
16
gaia/dev_apps/music-oga/test/marionette/fakecontrols/manifest.webapp
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "Fake Remote Controls",
|
||||
"description": "A fake remote controls app",
|
||||
"type": "certified",
|
||||
"launch_path": "/index.html",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"connections": {
|
||||
"mediacomms": {
|
||||
"description": "Communication with media apps for now playing info",
|
||||
"rules": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
gaia/dev_apps/music-oga/test/marionette/fakeringtones/index.html
Executable file
30
gaia/dev_apps/music-oga/test/marionette/fakeringtones/index.html
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Fake Ringtones</title>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#player {
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script defer src="/js/fakeringtones.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<audio id="player" controls autoplay></audio>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
41
gaia/dev_apps/music-oga/test/marionette/fakeringtones/js/fakeringtones.js
Executable file
41
gaia/dev_apps/music-oga/test/marionette/fakeringtones/js/fakeringtones.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
var FakeRingtones = {
|
||||
get player() {
|
||||
return document.getElementById('player');
|
||||
},
|
||||
|
||||
init: function() {
|
||||
this.loadMediaSample(function(file) {
|
||||
this.setSource(file);
|
||||
this.play();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
loadMediaSample: function(callback) {
|
||||
var songs = navigator.getDeviceStorage('music');
|
||||
|
||||
var cursor = songs.enumerate();
|
||||
|
||||
cursor.onsuccess = function(event) {
|
||||
var file = event.target.result;
|
||||
callback(file);
|
||||
};
|
||||
|
||||
cursor.onerror = function(event) {
|
||||
console.warn('No file found: ' + event.target.error);
|
||||
};
|
||||
},
|
||||
|
||||
setSource: function(file) {
|
||||
var url = window.URL.createObjectURL(file);
|
||||
this.player.mozAudioChannelType = 'ringer';
|
||||
this.player.src = url;
|
||||
},
|
||||
|
||||
play: function() {
|
||||
this.player.play();
|
||||
}
|
||||
};
|
||||
|
||||
FakeRingtones.init();
|
||||
14
gaia/dev_apps/music-oga/test/marionette/fakeringtones/manifest.webapp
Executable file
14
gaia/dev_apps/music-oga/test/marionette/fakeringtones/manifest.webapp
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Fake Ringtones",
|
||||
"description": "A fake ringtones app",
|
||||
"type": "certified",
|
||||
"launch_path": "/index.html",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"permissions": {
|
||||
"device-storage:music":{ "access": "readwrite" },
|
||||
"audio-channel-ringer":{}
|
||||
}
|
||||
}
|
||||
68
gaia/dev_apps/music-oga/test/marionette/lib/fakeactivitycaller.js
Executable file
68
gaia/dev_apps/music-oga/test/marionette/lib/fakeactivitycaller.js
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeActivityCaller(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeActivityCaller.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeActivityCaller;
|
||||
|
||||
FakeActivityCaller.DEFAULT_ORIGIN = 'fakeactivity.gaiamobile.org';
|
||||
|
||||
FakeActivityCaller.Selector = Object.freeze({
|
||||
openButton: '#open',
|
||||
pickButton: '#pick',
|
||||
|
||||
actionMenu: 'form[data-z-index-level="action-menu"]'
|
||||
});
|
||||
|
||||
FakeActivityCaller.prototype = {
|
||||
client: null,
|
||||
|
||||
get openButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
FakeActivityCaller.Selector.openButton);
|
||||
},
|
||||
|
||||
get pickButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
FakeActivityCaller.Selector.pickButton);
|
||||
},
|
||||
|
||||
get actionMenu() {
|
||||
return this.client.helper.waitForElement(
|
||||
FakeActivityCaller.Selector.actionMenu);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
},
|
||||
|
||||
tapOpenButton: function() {
|
||||
this.openButton.tap();
|
||||
},
|
||||
|
||||
tapPickButton: function() {
|
||||
this.pickButton.tap();
|
||||
},
|
||||
|
||||
selectMusicApp: function() {
|
||||
this.client.switchToFrame();
|
||||
|
||||
var list = this.actionMenu.findElements('button');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].text() === 'Music OGA') {
|
||||
this.client.helper.waitForElement(list[i]);
|
||||
list[i].tap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
37
gaia/dev_apps/music-oga/test/marionette/lib/fakecontrols.js
Executable file
37
gaia/dev_apps/music-oga/test/marionette/lib/fakecontrols.js
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeControls(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeControls.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeControls;
|
||||
|
||||
FakeControls.DEFAULT_ORIGIN = 'fakecontrols.gaiamobile.org';
|
||||
|
||||
FakeControls.Selector = Object.freeze({
|
||||
playPause: '#playpause'
|
||||
});
|
||||
|
||||
FakeControls.prototype = {
|
||||
client: null,
|
||||
|
||||
get playPauseElement() {
|
||||
return this.client.findElement(FakeControls.Selector.playPause);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
},
|
||||
|
||||
playPause: function() {
|
||||
this.playPauseElement.click();
|
||||
}
|
||||
};
|
||||
25
gaia/dev_apps/music-oga/test/marionette/lib/fakeringtones.js
Executable file
25
gaia/dev_apps/music-oga/test/marionette/lib/fakeringtones.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function FakeRingtones(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + FakeRingtones.DEFAULT_ORIGIN);
|
||||
}
|
||||
|
||||
module.exports = FakeRingtones;
|
||||
|
||||
FakeRingtones.DEFAULT_ORIGIN = 'fakeringtones.gaiamobile.org';
|
||||
|
||||
FakeRingtones.prototype = {
|
||||
client: null,
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
}
|
||||
};
|
||||
22
gaia/dev_apps/music-oga/test/marionette/lib/listviewhelper.js
Executable file
22
gaia/dev_apps/music-oga/test/marionette/lib/listviewhelper.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
/* global module, require */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var ListviewHelper = {
|
||||
|
||||
albumArtForListItem: function(client, item) {
|
||||
assert.ok(item);
|
||||
|
||||
var src;
|
||||
client.waitFor(function() {
|
||||
src = item.findElement('.list-album-art').getAttribute('src');
|
||||
return src !== null;
|
||||
});
|
||||
return src;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = ListviewHelper;
|
||||
397
gaia/dev_apps/music-oga/test/marionette/lib/music.js
Executable file
397
gaia/dev_apps/music-oga/test/marionette/lib/music.js
Executable file
|
|
@ -0,0 +1,397 @@
|
|||
/* global require, module */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
function Music(client, origin) {
|
||||
this.client = client;
|
||||
this.origin = origin || ('app://' + Music.DEFAULT_ORIGIN);
|
||||
this.actions = client.loader.getActions();
|
||||
}
|
||||
|
||||
module.exports = Music;
|
||||
|
||||
Music.DEFAULT_ORIGIN = 'music-oga.gaiamobile.org';
|
||||
|
||||
Music.Selector = Object.freeze({
|
||||
messageOverlay: '#overlay',
|
||||
firstTile: '.tile',
|
||||
playlistsTab: '#tabs-playlists',
|
||||
artistsTab: '#tabs-artists',
|
||||
songsTab: '#tabs-songs',
|
||||
albumsTab: '#tabs-albums',
|
||||
playerCover: '#player-cover',
|
||||
|
||||
// search fields
|
||||
searchField: '#views-search-input',
|
||||
searchTiles: '#views-tiles-search',
|
||||
searchTilesField: '#views-tiles-search-input',
|
||||
searchList: '#views-list-search',
|
||||
searchListField: '#views-list-search-input',
|
||||
// search results
|
||||
searchArtists: '#views-search-artists',
|
||||
searchAlbums: '#views-search-albums',
|
||||
searchTitles: '#views-search-titles',
|
||||
searchNoResult: '#views-search-no-result',
|
||||
|
||||
tilesView: '#views-tiles',
|
||||
listView: '#views-list',
|
||||
viewsList: '#views-list-anchor',
|
||||
sublistView: '#views-sublist',
|
||||
viewsSublist: '#views-sublist-anchor',
|
||||
sublistViewControls: '#views-sublist-header-controls',
|
||||
firstListItem: '.list-item',
|
||||
firstSong: '.list-item',
|
||||
firstSongSublist: '#views-sublist .list-item',
|
||||
playerView: '#views-player',
|
||||
playButton: '#player-controls-play',
|
||||
progressBar: '#player-seek-bar-progress',
|
||||
shareButton: '#player-cover-share',
|
||||
ratingBar: '#player-album-rating',
|
||||
ratingStarsOn: 'button.star-on',
|
||||
shareMenu: 'form[data-z-index-level="action-menu"]',
|
||||
pickDoneButton: '#title-done',
|
||||
header: '#title',
|
||||
titleText: '#title-text',
|
||||
sublistShuffleButton: '#views-sublist-controls-shuffle',
|
||||
playerIcon: '#title-player'
|
||||
});
|
||||
|
||||
Music.prototype = {
|
||||
client: null,
|
||||
|
||||
get messageOverlay() {
|
||||
return this.client.findElement(Music.Selector.messageOverlay);
|
||||
},
|
||||
|
||||
get firstTile() {
|
||||
return this.client.findElement(Music.Selector.firstTile);
|
||||
},
|
||||
|
||||
get playlistsTab() {
|
||||
return this.client.helper.waitForElement(Music.Selector.playlistsTab);
|
||||
},
|
||||
|
||||
get artistsTab() {
|
||||
return this.client.helper.waitForElement(Music.Selector.artistsTab);
|
||||
},
|
||||
|
||||
get songsTab() {
|
||||
return this.client.helper.waitForElement(Music.Selector.songsTab);
|
||||
},
|
||||
|
||||
get albumsTab() {
|
||||
return this.client.helper.waitForElement(Music.Selector.albumsTab);
|
||||
},
|
||||
|
||||
get firstSong() {
|
||||
return this.client.helper.waitForElement(Music.Selector.firstSong);
|
||||
},
|
||||
|
||||
get firstSongSublist() {
|
||||
return this.client.helper.waitForElement(Music.Selector.firstSongSublist);
|
||||
},
|
||||
|
||||
// Helper for the getter.
|
||||
_getListItems: function(selector) {
|
||||
this.waitForAList(selector);
|
||||
|
||||
var list = this.client.findElement(selector);
|
||||
assert.ok(list, 'Couldn\'t find element ' + selector);
|
||||
|
||||
var list_items = list.findElements('li.list-item', 'css selector');
|
||||
assert.ok(list_items, 'Coudln\'t find list-items for ' + selector);
|
||||
|
||||
return list_items;
|
||||
},
|
||||
|
||||
get firstListItem() {
|
||||
return this.client.helper.waitForElement(Music.Selector.firstListItem);
|
||||
},
|
||||
|
||||
get listItems() {
|
||||
return this._getListItems(Music.Selector.viewsList);
|
||||
},
|
||||
|
||||
get songs() {
|
||||
return this._getListItems(Music.Selector.viewsSublist);
|
||||
},
|
||||
|
||||
get playButton() {
|
||||
return this.client.findElement(Music.Selector.playButton);
|
||||
},
|
||||
|
||||
get shareButton() {
|
||||
return this.client.findElement(Music.Selector.shareButton);
|
||||
},
|
||||
|
||||
get title() {
|
||||
var header = this.client.findElement(Music.Selector.header);
|
||||
return header.findElement(Music.Selector.titleText);
|
||||
},
|
||||
|
||||
get sublistShuffleButton() {
|
||||
return this.client.helper.waitForElement(
|
||||
Music.Selector.sublistShuffleButton);
|
||||
},
|
||||
|
||||
// TODO(gareth): Move this shareMenu stuff into the helper.
|
||||
get shareMenu() {
|
||||
// Switch to the system app first.
|
||||
this.client.switchToFrame();
|
||||
return this.client.helper.waitForElement(Music.Selector.shareMenu);
|
||||
},
|
||||
|
||||
get progressBar() {
|
||||
return this.client.findElement(Music.Selector.progressBar);
|
||||
},
|
||||
|
||||
get pickDoneButton() {
|
||||
return this.client.findElement(Music.Selector.pickDoneButton);
|
||||
},
|
||||
|
||||
get header() {
|
||||
return this.client.findElement(Music.Selector.header);
|
||||
},
|
||||
|
||||
get playerIcon() {
|
||||
return this.client.findElement(Music.Selector.playerIcon);
|
||||
},
|
||||
|
||||
get isPlaying() {
|
||||
return this.playButton.getAttribute('class').indexOf('is-pause') === -1;
|
||||
},
|
||||
|
||||
get songProgress() {
|
||||
return parseFloat(this.progressBar.getAttribute('value'));
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.launch(this.origin);
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
this.client.helper.waitForElement('body');
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.client.apps.close(this.origin);
|
||||
},
|
||||
|
||||
switchToMe: function(options) {
|
||||
options = options || {};
|
||||
|
||||
this.client.switchToFrame();
|
||||
|
||||
// Switch to music even when it is in background.
|
||||
// We cannot use switchToApp here, because it will wait
|
||||
// for the app to come to foreground.
|
||||
if (options.background) {
|
||||
var musicFrame = this.client.findElement('iframe[src*="' +
|
||||
this.origin + '"]');
|
||||
this.client.switchToFrame(musicFrame);
|
||||
} else {
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
}
|
||||
},
|
||||
|
||||
waitForListEnumerate: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.ListView.handle.state === 'complete';
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitFinishedScanning: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.client.executeScript(function() {
|
||||
return window.wrappedJSObject.Database.initialScanComplete === true;
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForFirstTile: function() {
|
||||
this.client.helper.waitForElement(Music.Selector.firstTile);
|
||||
},
|
||||
|
||||
waitForMessageOverlayShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var volumeShown = this.messageOverlay.displayed();
|
||||
return volumeShown === shouldBeShown;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForAList: function(selector) {
|
||||
this.client.waitFor(function() {
|
||||
return this.client.findElement(selector).displayed();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// In sublist view.
|
||||
// XXX allow in list view too.
|
||||
waitForSongs: function(callback) {
|
||||
this.client.waitFor(function() {
|
||||
var songs = this.songs;
|
||||
return callback(songs);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForListView: function() {
|
||||
this.client.helper.waitForElement(Music.Selector.listView);
|
||||
},
|
||||
|
||||
waitForSubListView: function() {
|
||||
this.client.helper.waitForElement(Music.Selector.sublistView);
|
||||
},
|
||||
|
||||
waitForPlayerView: function() {
|
||||
this.client.helper.waitForElement(Music.Selector.playerView);
|
||||
},
|
||||
|
||||
// Because bug 862156 so we couldn't get the correct displayed value for the
|
||||
// player icon, instead we use the display property to check the visibility
|
||||
// of the player icon.
|
||||
checkPlayerIconShown: function(shouldBeShown) {
|
||||
var display = this.playerIcon.cssProperty('display');
|
||||
var result = (display !== 'none');
|
||||
assert.equal(shouldBeShown, result);
|
||||
},
|
||||
|
||||
showSearchInput: function(viewSelector) {
|
||||
var tilesView = this.client.findElement(viewSelector);
|
||||
var chain = this.actions.press(tilesView, 10, 10).perform();
|
||||
chain.moveByOffset(0, 110).perform();
|
||||
chain.release().perform();
|
||||
},
|
||||
|
||||
searchArtists: function(searchTerm) {
|
||||
this.search(Music.Selector.searchList, searchTerm);
|
||||
},
|
||||
|
||||
searchTiles: function(searchTerm) {
|
||||
this.search(Music.Selector.searchTiles, searchTerm);
|
||||
},
|
||||
|
||||
search: function(viewSelector, searchTerm) {
|
||||
this.client.findElement(viewSelector).tap();
|
||||
|
||||
var input = this.client.helper.waitForElement(Music.Selector.searchField);
|
||||
assert.ok(input);
|
||||
|
||||
input.clear();
|
||||
this.client.waitFor(input.displayed.bind(input));
|
||||
input.sendKeys(searchTerm);
|
||||
},
|
||||
|
||||
switchToArtistsView: function() {
|
||||
this.artistsTab.tap();
|
||||
},
|
||||
|
||||
switchToSongsView: function() {
|
||||
this.songsTab.tap();
|
||||
},
|
||||
|
||||
switchToAlbumsView: function() {
|
||||
this.albumsTab.tap();
|
||||
},
|
||||
|
||||
switchToPlaylistsView: function() {
|
||||
this.playlistsTab.tap();
|
||||
},
|
||||
|
||||
selectAlbum: function(name) {
|
||||
var list_items = this.listItems;
|
||||
|
||||
list_items.filter(function (element) {
|
||||
return element.findElement('.list-main-title', 'css selector')
|
||||
.text() === name;
|
||||
})[0].tap();
|
||||
},
|
||||
|
||||
selectArtist: function(name) {
|
||||
var list_items = this.listItems;
|
||||
|
||||
list_items.filter(function (element) {
|
||||
return element.findElement('.list-single-title', 'css selector')
|
||||
.text() === name;
|
||||
})[0].tap();
|
||||
},
|
||||
|
||||
selectPlaylist: function(name) {
|
||||
var list_items = this.listItems;
|
||||
|
||||
list_items.filter(function (element) {
|
||||
return element.findElement('.list-playlist-title', 'css selector')
|
||||
.text() === name;
|
||||
})[0].tap();
|
||||
},
|
||||
|
||||
// only from a list (song list)
|
||||
playFirstSong: function() {
|
||||
this.firstSong.click();
|
||||
},
|
||||
|
||||
// only from a sublist (artist, albums, playlists)
|
||||
playFirstSongSublist: function() {
|
||||
this.firstSongSublist.click();
|
||||
},
|
||||
|
||||
tapPlayButton: function() {
|
||||
this.actions.tap(this.playButton).perform();
|
||||
},
|
||||
|
||||
tapHeaderActionButton: function() {
|
||||
this.header.tap(25, 25);
|
||||
},
|
||||
|
||||
showSongInfo: function() {
|
||||
this.client.helper.waitForElement(Music.Selector.playerCover).click();
|
||||
},
|
||||
|
||||
tapRating: function(rating) {
|
||||
this.showSongInfo();
|
||||
this.client.helper.waitForElement('button.rating-star[data-rating="' +
|
||||
rating + '"]').tap();
|
||||
},
|
||||
|
||||
shareWith: function(appName) {
|
||||
|
||||
// Allow findElement to fail quickly.
|
||||
var quickly = this.client.scope({
|
||||
searchTimeout: 50
|
||||
});
|
||||
|
||||
var shareMenu;
|
||||
// Wait until the share menu is displayed.
|
||||
// Try to click the cover image followed by the share button in the case
|
||||
// that it hides before we get a chance to click it.
|
||||
this.client.waitFor(function() {
|
||||
this.showSongInfo();
|
||||
this.shareButton.tap();
|
||||
|
||||
this.client.switchToFrame();
|
||||
try {
|
||||
shareMenu = quickly.findElement(Music.Selector.shareMenu);
|
||||
} catch(e) {
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
return false;
|
||||
}
|
||||
|
||||
var isDisplayed = shareMenu.displayed();
|
||||
this.client.apps.switchToApp(this.origin);
|
||||
return isDisplayed;
|
||||
}.bind(this));
|
||||
|
||||
var list = this.shareMenu.findElements('button');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].text() === appName) {
|
||||
list[i].tap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
finishPick: function() {
|
||||
this.actions.tap(this.pickDoneButton).perform();
|
||||
}
|
||||
};
|
||||
28
gaia/dev_apps/music-oga/test/marionette/lib/playerhelper.js
Executable file
28
gaia/dev_apps/music-oga/test/marionette/lib/playerhelper.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
/* global module, require */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var PlayerHelper = {
|
||||
|
||||
// check the order of the rating stars.
|
||||
checkRatingStarsOrder: function(stars) {
|
||||
var expected = 1;
|
||||
stars.forEach(function(element) {
|
||||
// we always assume the stars are in DOM order. There is no reason
|
||||
// they wouldn't be.
|
||||
var value = element.getAttribute('data-rating');
|
||||
assert.equal(value, expected);
|
||||
expected++;
|
||||
});
|
||||
},
|
||||
|
||||
checkEmptyRating: function(stars) {
|
||||
stars.forEach(function(elem) {
|
||||
assert.equal(elem.getAttribute('class').indexOf('star-on'), -1,
|
||||
'Check empty rating. Star is on. Should be off');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = PlayerHelper;
|
||||
22
gaia/dev_apps/music-oga/test/marionette/lib/playlisthelper.js
Executable file
22
gaia/dev_apps/music-oga/test/marionette/lib/playlisthelper.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
|
||||
var PlaylistHelper = {
|
||||
|
||||
mainTitle: function(element) {
|
||||
return element.findElement('.list-main-title').text();
|
||||
},
|
||||
|
||||
songTitle: function(element) {
|
||||
return element.findElement('.list-song-title').text();
|
||||
},
|
||||
|
||||
songIndex: function(element) {
|
||||
return element.findElement('.list-song-index').text();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = PlaylistHelper;
|
||||
22
gaia/dev_apps/music-oga/test/marionette/lib/searchhelper.js
Executable file
22
gaia/dev_apps/music-oga/test/marionette/lib/searchhelper.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
|
||||
var SearchHelper = {
|
||||
|
||||
singleTitle: function(element) {
|
||||
return element.findElement('.list-single-title').text();
|
||||
},
|
||||
|
||||
mainTitle: function(element) {
|
||||
return element.findElement('.list-main-title').text();
|
||||
},
|
||||
|
||||
highlight: function(element) {
|
||||
return element.findElement('.search-highlight').text();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = SearchHelper;
|
||||
28
gaia/dev_apps/music-oga/test/marionette/lib/statusbar.js
Executable file
28
gaia/dev_apps/music-oga/test/marionette/lib/statusbar.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
/* global module */
|
||||
'use strict';
|
||||
|
||||
function Statusbar(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
module.exports = Statusbar;
|
||||
|
||||
Statusbar.Selector = Object.freeze({
|
||||
playingIndicator: '#statusbar-playing'
|
||||
});
|
||||
|
||||
Statusbar.prototype = {
|
||||
client: null,
|
||||
|
||||
get playingIndicator() {
|
||||
this.client.switchToFrame();
|
||||
return this.client.findElement(Statusbar.Selector.playingIndicator);
|
||||
},
|
||||
|
||||
waitForPlayingIndicatorShown: function(shouldBeShown) {
|
||||
this.client.waitFor(function() {
|
||||
var shown = this.playingIndicator.displayed();
|
||||
return shown === shouldBeShown;
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
84
gaia/dev_apps/music-oga/test/marionette/loading_test.js
Executable file
84
gaia/dev_apps/music-oga/test/marionette/loading_test.js
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
/* global require, marionette, setup, suite, test */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Music = require('./lib/music.js');
|
||||
|
||||
marionette('Music files loading', function() {
|
||||
var apps = {};
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
settings: {
|
||||
'lockscreen.enabled': false,
|
||||
'ftu.manifestURL': null
|
||||
},
|
||||
|
||||
apps: apps
|
||||
}
|
||||
});
|
||||
|
||||
var music;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
|
||||
});
|
||||
|
||||
suite('Detection of movies.', function () {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{
|
||||
// Regular Ogg Vorbis.
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/a.ogg'
|
||||
},
|
||||
{
|
||||
// AAC file.
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/aac-tags.mp4'
|
||||
},
|
||||
{
|
||||
// MPEG4 video file. Shouldn't load.
|
||||
type: 'music',
|
||||
filePath: 'test_media/Movies/gizmo2.mp4'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('Check the mp4 video isn\'t loaded. moztrap:8456', function() {
|
||||
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.waitFinishedScanning();
|
||||
|
||||
music.switchToSongsView();
|
||||
|
||||
music.waitForListEnumerate();
|
||||
|
||||
var songs = music.listItems;
|
||||
assert.equal(songs.length, 2, 'Wrong number of songs');
|
||||
|
||||
var songNames = client.executeScript(function() {
|
||||
var w = window.wrappedJSObject;
|
||||
return w.ListView.dataSource.map(function(record) {
|
||||
return record.name;
|
||||
}).sort();
|
||||
});
|
||||
|
||||
assert.equal(songNames.length, 2,
|
||||
'Data source have wrong number of songs');
|
||||
|
||||
assert.equal(songNames[0], 'music/a.ogg');
|
||||
assert.equal(songNames[1], 'music/aac-tags.mp4');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
1
gaia/dev_apps/music-oga/test/marionette/manifest.ini
Executable file
1
gaia/dev_apps/music-oga/test/marionette/manifest.ini
Executable file
|
|
@ -0,0 +1 @@
|
|||
[Player_test.js]
|
||||
117
gaia/dev_apps/music-oga/test/marionette/metadata_test.js
Executable file
117
gaia/dev_apps/music-oga/test/marionette/metadata_test.js
Executable file
|
|
@ -0,0 +1,117 @@
|
|||
/* global require, marionette, setup, suite, test */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Music = require('./lib/music.js');
|
||||
var ListviewHelper = require('./lib/listviewhelper.js');
|
||||
|
||||
function assertStartsWith(start, actual) {
|
||||
assert.equal(actual.indexOf(start), 0,
|
||||
'"' + actual + '" does not start with "' + start + '"');
|
||||
}
|
||||
|
||||
marionette('Music player metadata', function() {
|
||||
var apps = {};
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
settings: {
|
||||
'lockscreen.enabled': false,
|
||||
'ftu.manifestURL': null
|
||||
},
|
||||
|
||||
apps: apps
|
||||
}
|
||||
});
|
||||
|
||||
var music;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
|
||||
});
|
||||
|
||||
suite('Album art display', function () {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
// if the metadata changes in any of this files
|
||||
// the test might break. Artist, Title, Album, Art.
|
||||
{
|
||||
// blue art.
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/a.ogg'
|
||||
},
|
||||
{
|
||||
// red art
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/01.ogg'
|
||||
},
|
||||
{
|
||||
// no art
|
||||
type: 'music',
|
||||
filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Check album art is displayed. moztrap:8749', function() {
|
||||
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
music.switchToArtistsView();
|
||||
|
||||
var bg;
|
||||
var blue, red;
|
||||
|
||||
// We test the album art url.
|
||||
// an "app:" URL is the default placeholder.
|
||||
// a "blob:" URL is a blob. Produced from the metadata.
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[0]);
|
||||
assertStartsWith('app:', bg);
|
||||
|
||||
blue = ListviewHelper.albumArtForListItem(client, music.listItems[1]);
|
||||
assertStartsWith('blob:', blue);
|
||||
|
||||
red = ListviewHelper.albumArtForListItem(client, music.listItems[2]);
|
||||
assertStartsWith('blob:', red);
|
||||
|
||||
assert.notEqual(blue, red, 'Red and Blue should be different');
|
||||
|
||||
music.switchToAlbumsView();
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[0]);
|
||||
assertStartsWith('app:', bg);
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[1]);
|
||||
assertStartsWith('blob:', bg);
|
||||
assert.equal(bg, blue, 'Is not the blue art');
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[2]);
|
||||
assertStartsWith('blob:', bg);
|
||||
assert.equal(bg, red, 'Is not the red art');
|
||||
|
||||
music.switchToSongsView();
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[0]);
|
||||
assertStartsWith('blob:', bg);
|
||||
assert.equal(bg, blue, 'Is not the blue art');
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[1]);
|
||||
assertStartsWith('app:', bg);
|
||||
|
||||
bg = ListviewHelper.albumArtForListItem(client, music.listItems[2]);
|
||||
assertStartsWith('blob:', bg);
|
||||
assert.equal(bg, red, 'Is not the red art');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
104
gaia/dev_apps/music-oga/test/marionette/music_test.js
Executable file
104
gaia/dev_apps/music-oga/test/marionette/music_test.js
Executable file
|
|
@ -0,0 +1,104 @@
|
|||
/* global require, marionette, setup, suite, test, __dirname */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var Music = require('./lib/music.js');
|
||||
var FakeActivityCaller = require('./lib/fakeactivitycaller.js');
|
||||
|
||||
marionette('Music ui tests', function() {
|
||||
var apps = {};
|
||||
apps[FakeActivityCaller.DEFAULT_ORIGIN] = __dirname + '/fakeactivitycaller';
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
apps: apps
|
||||
}
|
||||
});
|
||||
|
||||
var music, system;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
system = client.loader.getAppClass('system');
|
||||
system.waitForFullyLoaded();
|
||||
});
|
||||
|
||||
suite('Launch regular music with no audio files', function() {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
});
|
||||
|
||||
test('Overlay should be shown when storage has no songs', function() {
|
||||
music.launch();
|
||||
music.waitFinishedScanning();
|
||||
music.waitForMessageOverlayShown(true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Launch regular music with one audio file', function() {
|
||||
setup(function() {
|
||||
client.fileManager.add([
|
||||
{ type: 'music', filePath: 'test_media/samples/Music/b2g.ogg' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('Overlay should be hidden when storage has some songs', function() {
|
||||
music.launch();
|
||||
music.waitFinishedScanning();
|
||||
music.waitForMessageOverlayShown(false);
|
||||
music.waitForFirstTile();
|
||||
});
|
||||
});
|
||||
|
||||
suite('Launch music picker with no audio files', function() {
|
||||
var activitycaller;
|
||||
|
||||
setup(function() {
|
||||
activitycaller = new FakeActivityCaller(client);
|
||||
client.fileManager.removeAllFiles();
|
||||
});
|
||||
|
||||
test('Overlay should be shown when storage has no songs', function() {
|
||||
try {
|
||||
activitycaller.launch();
|
||||
activitycaller.tapPickButton();
|
||||
activitycaller.selectMusicApp();
|
||||
|
||||
music.switchToMe();
|
||||
music.waitFinishedScanning();
|
||||
music.waitForMessageOverlayShown(true);
|
||||
} catch (e) { assert.ok(false, e.stack); }
|
||||
});
|
||||
});
|
||||
|
||||
suite('Launch music picker with one audio file', function() {
|
||||
var activitycaller;
|
||||
|
||||
setup(function() {
|
||||
activitycaller = new FakeActivityCaller(client);
|
||||
|
||||
client.fileManager.add([
|
||||
{ type: 'music', filePath: 'test_media/samples/Music/b2g.ogg' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('Overlay should be hidden when storage has some songs', function() {
|
||||
try {
|
||||
activitycaller.launch();
|
||||
activitycaller.tapPickButton();
|
||||
activitycaller.selectMusicApp();
|
||||
|
||||
music.switchToMe();
|
||||
music.waitFinishedScanning();
|
||||
music.waitForMessageOverlayShown(false);
|
||||
} catch (e) { assert.ok(false, e.stack); }
|
||||
});
|
||||
});
|
||||
});
|
||||
459
gaia/dev_apps/music-oga/test/marionette/playlist_test.js
Executable file
459
gaia/dev_apps/music-oga/test/marionette/playlist_test.js
Executable file
|
|
@ -0,0 +1,459 @@
|
|||
/* global require, marionette, setup, suite, test, __dirname */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Music = require('./lib/music.js');
|
||||
var FakeRingtones = require('./lib/fakeringtones.js');
|
||||
var FakeControls = require('./lib/fakecontrols.js');
|
||||
var PlaylistHelper = require('./lib/playlisthelper.js');
|
||||
/*var Statusbar = require('./lib/statusbar.js');*/
|
||||
|
||||
marionette('Music player playlist', function() {
|
||||
var apps = {};
|
||||
apps[FakeRingtones.DEFAULT_ORIGIN] = __dirname + '/fakeringtones';
|
||||
apps[FakeControls.DEFAULT_ORIGIN] = __dirname + '/fakecontrols';
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
apps: apps
|
||||
},
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var music;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
});
|
||||
|
||||
|
||||
suite('Special names', function() {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
// Title: "dump 2>&1 < ~/® <b>& Injection Vulnerablity</b>"
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/d.ogg'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('Check name with >, <, ~, &, markup and some Unicode. ' +
|
||||
'moztrap:2346,2347,8499,8491', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToSongsView();
|
||||
|
||||
// this will wait on the first song as well.
|
||||
var song = music.firstSong;
|
||||
assert.equal(PlaylistHelper.mainTitle(song),
|
||||
'dump 2>&1 < ~/® <b>& Injection Vulnerablity</b>');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Single disc tests', function () {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/01.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/02.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/03.ogg'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test('Check the sort order', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToAlbumsView();
|
||||
|
||||
music.selectAlbum('Where is Julian Assange?');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 3;
|
||||
});
|
||||
|
||||
var songs = music.songs;
|
||||
|
||||
assert.equal(songs.length, 3);
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]),
|
||||
'Australian citizen is a US traitor');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[1]), '4');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[1]),
|
||||
'Ich bin ein Berliner');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[2]), '8');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[2]),
|
||||
'The Ecuadorian Embassy');
|
||||
});
|
||||
|
||||
test('Check the playlist indexes', function() {
|
||||
// this test will check that the index value of each song is the index
|
||||
// and not the track number.
|
||||
// See bug 1129708
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.selectPlaylist('Recently added');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 3;
|
||||
});
|
||||
|
||||
var songs = music.songs;
|
||||
|
||||
assert.equal(songs.length, 3);
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songIndex(songs[1]), '2');
|
||||
assert.equal(PlaylistHelper.songIndex(songs[2]), '3');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Multi disc tests', function() {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/a.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/b.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/c.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/w.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/x.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/y.ogg'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Check the sort order', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.switchToAlbumsView();
|
||||
|
||||
music.selectAlbum('We crash computers');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
|
||||
var songs = music.songs;
|
||||
|
||||
assert.equal(songs.length, 6);
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1.01');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]), 'Yield to thread');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[2]), '1.03');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[2]), 'Windows BSOD');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[3]), '2.01');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[3]), 'Crash');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Default playlists', function() {
|
||||
setup(function() {
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{
|
||||
// Track 1.01 "Yield to Thread"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/y.ogg'
|
||||
},
|
||||
{
|
||||
// Track 1.02 "XOXO"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/x.ogg'
|
||||
},
|
||||
{
|
||||
// Track 1.03 "Windows BSOD"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/w.ogg'
|
||||
},
|
||||
{
|
||||
// Track 2.01 "Crash"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/c.ogg'
|
||||
},
|
||||
{
|
||||
// Track 2.02 "Break"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/b.ogg'
|
||||
},
|
||||
{
|
||||
// Track 2.03 "Abort"
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/a.ogg'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Highest rated playlist sort order. moztrap:3674', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
music.switchToAlbumsView();
|
||||
|
||||
music.selectAlbum('We crash computers');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
|
||||
var songs = music.songs;
|
||||
assert.equal(songs.length, 6);
|
||||
|
||||
client.executeScript(function() {
|
||||
var w = window.wrappedJSObject;
|
||||
var songData = w.SubListView.dataSource[3];
|
||||
w.Database.setSongRating(songData, 4);
|
||||
|
||||
songData = w.SubListView.dataSource[1];
|
||||
w.Database.setSongRating(songData, 5);
|
||||
});
|
||||
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.selectPlaylist('Highest rated');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
songs = music.songs;
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]), 'XOXO');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[1]), '2');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[1]), 'Crash');
|
||||
});
|
||||
|
||||
test('Recently added playlist sort order. moztrap:3675', function() {
|
||||
// start the app so the music files are added to the database.
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.waitFinishedScanning();
|
||||
|
||||
// close the app
|
||||
music.close();
|
||||
|
||||
// add more files.
|
||||
client.fileManager.add([
|
||||
{
|
||||
// This song has the title 'The Ecuadorian Embassy'
|
||||
// And the index '1'
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/01.ogg'
|
||||
}
|
||||
]);
|
||||
|
||||
// start it over. It will add the file above.
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
music.waitFinishedScanning();
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.selectPlaylist('Recently added');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
var songs = music.songs;
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]),
|
||||
'The Ecuadorian Embassy');
|
||||
});
|
||||
|
||||
test('Most played playlist sort order. moztrap:3676,3677', function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
music.switchToAlbumsView();
|
||||
|
||||
music.selectAlbum('We crash computers');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
|
||||
var songs = music.songs;
|
||||
assert.equal(songs.length, 6);
|
||||
|
||||
// we set the playcount.
|
||||
client.executeScript(function() {
|
||||
function incrementPlayCount(filename, value) {
|
||||
for (var i = 0; i < value; i++) {
|
||||
w.Database.incrementPlayCount(filename);
|
||||
}
|
||||
}
|
||||
|
||||
var w = window.wrappedJSObject;
|
||||
|
||||
// 'XOXO'
|
||||
var songData = w.SubListView.dataSource[1];
|
||||
incrementPlayCount(songData, 5);
|
||||
|
||||
// 'Crash'
|
||||
songData = w.SubListView.dataSource[3];
|
||||
incrementPlayCount(songData, 4);
|
||||
|
||||
songData = w.SubListView.dataSource[2];
|
||||
incrementPlayCount(songData, 3);
|
||||
|
||||
songData = w.SubListView.dataSource[4];
|
||||
incrementPlayCount(songData, 2);
|
||||
|
||||
// 'Yield to Thread'
|
||||
songData = w.SubListView.dataSource[0];
|
||||
incrementPlayCount(songData, 1);
|
||||
|
||||
// 'Abort'
|
||||
// Play count is 0 for that song.
|
||||
});
|
||||
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.selectPlaylist('Most played');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
songs = music.songs;
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]), 'XOXO');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[1]), '2');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[1]), 'Crash');
|
||||
|
||||
// Trick to go back to the playlistview.
|
||||
// Alternative is to tap the back button.
|
||||
music.switchToAlbumsView();
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
// Least played
|
||||
music.selectPlaylist('Least played');
|
||||
|
||||
music.waitForSongs(function(songs) {
|
||||
return songs.length >= 6;
|
||||
});
|
||||
songs = music.songs;
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[0]), '1');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[0]), 'Abort');
|
||||
|
||||
assert.equal(PlaylistHelper.songIndex(songs[1]), '2');
|
||||
assert.equal(PlaylistHelper.songTitle(songs[1]), 'Yield to thread');
|
||||
});
|
||||
|
||||
test('Shuffle all sort order. moztrap:2357', function() {
|
||||
try {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
var notrandom = 0;
|
||||
var lastTitle = '';
|
||||
var loopCount = 10;
|
||||
for (var i = 0; i < loopCount; i++) {
|
||||
|
||||
// selecting the playlist will put us into the player.
|
||||
music.selectPlaylist('Shuffle all');
|
||||
|
||||
// wait for the player.
|
||||
music.waitForPlayerView();
|
||||
|
||||
var title = music.title.text();
|
||||
if (title === lastTitle) {
|
||||
notrandom++;
|
||||
}
|
||||
lastTitle = title;
|
||||
// tap back
|
||||
music.tapHeaderActionButton();
|
||||
}
|
||||
// the first loop will never be "notrandom".
|
||||
assert.notEqual(notrandom, loopCount - 1, 'we didn\'t randomise');
|
||||
} catch(e) { assert.ok(false, 'Exception: ' + e.stack); }
|
||||
});
|
||||
|
||||
test('Shuffle playlist order. moztrap:2357', function() {
|
||||
try {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
music.switchToPlaylistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.selectPlaylist('Least played');
|
||||
|
||||
var notrandom = 0;
|
||||
var lastTitle = '';
|
||||
var loopCount = 10;
|
||||
for (var i = 0; i < loopCount; i++) {
|
||||
music.waitForSubListView();
|
||||
|
||||
client.helper.waitForElement(Music.Selector.sublistViewControls);
|
||||
|
||||
// tapping shuffle will put us into the player.
|
||||
music.sublistShuffleButton.tap();
|
||||
|
||||
// wait for the player.
|
||||
music.waitForPlayerView();
|
||||
|
||||
var title = music.title.text();
|
||||
if (title === lastTitle) {
|
||||
notrandom++;
|
||||
}
|
||||
lastTitle = title;
|
||||
// tap back
|
||||
music.tapHeaderActionButton();
|
||||
}
|
||||
assert.notEqual(notrandom, loopCount - 1, 'we didn\'t randomise');
|
||||
} catch(e) { assert.ok(false, 'Exception: ' + e.stack); }
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
177
gaia/dev_apps/music-oga/test/marionette/search_test.js
Executable file
177
gaia/dev_apps/music-oga/test/marionette/search_test.js
Executable file
|
|
@ -0,0 +1,177 @@
|
|||
/* global require, marionette, setup, suite, test, __dirname */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Music = require('./lib/music.js');
|
||||
var FakeRingtones = require('./lib/fakeringtones.js');
|
||||
var FakeControls = require('./lib/fakecontrols.js');
|
||||
var SearchHelper = require('./lib/searchhelper.js');
|
||||
/*var Statusbar = require('./lib/statusbar.js');*/
|
||||
|
||||
marionette('Music player search', function() {
|
||||
var apps = {};
|
||||
apps[FakeRingtones.DEFAULT_ORIGIN] = __dirname + '/fakeringtones';
|
||||
apps[FakeControls.DEFAULT_ORIGIN] = __dirname + '/fakecontrols';
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
},
|
||||
|
||||
apps: apps
|
||||
},
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var music;
|
||||
|
||||
setup(function() {
|
||||
music = new Music(client);
|
||||
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/01.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/02.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/03.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/a.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/b.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/c.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/w.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/x.ogg'
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
filePath: 'apps/music/test-data/playlists/y.ogg'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
function testSearchResults(viewSelector, expectedCount) {
|
||||
var view = client.helper.waitForElement(viewSelector);
|
||||
assert.ok(view);
|
||||
|
||||
// wait for the results to be displayed.
|
||||
// XXX this mostly assume we populate before showing the div.
|
||||
client.waitFor(function() {
|
||||
return view.displayed();
|
||||
});
|
||||
|
||||
// since we display the count, just get it.
|
||||
var count = view.findElement('.search-result-count').text();
|
||||
var results = view.findElement('.search-results');
|
||||
assert.ok(results);
|
||||
|
||||
var resultsList = results.findElements('li.list-item', 'css selector');
|
||||
assert.ok(resultsList);
|
||||
|
||||
// detect inconsistency.
|
||||
assert.equal(resultsList.length, count);
|
||||
// check what we expect.
|
||||
assert.equal(count, expectedCount);
|
||||
|
||||
return resultsList;
|
||||
}
|
||||
|
||||
suite('Search tests', function () {
|
||||
|
||||
setup(function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
|
||||
// Here we wait 1.5 seconds for the search input hides completely.
|
||||
// it will re-show after we scroll the target view.
|
||||
client.helper.wait(1500);
|
||||
music.showSearchInput(Music.Selector.tilesView);
|
||||
});
|
||||
|
||||
test('Check simple search results in artists.', function() {
|
||||
music.searchTiles('the');
|
||||
// check for the results in "artists"
|
||||
var resultsList = testSearchResults(Music.Selector.searchArtists, 2);
|
||||
|
||||
assert.equal(SearchHelper.singleTitle(resultsList[1]), 'The NSA');
|
||||
assert.equal(SearchHelper.highlight(resultsList[1]), 'The');
|
||||
|
||||
var noResult = client.findElement(Music.Selector.searchNoResult);
|
||||
assert.ok(noResult);
|
||||
assert.ok(!noResult.displayed());
|
||||
});
|
||||
|
||||
test('Check simple search results in tracks.', function() {
|
||||
music.searchTiles('the');
|
||||
// check for the results in "artists"
|
||||
var resultsList = testSearchResults(Music.Selector.searchTitles, 1);
|
||||
|
||||
assert.equal(SearchHelper.mainTitle(resultsList[0]),
|
||||
'The Ecuadorian Embassy');
|
||||
assert.equal(SearchHelper.highlight(resultsList[0]), 'The');
|
||||
var noResult = client.findElement(Music.Selector.searchNoResult);
|
||||
assert.ok(noResult);
|
||||
assert.ok(!noResult.displayed());
|
||||
});
|
||||
|
||||
test('Check empty results', function() {
|
||||
music.searchTiles('qwerty');
|
||||
|
||||
var view = client.findElement(Music.Selector.searchNoResult);
|
||||
|
||||
assert.ok(view);
|
||||
|
||||
client.waitFor(function() {
|
||||
return view.displayed();
|
||||
});
|
||||
|
||||
assert.ok(view.displayed());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Search context tests', function () {
|
||||
|
||||
setup(function() {
|
||||
music.launch();
|
||||
music.waitForFirstTile();
|
||||
});
|
||||
|
||||
// Tiles mode is already tested above.
|
||||
|
||||
test('Check the context for artists', function() {
|
||||
music.switchToArtistsView();
|
||||
music.waitForListView();
|
||||
|
||||
music.showSearchInput(Music.Selector.listView);
|
||||
music.searchArtists('the');
|
||||
|
||||
var resultsList = testSearchResults(Music.Selector.searchArtists, 2);
|
||||
|
||||
assert.equal(SearchHelper.singleTitle(resultsList[1]), 'The NSA');
|
||||
assert.equal(SearchHelper.highlight(resultsList[1]), 'The');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
17
gaia/dev_apps/music-oga/test/unit/.jshintrc
Executable file
17
gaia/dev_apps/music-oga/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
165
gaia/dev_apps/music-oga/test/unit/m3u_test.js
Executable file
165
gaia/dev_apps/music-oga/test/unit/m3u_test.js
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
/* global Playlist */
|
||||
/* jshint multistr: true */
|
||||
'use strict';
|
||||
|
||||
require('/js/playlist.js');
|
||||
|
||||
var sample1 = '#EXTM3U\n' +
|
||||
'#EXTINF:,It\'s Raining Again\n' +
|
||||
'Supertramp/_..Famous Last Words/03 It\'s Raining Again.mp3\n' +
|
||||
'#EXTINF:301,3e Sexe\n' +
|
||||
'/home/hub/Music/Indochine/3/13e_sexe.ogg\n' +
|
||||
'#EXTINF:-1,My Cool Stream\n' +
|
||||
'http://www.site.com:8000/listen.pls\n';
|
||||
|
||||
var MockGetDeviceStorage = function() {
|
||||
return {
|
||||
get: function(filepath) {
|
||||
return {
|
||||
set onsuccess(cb) {
|
||||
cb.call(this);
|
||||
},
|
||||
get result() {
|
||||
var file = new Blob([sample1], {});
|
||||
return file;
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
suite('m3u playlist', function() {
|
||||
var realNavigatorGetDeviceStorage;
|
||||
|
||||
suiteSetup(function() {
|
||||
realNavigatorGetDeviceStorage = navigator.getDeviceStorage;
|
||||
navigator.getDeviceStorage = MockGetDeviceStorage;
|
||||
this.timeout(1000);
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.getDeviceStorage = realNavigatorGetDeviceStorage;
|
||||
});
|
||||
|
||||
test('url', function() {
|
||||
assert.ok(Playlist.isUrl('file://tmp/foo.mp3'));
|
||||
assert.ok(Playlist.isUrl('http://www.stream.invalid/foo.mp3'));
|
||||
assert.ok(Playlist.isUrl('https://www.stream.invalid/foo.mp3'));
|
||||
assert.ok(Playlist.isUrl('mms://www.stream.invalid/foo.mp3'));
|
||||
assert.ok(Playlist.isUrl('rtmp://www.stream.invalid/foo.mp3'));
|
||||
assert.ok(!Playlist.isUrl('c:/tmp/foo.mp3'));
|
||||
assert.ok(!Playlist.isUrl('/tmp/foo.mp3'));
|
||||
});
|
||||
|
||||
test('load', function() {
|
||||
var pl = new Playlist('/home/hub/Music/my playlist.m3u');
|
||||
pl.fromString(sample1);
|
||||
|
||||
assert.strictEqual(pl.directory, '/home/hub/Music/');
|
||||
|
||||
var n = pl._makeAbsolutePath('ACDC/Thunderstruck.mp3');
|
||||
assert.strictEqual('/home/hub/Music/ACDC/Thunderstruck.mp3', n);
|
||||
n = pl._makeAbsolutePath('/tmp/ACDC/Thunderstruck.mp3');
|
||||
assert.strictEqual('/tmp/ACDC/Thunderstruck.mp3', n);
|
||||
|
||||
var songs = pl.songs;
|
||||
|
||||
assert.strictEqual(songs.length, 3);
|
||||
|
||||
assert.strictEqual(songs[0],
|
||||
'/home/hub/Music/Supertramp/' +
|
||||
'_..Famous Last Words/03 It\'s Raining Again.mp3');
|
||||
|
||||
assert.strictEqual(songs[1],
|
||||
'/home/hub/Music/Indochine/3/13e_sexe.ogg');
|
||||
|
||||
assert.strictEqual(songs[2],
|
||||
'http://www.site.com:8000/listen.pls');
|
||||
});
|
||||
|
||||
test('parse', function(done) {
|
||||
Playlist.parse('apps/music/test-data/playlists/my_playlist.m3u')
|
||||
.then(function(pl) {
|
||||
done(function() {
|
||||
var songs = pl.songs;
|
||||
assert.strictEqual(songs.length, 3);
|
||||
|
||||
assert.strictEqual(songs[0],
|
||||
'apps/music/test-data/playlists/Supertramp/' +
|
||||
'_..Famous Last Words/03 It\'s Raining Again.mp3');
|
||||
|
||||
assert.strictEqual(songs[1],
|
||||
'/home/hub/Music/Indochine/3/13e_sexe.ogg');
|
||||
});
|
||||
}, function(reason) {
|
||||
console.log('rejected', reason.message);
|
||||
});
|
||||
});
|
||||
|
||||
test('playlist manipulation', function() {
|
||||
var pl = new Playlist('/home/hub/Music/my playlist.m3u');
|
||||
|
||||
// add song.
|
||||
pl.addSong('/home/hub/Music/Stairway to haven.mp3');
|
||||
|
||||
// add song at index.
|
||||
pl.addSong('/home/hub/Music/Communication breakdown.mp3', 0);
|
||||
|
||||
pl.addSong('/home/hub/Music/Kashmir.mp3');
|
||||
|
||||
// add extended format song.
|
||||
pl.addSong('/home/hub/Music/Song Remain the same.mp3');
|
||||
assert.strictEqual(pl.songs.length, 4);
|
||||
assert.strictEqual(pl.songs[0],
|
||||
'/home/hub/Music/Communication breakdown.mp3');
|
||||
assert.strictEqual(pl.songs[1],
|
||||
'/home/hub/Music/Stairway to haven.mp3');
|
||||
|
||||
// test remove song
|
||||
pl.removeSong('/home/hub/Music/Kashmir.mp3');
|
||||
assert.strictEqual(pl.songs.length, 3);
|
||||
|
||||
assert.strictEqual(pl.songs[2],
|
||||
'/home/hub/Music/Song Remain the same.mp3');
|
||||
|
||||
// add duplicate (valid)
|
||||
pl.addSong('/home/hub/Music/Kashmir.mp3', 0);
|
||||
assert.strictEqual(pl.songs.length, 4);
|
||||
pl.addSong('/home/hub/Music/Kashmir.mp3');
|
||||
assert.strictEqual(pl.songs.length, 5);
|
||||
assert.strictEqual(pl.songs[0],
|
||||
'/home/hub/Music/Kashmir.mp3');
|
||||
assert.strictEqual(pl.songs[4],
|
||||
'/home/hub/Music/Kashmir.mp3');
|
||||
|
||||
// remove the duplicate
|
||||
pl.removeSong('/home/hub/Music/Kashmir.mp3');
|
||||
assert.strictEqual(pl.songs.length, 4);
|
||||
assert.strictEqual(pl.songs[0],
|
||||
'/home/hub/Music/Communication breakdown.mp3');
|
||||
assert.strictEqual(pl.songs[3],
|
||||
'/home/hub/Music/Kashmir.mp3');
|
||||
|
||||
// remove by index
|
||||
pl.removeSongAt(2);
|
||||
assert.strictEqual(pl.songs.length, 3);
|
||||
assert.strictEqual(pl.songs[2],
|
||||
'/home/hub/Music/Kashmir.mp3');
|
||||
});
|
||||
|
||||
test('save', function() {
|
||||
var pl = new Playlist('/home/hub/Music/my playlist.m3u');
|
||||
|
||||
pl.addSong('Stairway to haven.mp3');
|
||||
|
||||
pl.addSong('/home/hub/Music/Song Remain the same.mp3');
|
||||
assert.strictEqual(pl.songs.length, 2);
|
||||
|
||||
var output = pl.toString();
|
||||
|
||||
var lines = output.split('\n');
|
||||
assert.strictEqual(lines[0], '#EXTM3U');
|
||||
assert.strictEqual(lines[1], '/home/hub/Music/Stairway to haven.mp3');
|
||||
assert.strictEqual(lines[2], '/home/hub/Music/Song Remain the same.mp3');
|
||||
});
|
||||
});
|
||||
177
gaia/dev_apps/music-oga/test/unit/metadata/album_art_test.js
Executable file
177
gaia/dev_apps/music-oga/test/unit/metadata/album_art_test.js
Executable file
|
|
@ -0,0 +1,177 @@
|
|||
/* global AlbumArt, MockFileStore, MockGetDeviceStorage, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/test/unit/metadata/mock_navigator_getdevicestorage.js');
|
||||
|
||||
suite('album art', function() {
|
||||
|
||||
var RealGetDeviceStorage;
|
||||
|
||||
setup(function(done) {
|
||||
RealGetDeviceStorage = navigator.getDeviceStorage;
|
||||
navigator.getDeviceStorage = MockGetDeviceStorage;
|
||||
|
||||
require('/js/metadata/album_art.js', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.getDeviceStorage = RealGetDeviceStorage;
|
||||
});
|
||||
|
||||
var songBlob = new Blob();
|
||||
songBlob.name = '/path/to/file.mp3';
|
||||
|
||||
test('embedded art', function(done) {
|
||||
var metadata = {
|
||||
picture: { flavor: 'embedded' }
|
||||
};
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
// Make sure we didn't do anything to the metadata.
|
||||
assert.deepEqual(metadata, {
|
||||
picture: { flavor: 'embedded' }
|
||||
});
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('unsynced art', function(done) {
|
||||
var imageData = 'an image';
|
||||
var metadata = {
|
||||
artist: 'artist',
|
||||
album: 'album',
|
||||
picture: {
|
||||
flavor: 'unsynced',
|
||||
blob: new Blob([imageData], {type: 'image/jpeg'})
|
||||
}
|
||||
};
|
||||
var pictureFilename = '/path/.music/covers/' + metadata.artist + '.' +
|
||||
metadata.album + '.' + imageData.length + '.jpg';
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
assert.deepEqual(metadata, {
|
||||
artist: 'artist',
|
||||
album: 'album',
|
||||
picture: { flavor: 'unsynced', filename: pictureFilename }
|
||||
});
|
||||
|
||||
return MockFileStore.readAsText('pictures', pictureFilename);
|
||||
})
|
||||
.then(function(text) {
|
||||
assert.strictEqual(imageData, text);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('unsynced art (no artist/album)', function(done) {
|
||||
var imageData = 'an image';
|
||||
var metadata = {
|
||||
picture: {
|
||||
flavor: 'unsynced',
|
||||
blob: new Blob([imageData], {type: 'image/jpeg'})
|
||||
}
|
||||
};
|
||||
var pictureFilename = '/path/.music/covers/_path_to_file.mp3.' +
|
||||
imageData.length + '.jpg';
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
assert.deepEqual(metadata, {
|
||||
picture: { flavor: 'unsynced', filename: pictureFilename }
|
||||
});
|
||||
|
||||
return MockFileStore.readAsText('pictures', pictureFilename);
|
||||
})
|
||||
.then(function(text) {
|
||||
assert.strictEqual(imageData, text);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('unsynced art (already stored)', function(done) {
|
||||
// These should be the same length!
|
||||
var oldImageData = 'an image';
|
||||
var newImageData = 'diffdata';
|
||||
|
||||
var metadata = {
|
||||
artist: 'artist',
|
||||
album: 'album',
|
||||
picture: {
|
||||
flavor: 'unsynced',
|
||||
blob: new Blob([newImageData], {type: 'image/jpeg'})
|
||||
}
|
||||
};
|
||||
var pictureFilename = '/path/.music/covers/' + metadata.artist + '.' +
|
||||
metadata.album + '.' + newImageData.length + '.jpg';
|
||||
|
||||
// Inject an existing image with slightly different data so we can tell
|
||||
// it from the metadata's "new" image.
|
||||
MockFileStore.set('pictures', pictureFilename, new Blob(
|
||||
[oldImageData], {type: 'image/jpeg'}
|
||||
));
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
assert.deepEqual(metadata, {
|
||||
artist: 'artist',
|
||||
album: 'album',
|
||||
picture: { flavor: 'unsynced', filename: pictureFilename }
|
||||
});
|
||||
|
||||
return MockFileStore.readAsText('pictures', pictureFilename);
|
||||
})
|
||||
.then(function(text) {
|
||||
assert.strictEqual(oldImageData, text);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('external art', function(done) {
|
||||
var imageData = 'an image';
|
||||
var pictureFilename = '/path/to/cover.jpg';
|
||||
// Inject an external image into the same folder as our "file".
|
||||
MockFileStore.set('pictures', pictureFilename, new Blob(
|
||||
[imageData], {type: 'image/jpeg'}
|
||||
));
|
||||
|
||||
var metadata = {};
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
// Make sure we found the external cover art.
|
||||
assert.deepEqual(metadata, {
|
||||
picture: { flavor: 'external', filename: pictureFilename }
|
||||
});
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('no art', function(done) {
|
||||
var metadata = {};
|
||||
songBlob.name = '/another/path/to/file.mp3';
|
||||
|
||||
AlbumArt.process(songBlob, metadata)
|
||||
.then(function(metadata) {
|
||||
// Make sure we didn't do anything to the metadata.
|
||||
assert.deepEqual(metadata, {});
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('temporary file', function(done) {
|
||||
var metadata = {};
|
||||
|
||||
AlbumArt.process(new Blob(), metadata)
|
||||
.then(function(metadata) {
|
||||
// Make sure we didn't do anything to the metadata.
|
||||
assert.deepEqual(metadata, {});
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
91
gaia/dev_apps/music-oga/test/unit/metadata/core_test.js
Executable file
91
gaia/dev_apps/music-oga/test/unit/metadata/core_test.js
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
/* global AudioMetadata, fetchBuffer, MockLazyLoader, readBlob,
|
||||
assertBuffersEqual, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/shared/js/omadrm/fl.js');
|
||||
require('/js/metadata/forward_lock.js');
|
||||
require('/js/metadata/id3v1.js');
|
||||
require('/js/metadata/id3v2.js');
|
||||
require('/js/metadata/ogg.js');
|
||||
require('/js/metadata/vorbis_picture.js');
|
||||
|
||||
suite('core metadata parsing', function() {
|
||||
|
||||
var RealLazyLoader, expectedPicture;
|
||||
|
||||
setup(function(done) {
|
||||
RealLazyLoader = window.LazyLoader;
|
||||
window.LazyLoader = MockLazyLoader;
|
||||
|
||||
require('/js/metadata/core.js')
|
||||
.then(function() {
|
||||
return fetchBuffer('/test-data/album-art.jpg');
|
||||
})
|
||||
.then(function(data) {
|
||||
expectedPicture = data;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.LazyLoader = RealLazyLoader;
|
||||
});
|
||||
|
||||
test('3gp video rejected', function(done) {
|
||||
var blob = new Blob([new Uint8Array(256)]);
|
||||
blob.name = 'DCIM/video.3gp';
|
||||
AudioMetadata.parse(blob)
|
||||
.then(fail(done, '3gp video wasn\'t rejected'), pass(done));
|
||||
});
|
||||
|
||||
test('small file rejected', function(done) {
|
||||
var blob = new Blob([]);
|
||||
AudioMetadata.parse(blob)
|
||||
.then(fail(done, 'small file wasn\'t rejected'), pass(done));
|
||||
});
|
||||
|
||||
test('ordinary file', function(done) {
|
||||
fetchBuffer('/test-data/vorbis-c.ogg')
|
||||
.then(function(data) {
|
||||
return AudioMetadata.parse(new Blob([data]));
|
||||
})
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'vorbis');
|
||||
assert.strictEqual(metadata.artist, 'Angra');
|
||||
assert.strictEqual(metadata.album, 'Holy Land');
|
||||
assert.strictEqual(metadata.title, 'Carolina IV');
|
||||
assert.strictEqual(metadata.tracknum, 4);
|
||||
assert.strictEqual(metadata.trackcount, 10);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('no metadata', function(done) {
|
||||
var filename = '/test-data/no-tag.mp3';
|
||||
fetchBuffer(filename)
|
||||
.then(function(buffer) {
|
||||
var blob = new Blob([buffer]);
|
||||
blob.name = filename;
|
||||
return blob;
|
||||
})
|
||||
.then(AudioMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, undefined);
|
||||
assert.strictEqual(metadata.artist, '');
|
||||
assert.strictEqual(metadata.album, '');
|
||||
assert.strictEqual(metadata.title, 'no-tag');
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
54
gaia/dev_apps/music-oga/test/unit/metadata/flac_test.js
Executable file
54
gaia/dev_apps/music-oga/test/unit/metadata/flac_test.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
/* global FLACMetadata, fetchBuffer, makeBlobView, MockLazyLoader, readPicSlice,
|
||||
assertBuffersEqual, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/js/metadata/flac.js');
|
||||
require('/js/metadata/vorbis_picture.js');
|
||||
|
||||
suite('flac tags', function() {
|
||||
var RealLazyLoader;
|
||||
|
||||
var expectedPicture;
|
||||
setup(function(done) {
|
||||
RealLazyLoader = window.LazyLoader;
|
||||
window.LazyLoader = MockLazyLoader;
|
||||
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.LazyLoader = RealLazyLoader;
|
||||
});
|
||||
|
||||
test('vorbis comment', function(done) {
|
||||
var blob;
|
||||
fetchBuffer('/test-data/vorbis-c.flac')
|
||||
.then(function(buffer) {
|
||||
return (blob = new Blob([buffer]));
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(FLACMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'flac');
|
||||
assert.strictEqual(metadata.artist, 'Black Sabbath');
|
||||
assert.strictEqual(metadata.album, 'Master of Reality');
|
||||
assert.strictEqual(metadata.title, 'Children of the Grave');
|
||||
assert.strictEqual(metadata.tracknum, 4);
|
||||
assert.strictEqual(metadata.trackcount, 8);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
assert.strictEqual(metadata.picture.flavor, 'embedded');
|
||||
assert.strictEqual(metadata.picture.type, 'image/jpeg');
|
||||
|
||||
return readPicSlice(blob, metadata.picture);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
59
gaia/dev_apps/music-oga/test/unit/metadata/formats_test.js
Executable file
59
gaia/dev_apps/music-oga/test/unit/metadata/formats_test.js
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
/* global FLACMetadata, ForwardLockMetadata, ID3v1Metadata, ID3v2Metadata,
|
||||
MP4Metadata, OggMetadata, MetadataFormats, fetchBlobView, fetchBuffer,
|
||||
ForwardLock, makeBlobView, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/shared/js/omadrm/fl.js');
|
||||
require('/js/metadata/flac.js');
|
||||
require('/js/metadata/forward_lock.js');
|
||||
require('/js/metadata/id3v1.js');
|
||||
require('/js/metadata/id3v2.js');
|
||||
require('/js/metadata/mp4.js');
|
||||
require('/js/metadata/ogg.js');
|
||||
|
||||
suite('format deduction', function() {
|
||||
// Define the tests we want to run; note that `module` is a getter because
|
||||
// the modules won't be loaded by the time this code is run.
|
||||
var formats = [
|
||||
{name: 'flac', file: '/test-data/vorbis-c.flac',
|
||||
get module() { return FLACMetadata; }},
|
||||
{name: 'mp3 (id3v1)', file: '/test-data/id3v1-simple.mp3',
|
||||
get module() { return ID3v1Metadata; }},
|
||||
{name: 'mp3 (id3v2)', file: '/test-data/id3v2.4-simple-latin1.mp3',
|
||||
get module() { return ID3v2Metadata; }},
|
||||
{name: 'mp3 (id3v1+2)', file: '/test-data/id3v1+2-simple.mp3',
|
||||
get module() { return ID3v2Metadata; }},
|
||||
{name: 'flac', file: '/test-data/aac-tags.m4a',
|
||||
get module() { return MP4Metadata; }},
|
||||
{name: 'ogg', file: '/test-data/vorbis-c.ogg',
|
||||
get module() { return OggMetadata; }},
|
||||
];
|
||||
|
||||
formats.forEach(function(fmt) {
|
||||
test(fmt.name, function(done) {
|
||||
fetchBlobView(fmt.file)
|
||||
.then(MetadataFormats.findParser)
|
||||
.then(function(parser) {
|
||||
assert.strictEqual(parser._formatInfo.module, fmt.module);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
test('forwardlock', function(done) {
|
||||
fetchBuffer('/test-data/no-tag.mp3')
|
||||
.then(function(buffer) {
|
||||
return ForwardLock.lockBuffer(
|
||||
0xDEADBEEF, buffer, 'audio/mpeg', {}
|
||||
);
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(MetadataFormats.findParser)
|
||||
.then(function(parser) {
|
||||
assert.strictEqual(parser._formatInfo.module, ForwardLockMetadata);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
157
gaia/dev_apps/music-oga/test/unit/metadata/forward_lock_test.js
Executable file
157
gaia/dev_apps/music-oga/test/unit/metadata/forward_lock_test.js
Executable file
|
|
@ -0,0 +1,157 @@
|
|||
/* global ForwardLockMetadata, fetchBuffer, ForwardLock, makeBlobView,
|
||||
MockGetDeviceStorage, MockLazyLoader, readBlob, assertBuffersEqual, pass,
|
||||
fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_getdevicestorage.js');
|
||||
require('/shared/js/omadrm/fl.js');
|
||||
require('/js/metadata/forward_lock.js');
|
||||
require('/js/metadata/id3v1.js');
|
||||
require('/js/metadata/id3v2.js');
|
||||
|
||||
suite('forwardlock files', function() {
|
||||
|
||||
var RealLazyLoader, RealGetDeviceStorage, RealForwardLockGetKey;
|
||||
|
||||
var secret = 0xDEADBEEF;
|
||||
function mockGetKey(secret, callback) {
|
||||
callback(secret);
|
||||
}
|
||||
|
||||
setup(function(done) {
|
||||
RealLazyLoader = window.LazyLoader;
|
||||
window.LazyLoader = MockLazyLoader;
|
||||
|
||||
RealGetDeviceStorage = navigator.getDeviceStorage;
|
||||
navigator.getDeviceStorage = MockGetDeviceStorage;
|
||||
|
||||
RealForwardLockGetKey = ForwardLock.getKey;
|
||||
ForwardLock.getKey = mockGetKey.bind(this, secret);
|
||||
|
||||
require('/js/metadata/core.js', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.LazyLoader = RealLazyLoader;
|
||||
navigator.getDeviceStorage = RealGetDeviceStorage;
|
||||
ForwardLock.getKey = RealForwardLockGetKey;
|
||||
});
|
||||
|
||||
test('mp3 with id3v2 tag', function(done) {
|
||||
var vendorMetadata = {vendor: 'Songs for Cool Kids, Inc'};
|
||||
fetchBuffer('/test-data/id3v2.4-simple-latin1.mp3')
|
||||
.then(function(buffer) {
|
||||
return ForwardLock.lockBuffer(
|
||||
secret, buffer, 'audio/mpeg', vendorMetadata
|
||||
);
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(ForwardLockMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.locked, true);
|
||||
assert.strictEqual(metadata.vendor, vendorMetadata.vendor);
|
||||
assert.strictEqual(metadata.tag_format, 'id3v2.4.0');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('mp3 with no metadata', function(done) {
|
||||
var vendorMetadata = {vendor: 'Songs for Cool Kids, Inc',
|
||||
name: 'The Song That Never Ends'};
|
||||
fetchBuffer('/test-data/no-tag.mp3')
|
||||
.then(function(buffer) {
|
||||
return ForwardLock.lockBuffer(
|
||||
secret, buffer, 'audio/mpeg', vendorMetadata
|
||||
);
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(ForwardLockMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
done(function() {
|
||||
assert.strictEqual(metadata.locked, true);
|
||||
assert.strictEqual(metadata.vendor, vendorMetadata.vendor);
|
||||
assert.strictEqual(metadata.artist, '');
|
||||
assert.strictEqual(metadata.album, '');
|
||||
assert.strictEqual(metadata.title, vendorMetadata.name);
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
test('decrypting forwardlock with no secret key', function(done) {
|
||||
fetchBuffer('/test-data/id3v2.4-simple-latin1.mp3')
|
||||
.then(function(buffer) {
|
||||
return ForwardLock.lockBuffer(
|
||||
secret, buffer, 'audio/mpeg', {vendor: 'vendor'}
|
||||
);
|
||||
})
|
||||
.then(function(blob) {
|
||||
ForwardLock.getKey = mockGetKey.bind(this, null);
|
||||
return blob;
|
||||
})
|
||||
.catch(function(err) {
|
||||
// Make sure we haven't hit an error yet...
|
||||
done(new Error('failed to encrypt our test file'));
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(ForwardLockMetadata.parse)
|
||||
.then(fail(done, 'parsing should have failed, but passed'), pass(done));
|
||||
});
|
||||
|
||||
suite('album art', function() {
|
||||
var expectedPicture;
|
||||
setup(function(done) {
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var testInfo = [
|
||||
{test: 'mp3 with non-unsynced album art',
|
||||
filename: '/test-data/id3v2.4-picture.mp3'},
|
||||
{test: 'mp3 with unsynced album art',
|
||||
filename: '/test-data/id3v2.4-framesunsync.mp3'},
|
||||
];
|
||||
testInfo.forEach(function(info) {
|
||||
test(info.test, function(done) {
|
||||
var vendorMetadata = {vendor: 'Songs for Cool Kids, Inc'}, blob;
|
||||
fetchBuffer(info.filename)
|
||||
.then(function(buffer) {
|
||||
return (blob = ForwardLock.lockBuffer(
|
||||
secret, buffer, 'audio/mpeg', vendorMetadata
|
||||
));
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(ForwardLockMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.locked, true);
|
||||
assert.strictEqual(metadata.vendor, vendorMetadata.vendor);
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
32
gaia/dev_apps/music-oga/test/unit/metadata/id3v1_test.js
Executable file
32
gaia/dev_apps/music-oga/test/unit/metadata/id3v1_test.js
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
/* global ID3v1Metadata, fetchBlobView, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/js/metadata/id3v1.js');
|
||||
|
||||
suite('id3v1 tags', function() {
|
||||
|
||||
test('id3v1', function(done) {
|
||||
fetchBlobView('/test-data/id3v1-simple.mp3')
|
||||
.then(ID3v1Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'id3v1');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, undefined);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('no metadata', function(done) {
|
||||
fetchBlobView('/test-data/no-tag.mp3')
|
||||
.then(ID3v1Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.deepEqual(metadata, {});
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
343
gaia/dev_apps/music-oga/test/unit/metadata/id3v2_test.js
Executable file
343
gaia/dev_apps/music-oga/test/unit/metadata/id3v2_test.js
Executable file
|
|
@ -0,0 +1,343 @@
|
|||
/* global ID3v2Metadata, fetchBlobView, fetchBuffer, makeBlobView,
|
||||
MockLazyLoader, readBlob, readPicSlice, assertBuffersEqual, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/js/metadata/id3v1.js');
|
||||
require('/js/metadata/id3v2.js');
|
||||
|
||||
suite('id3v2 tags', function() {
|
||||
|
||||
// We have a bunch of otherwise-identical mp3 files using different ID3
|
||||
// versions, covering all valid character encodings. Test them all.
|
||||
[2, 3, 4].forEach(function(version) {
|
||||
var tag_format = 'id3v2.' + version + '.0';
|
||||
suite('simple id3v2.' + version, function() {
|
||||
|
||||
test('latin1', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-simple-latin1.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('utf16', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-simple-utf16.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'Mötley Crüe');
|
||||
assert.strictEqual(metadata.album, 'Dr. Feelgood');
|
||||
assert.strictEqual(metadata.title, 'Kickstart My Heart');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 11);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
if (version === 4) {
|
||||
test('utf16be', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-simple-utf16be.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'Gåte');
|
||||
assert.strictEqual(metadata.album, 'Jygri');
|
||||
assert.strictEqual(metadata.title, 'Bruremarsj frå Jämtland');
|
||||
assert.strictEqual(metadata.tracknum, 8);
|
||||
assert.strictEqual(metadata.trackcount, 12);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('utf8', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-simple-utf8.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'Lunar Aurora');
|
||||
assert.strictEqual(metadata.album, 'Hoagascht');
|
||||
assert.strictEqual(metadata.title, 'Håbergoaß');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 8);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('simple id3v1+2', function() {
|
||||
|
||||
test('id3v1+2', function(done) {
|
||||
var filename = '/test-data/id3v1+2-simple.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
// Here we should have the v2 tag content.
|
||||
assert.strictEqual(metadata.tag_format, 'id3v2.3.0');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, undefined);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('album art', function() {
|
||||
|
||||
var expectedPicture;
|
||||
setup(function(done) {
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
[2, 3, 4].forEach(function(version) {
|
||||
test('id3v2.' + version, function(done) {
|
||||
var blob, filename = '/test-data/id3v2.' + version + '-picture.mp3';
|
||||
fetchBuffer(filename)
|
||||
.then(function(buffer) {
|
||||
return (blob = new Blob([buffer]));
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.picture.flavor, 'embedded');
|
||||
assert.strictEqual(metadata.picture.type, 'image/jpeg');
|
||||
|
||||
return readPicSlice(blob, metadata.picture);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('extended header', function() {
|
||||
|
||||
[3, 4].forEach(function(version) {
|
||||
var tag_format = 'id3v2.' + version + '.0';
|
||||
|
||||
test('id3v2.' + version, function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-extheader.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('unsynchronized data', function() {
|
||||
|
||||
var expectedPicture;
|
||||
setup(function(done) {
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
[3, 4].forEach(function(version) {
|
||||
var tag_format = 'id3v2.' + version + '.0';
|
||||
|
||||
test('id3v2.' + version + ' whole tag', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version + '-allunsync.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, tag_format);
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
test('id3v2.4 selected frames', function(done) {
|
||||
var filename = '/test-data/id3v2.4-framesunsync.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'id3v2.4.0');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('id3v2.4 selected frames with data length indicator', function(done) {
|
||||
var filename = '/test-data/id3v2.4-framesunsync-datalen.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'id3v2.4.0');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, 9);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
suite('multivalue frames', function() {
|
||||
|
||||
['latin1', 'utf16', 'utf16be', 'utf8'].forEach(function(encoding) {
|
||||
test('id3v2.4 ' + encoding, function(done) {
|
||||
var filename = '/test-data/id3v2.4-multivalue-' + encoding + '.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'id3v2.4.0');
|
||||
assert.strictEqual(metadata.artist, 'Dynatron / Perturbator');
|
||||
assert.strictEqual(metadata.album, 'I Am the Night');
|
||||
assert.strictEqual(metadata.title, 'Volcanic Machinery');
|
||||
assert.strictEqual(metadata.tracknum, 13);
|
||||
assert.strictEqual(metadata.trackcount, 15);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('invalid files', function() {
|
||||
var RealLazyLoader;
|
||||
|
||||
setup(function() {
|
||||
RealLazyLoader = window.LazyLoader;
|
||||
window.LazyLoader = MockLazyLoader;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.LazyLoader = RealLazyLoader;
|
||||
});
|
||||
|
||||
[2, 3, 4].forEach(function(version) {
|
||||
suite('invalid id3v2.' + version, function() {
|
||||
|
||||
test('invalid frame size', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version +
|
||||
'-invalid-frame-size.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, undefined);
|
||||
assert.strictEqual(metadata.artist, undefined);
|
||||
assert.strictEqual(metadata.album, undefined);
|
||||
assert.strictEqual(metadata.title, undefined);
|
||||
assert.strictEqual(metadata.tracknum, undefined);
|
||||
assert.strictEqual(metadata.trackcount, undefined);
|
||||
assert.strictEqual(metadata.discnum, undefined);
|
||||
assert.strictEqual(metadata.disccount, undefined);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('invalid skipped frame', function(done) {
|
||||
var filename = '/test-data/id3v2.' + version +
|
||||
'-invalid-skipped-frame-size.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, undefined);
|
||||
assert.strictEqual(metadata.artist, undefined);
|
||||
assert.strictEqual(metadata.album, undefined);
|
||||
assert.strictEqual(metadata.title, undefined);
|
||||
assert.strictEqual(metadata.tracknum, undefined);
|
||||
assert.strictEqual(metadata.trackcount, undefined);
|
||||
assert.strictEqual(metadata.discnum, undefined);
|
||||
assert.strictEqual(metadata.disccount, undefined);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('id3v1+2 with invalid ID3v2 frame size', function(done) {
|
||||
var filename = '/test-data/id3v1+2-invalid-frame-size.mp3';
|
||||
fetchBlobView(filename)
|
||||
.then(ID3v2Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
// Here we should have the v1 tag content.
|
||||
assert.strictEqual(metadata.tag_format, 'id3v1');
|
||||
assert.strictEqual(metadata.artist, 'AC/DC');
|
||||
assert.strictEqual(metadata.album, 'Dirty Deeds Done Dirt Cheap');
|
||||
assert.strictEqual(metadata.title, 'Problem Child OLD');
|
||||
assert.strictEqual(metadata.tracknum, 5);
|
||||
assert.strictEqual(metadata.trackcount, undefined);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
12
gaia/dev_apps/music-oga/test/unit/metadata/mock_album_art_cache.js
Executable file
12
gaia/dev_apps/music-oga/test/unit/metadata/mock_album_art_cache.js
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
/* exported MockAlbumArtCache */
|
||||
'use strict';
|
||||
|
||||
|
||||
var MockAlbumArtCache = {
|
||||
getThumbnailURL: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
getThumbnailBlob: function() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
/* globals MockDOMRequest */
|
||||
/* exported MockGetDeviceStorage */
|
||||
|
||||
var MockFileStore = {
|
||||
_data: {},
|
||||
|
||||
get: function(store, filename) {
|
||||
if (this._data[store]) {
|
||||
return this._data[store][filename];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
set: function(store, filename, data) {
|
||||
if (!(store in this._data)) {
|
||||
this._data[store] = {};
|
||||
}
|
||||
this._data[store][filename] = data;
|
||||
},
|
||||
|
||||
readAsText: function(store, filename) {
|
||||
var self = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsText(self.get(store, filename));
|
||||
reader.onload = function() {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = function() {
|
||||
reject(reader.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var MockGetDeviceStorage = function(store) {
|
||||
return {
|
||||
get: function(filename) {
|
||||
return {
|
||||
set onsuccess(cb) {
|
||||
if (MockFileStore.get(store, filename)) {
|
||||
cb.call(this);
|
||||
}
|
||||
},
|
||||
set onerror(cb) {
|
||||
if (!MockFileStore.get(store, filename)) {
|
||||
cb.call(this);
|
||||
}
|
||||
},
|
||||
get result() {
|
||||
return MockFileStore.get(store, filename);
|
||||
}
|
||||
};
|
||||
},
|
||||
addNamed: function(blob, filename) {
|
||||
var file = new Blob([blob], {type: blob.type});
|
||||
file.name = filename;
|
||||
MockFileStore.set(store, filename, file);
|
||||
|
||||
return {
|
||||
set onsuccess(cb) {
|
||||
cb({ target: {result: filename} });
|
||||
}
|
||||
};
|
||||
},
|
||||
addEventListener: function() {
|
||||
// Do nothing
|
||||
},
|
||||
available: function() {
|
||||
return {
|
||||
set onsuccess(cb) {
|
||||
setTimeout(cb);
|
||||
},
|
||||
get result() {
|
||||
return 'available';
|
||||
}
|
||||
};
|
||||
},
|
||||
// need include libraries to test following domrequest functions
|
||||
// * /shared/test/unit/mocks/mock_event_target.js
|
||||
// * /shared/test/unit/mocks/mock_dom_request.js
|
||||
freeSpace: function freeSpace() {
|
||||
return new MockDOMRequest();
|
||||
},
|
||||
usedSpace: function usedSpace() {
|
||||
return new MockDOMRequest();
|
||||
}
|
||||
};
|
||||
};
|
||||
45
gaia/dev_apps/music-oga/test/unit/metadata/mp4_test.js
Executable file
45
gaia/dev_apps/music-oga/test/unit/metadata/mp4_test.js
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
/* global MP4Metadata, fetchBuffer, makeBlobView, readPicSlice,
|
||||
assertBuffersEqual, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/js/metadata/mp4.js');
|
||||
|
||||
suite('mp4', function () {
|
||||
var expectedPicture;
|
||||
setup(function(done) {
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('m4a tags', function(done) {
|
||||
var blob;
|
||||
fetchBuffer('/test-data/aac-tags.m4a')
|
||||
.then(function(buffer) {
|
||||
return (blob = new Blob([buffer]));
|
||||
})
|
||||
.then(makeBlobView)
|
||||
.then(MP4Metadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'mp4');
|
||||
assert.strictEqual(metadata.artist, 'The Tragically Hip');
|
||||
assert.strictEqual(metadata.album, 'Trouble At The Henhouse');
|
||||
assert.strictEqual(metadata.title, 'Giftshop');
|
||||
assert.strictEqual(metadata.tracknum, 1);
|
||||
assert.strictEqual(metadata.trackcount, 12);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
assert.strictEqual(metadata.picture.flavor, 'embedded');
|
||||
assert.strictEqual(metadata.picture.type, 'image/jpeg');
|
||||
|
||||
return readPicSlice(blob, metadata.picture);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
49
gaia/dev_apps/music-oga/test/unit/metadata/ogg_test.js
Executable file
49
gaia/dev_apps/music-oga/test/unit/metadata/ogg_test.js
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
/* global OggMetadata, fetchBlobView, fetchBuffer, MockLazyLoader, readBlob,
|
||||
assertBuffersEqual, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/metadata/utils.js');
|
||||
require('/js/metadata/ogg.js');
|
||||
require('/js/metadata/vorbis_picture.js');
|
||||
|
||||
suite('ogg tags', function() {
|
||||
var RealLazyLoader;
|
||||
var expectedPicture;
|
||||
|
||||
setup(function(done) {
|
||||
RealLazyLoader = window.LazyLoader;
|
||||
window.LazyLoader = MockLazyLoader;
|
||||
|
||||
fetchBuffer('/test-data/album-art.jpg').then(function(buffer) {
|
||||
expectedPicture = buffer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.LazyLoader = RealLazyLoader;
|
||||
});
|
||||
|
||||
test('vorbis comment', function(done) {
|
||||
fetchBlobView('/test-data/vorbis-c.ogg')
|
||||
.then(OggMetadata.parse)
|
||||
.then(function(metadata) {
|
||||
assert.strictEqual(metadata.tag_format, 'vorbis');
|
||||
assert.strictEqual(metadata.artist, 'Angra');
|
||||
assert.strictEqual(metadata.album, 'Holy Land');
|
||||
assert.strictEqual(metadata.title, 'Carolina IV');
|
||||
assert.strictEqual(metadata.tracknum, 4);
|
||||
assert.strictEqual(metadata.trackcount, 10);
|
||||
assert.strictEqual(metadata.discnum, 1);
|
||||
assert.strictEqual(metadata.disccount, 1);
|
||||
assert.strictEqual(metadata.picture.flavor, 'unsynced');
|
||||
assert.strictEqual(metadata.picture.blob.type, 'image/jpeg');
|
||||
|
||||
return readBlob(metadata.picture.blob);
|
||||
})
|
||||
.then(function(buffer) {
|
||||
assertBuffersEqual(buffer, expectedPicture);
|
||||
})
|
||||
.then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
72
gaia/dev_apps/music-oga/test/unit/metadata/utils.js
Executable file
72
gaia/dev_apps/music-oga/test/unit/metadata/utils.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
/* global BlobView */
|
||||
/* exported fetchBlobView, fetchBuffer, readPicSlice, assertBuffersEqual */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/blobview.js');
|
||||
require('/js/metadata/formats.js');
|
||||
require('/test/unit/mock_lazy_loader.js');
|
||||
require('/test/unit/utils.js');
|
||||
|
||||
function fetchBuffer(url) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status !== 200) {
|
||||
reject(new Error('Failed with status: ' + xhr.status));
|
||||
} else {
|
||||
resolve(this.response);
|
||||
}
|
||||
};
|
||||
xhr.onerror = xhr.ontimeout = function() {
|
||||
reject(new Error('Failed'));
|
||||
};
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function makeBlobView(blob, size) {
|
||||
if (size === undefined) {
|
||||
size = 64 * 1024;
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
BlobView.get(blob, 0, size, function(blobview, error) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(blobview);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fetchBlobView(url, size) {
|
||||
return fetchBuffer(url).then(function(buffer) {
|
||||
return makeBlobView(new Blob([buffer]), size);
|
||||
});
|
||||
}
|
||||
|
||||
function readBlob(blob) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob);
|
||||
reader.onload = function(event) {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = function(event) {
|
||||
reject(event.target.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function readPicSlice(blob, picture) {
|
||||
var slice = blob.slice(picture.start, picture.end, picture.type);
|
||||
return readBlob(slice);
|
||||
}
|
||||
|
||||
function assertBuffersEqual(a, b) {
|
||||
return assert.deepEqual(new Uint8Array(a), new Uint8Array(b));
|
||||
}
|
||||
17
gaia/dev_apps/music-oga/test/unit/mock_async_storage.js
Executable file
17
gaia/dev_apps/music-oga/test/unit/mock_async_storage.js
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
/* exported MockAsyncStorage */
|
||||
'use strict';
|
||||
var MockAsyncStorage = {
|
||||
user_data: {},
|
||||
|
||||
getItem: function(key, callback) {
|
||||
callback(this.user_data[key]);
|
||||
},
|
||||
|
||||
setItem: function(key, value) {
|
||||
this.user_data[key] = value;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.user_data = {};
|
||||
}
|
||||
};
|
||||
79
gaia/dev_apps/music-oga/test/unit/mock_db.js
Executable file
79
gaia/dev_apps/music-oga/test/unit/mock_db.js
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
/* exported MockDatabase */
|
||||
'use strict';
|
||||
|
||||
var MockDatabase = {
|
||||
|
||||
cancelEnumeration: function() {},
|
||||
|
||||
_mockResultsMetadataAlbum: [
|
||||
{
|
||||
metadata: {
|
||||
album: 'Crime of the Century',
|
||||
artist: 'Supertramp',
|
||||
title: 'School'
|
||||
}
|
||||
},
|
||||
{
|
||||
metadata: {
|
||||
album: 'Crisis? What Crisis?',
|
||||
artist: 'Supertramp',
|
||||
title: 'Lady'
|
||||
}
|
||||
},
|
||||
{
|
||||
metadata: {
|
||||
album: 'Even in the Quietests Moments...',
|
||||
artist: 'Supertramp',
|
||||
title: 'Fool\'s Overture'
|
||||
}
|
||||
},
|
||||
{
|
||||
metadata: {
|
||||
album: 'Breakfast in America',
|
||||
artist: 'Supertramp',
|
||||
title: 'Goodbye Stranger'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
enumerate: function(key, range, direction, callback) {
|
||||
|
||||
switch (key) {
|
||||
case 'metadata.album':
|
||||
this._mockResultsMetadataAlbum.forEach((entry) => callback(entry));
|
||||
// the end
|
||||
callback(null);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
|
||||
enumerateAll: function(key, range, direction, callback) {
|
||||
var results = [];
|
||||
|
||||
switch (key) {
|
||||
case 'metadata.album':
|
||||
results = results.concat(this._mockResultsMetadataAlbum);
|
||||
break;
|
||||
}
|
||||
|
||||
callback(results);
|
||||
},
|
||||
|
||||
advancedEnumerate: function(key, range, direction, index, callback) {
|
||||
// XXX: This assumes that the callback immediately calls cancelEnumeration!
|
||||
callback(this._mockResultsMetadataAlbum[index]);
|
||||
},
|
||||
|
||||
count: function(key, range, callback) {
|
||||
var result = 0;
|
||||
|
||||
switch (key) {
|
||||
case 'metadata.album':
|
||||
result = this._mockResultsMetadataAlbum.length;
|
||||
break;
|
||||
}
|
||||
|
||||
callback(result);
|
||||
}
|
||||
};
|
||||
19
gaia/dev_apps/music-oga/test/unit/mock_lazy_loader.js
Executable file
19
gaia/dev_apps/music-oga/test/unit/mock_lazy_loader.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
/* exported MockLazyLoader */
|
||||
|
||||
// Our MockLazyLoader is better than the shared one because we actually use a
|
||||
// Promise for load(). However, trying to do that with the shared version breaks
|
||||
// all the system app's tests. Oh well.
|
||||
var MockLazyLoader = {
|
||||
load: function(fileArray, callback) {
|
||||
if (callback) {
|
||||
callback();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
getJSON: function(file) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
};
|
||||
14
gaia/dev_apps/music-oga/test/unit/mock_music.js
Executable file
14
gaia/dev_apps/music-oga/test/unit/mock_music.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
/* exported MockApp */
|
||||
'use strict';
|
||||
|
||||
var MockApp = {
|
||||
pendingPick: null,
|
||||
currentOverlay: null,
|
||||
knownSongs: [],
|
||||
// Exported functions
|
||||
showOverlay: function() {},
|
||||
showCorrectOverlay: function() {},
|
||||
dbEnumerable: function() {},
|
||||
dbReady: function() {},
|
||||
refreshViews: function() {}
|
||||
};
|
||||
254
gaia/dev_apps/music-oga/test/unit/queue_test.js
Executable file
254
gaia/dev_apps/music-oga/test/unit/queue_test.js
Executable file
|
|
@ -0,0 +1,254 @@
|
|||
/* global MockAsyncStorage, MockDatabase, PlaybackQueue, pass, fail */
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/mock_async_storage.js');
|
||||
require('/test/unit/mock_db.js');
|
||||
require('/test/unit/utils.js');
|
||||
require('/js/queue.js');
|
||||
|
||||
suite('playback queues', () => {
|
||||
var RealAsyncStorage, RealDatabase;
|
||||
|
||||
setup(() => {
|
||||
RealAsyncStorage = window.asyncStorage;
|
||||
window.asyncStorage = MockAsyncStorage;
|
||||
window.asyncStorage.clear();
|
||||
|
||||
RealDatabase = window.Database;
|
||||
window.Database = MockDatabase;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
window.asyncStorage = RealAsyncStorage;
|
||||
window.Database = RealDatabase;
|
||||
});
|
||||
|
||||
suite('settings', () => {
|
||||
|
||||
test('load default settings', (done) => {
|
||||
PlaybackQueue.loadSettings().then(() => {
|
||||
assert.strictEqual(PlaybackQueue.repeat, PlaybackQueue.Repeat.OFF);
|
||||
assert.strictEqual(PlaybackQueue.shuffle, false);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('save settings', (done) => {
|
||||
PlaybackQueue.loadSettings().then(() => {
|
||||
PlaybackQueue.repeat = PlaybackQueue.Repeat.SONG;
|
||||
PlaybackQueue.shuffle = true;
|
||||
return PlaybackQueue.loadSettings();
|
||||
}).then(() => {
|
||||
assert.strictEqual(PlaybackQueue.repeat, PlaybackQueue.Repeat.SONG);
|
||||
assert.strictEqual(PlaybackQueue.shuffle, true);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var infos = [
|
||||
{name: 'static queue', creator: (fileinfos, index) => {
|
||||
return new PlaybackQueue.StaticQueue(fileinfos, index);
|
||||
}},
|
||||
{name: 'dynamic queue', creator: (fileinfos, index) => {
|
||||
return new PlaybackQueue.DynamicQueue({count: fileinfos.length}, index);
|
||||
}},
|
||||
];
|
||||
|
||||
infos.forEach(({name: name, creator: creator}) => {
|
||||
|
||||
suite(name, () => {
|
||||
var fileinfos;
|
||||
|
||||
function checkCurrent(queue, rawIndex, shuffledIndices = null) {
|
||||
var index = shuffledIndices ? shuffledIndices[rawIndex] : rawIndex;
|
||||
|
||||
assert.strictEqual(queue.rawIndex, rawIndex);
|
||||
assert.strictEqual(queue.index, index);
|
||||
return queue.current().then((info) => {
|
||||
assert.deepEqual(info, fileinfos[index]);
|
||||
});
|
||||
}
|
||||
|
||||
setup((done) => {
|
||||
fileinfos = MockDatabase._mockResultsMetadataAlbum;
|
||||
PlaybackQueue.loadSettings().then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
suite('basic', () => {
|
||||
var queue;
|
||||
|
||||
setup(() => {
|
||||
queue = creator(fileinfos);
|
||||
});
|
||||
|
||||
test('length', () => {
|
||||
assert.strictEqual(queue.length, fileinfos.length);
|
||||
});
|
||||
|
||||
test('index', () => {
|
||||
assert.strictEqual(queue.index, 0);
|
||||
});
|
||||
|
||||
test('rawIndex', () => {
|
||||
assert.strictEqual(queue.rawIndex, 0);
|
||||
});
|
||||
|
||||
test('current()', (done) => {
|
||||
checkCurrent(queue, 0).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('next()', (done) => {
|
||||
queue.next();
|
||||
checkCurrent(queue, 1).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('previous()', (done) => {
|
||||
queue.next();
|
||||
queue.previous();
|
||||
checkCurrent(queue, 0).then(() => {
|
||||
queue.previous();
|
||||
return checkCurrent(queue, 0);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('start at known index', (done) => {
|
||||
queue = creator(fileinfos, 1);
|
||||
checkCurrent(queue, 1).then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
suite('repeat list', () => {
|
||||
var queue;
|
||||
|
||||
setup(() => {
|
||||
PlaybackQueue.repeat = PlaybackQueue.Repeat.LIST;
|
||||
queue = creator(fileinfos);
|
||||
});
|
||||
|
||||
test('next()', (done) => {
|
||||
queue.next();
|
||||
queue.next();
|
||||
queue.next();
|
||||
queue.next();
|
||||
|
||||
checkCurrent(queue, 0).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('previous()', (done) => {
|
||||
queue.previous();
|
||||
checkCurrent(queue, 3).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('start at known index', (done) => {
|
||||
queue = creator(fileinfos, 3);
|
||||
checkCurrent(queue, 3).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 0);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
suite('repeat song', () => {
|
||||
var queue;
|
||||
|
||||
setup(() => {
|
||||
PlaybackQueue.repeat = PlaybackQueue.Repeat.SONG;
|
||||
queue = creator(fileinfos);
|
||||
});
|
||||
|
||||
test('next(false)', (done) => {
|
||||
queue.next();
|
||||
checkCurrent(queue, 1).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('next(true)', (done) => {
|
||||
queue.next(true);
|
||||
checkCurrent(queue, 0).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('previous()', (done) => {
|
||||
queue.previous();
|
||||
checkCurrent(queue, 0).then(pass(done), fail(done));
|
||||
});
|
||||
});
|
||||
|
||||
suite('shuffle', () => {
|
||||
var queue, RealRandom, seed;
|
||||
|
||||
function MockRandom() {
|
||||
var x = Math.sin(seed++) * 1000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
seed = 1;
|
||||
RealRandom = Math.random;
|
||||
Math.random = MockRandom;
|
||||
|
||||
PlaybackQueue.shuffle = true;
|
||||
|
||||
queue = creator(fileinfos);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
Math.random = RealRandom;
|
||||
});
|
||||
|
||||
test('shuffled order', (done) => {
|
||||
var expectedIndices = [3, 2, 0, 1];
|
||||
|
||||
checkCurrent(queue, 0, expectedIndices).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 1, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 2, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 3, expectedIndices);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('repeat list', (done) => {
|
||||
PlaybackQueue.repeat = PlaybackQueue.Repeat.LIST;
|
||||
var expectedIndices = [3, 1, 2, 0];
|
||||
|
||||
queue.next();
|
||||
queue.next();
|
||||
queue.next();
|
||||
queue.next();
|
||||
|
||||
checkCurrent(queue, 0, expectedIndices).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 1, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 2, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 3, expectedIndices);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
test('start at known index', (done) => {
|
||||
var expectedIndices = [1, 2, 3, 0];
|
||||
queue = creator(fileinfos, 1);
|
||||
|
||||
checkCurrent(queue, 0, expectedIndices).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 1, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 2, expectedIndices);
|
||||
}).then(() => {
|
||||
queue.next();
|
||||
return checkCurrent(queue, 3, expectedIndices);
|
||||
}).then(pass(done), fail(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
524
gaia/dev_apps/music-oga/test/unit/remote_controls_test.js
Executable file
524
gaia/dev_apps/music-oga/test/unit/remote_controls_test.js
Executable file
|
|
@ -0,0 +1,524 @@
|
|||
/* global MediaRemoteControls, REMOTE_CONTROLS, AVRCP, IAC,
|
||||
MockBluetoothHelper, MockBluetoothHelperInstance,
|
||||
MockNavigatormozSetMessageHandler */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/media/remote_controls.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
|
||||
require('/shared/test/unit/mocks/mock_bluetooth_helper.js');
|
||||
|
||||
suite('Media Remote Controls', function() {
|
||||
var mrc;
|
||||
var realSetMessageHandler;
|
||||
var realBluetoothHelper;
|
||||
|
||||
setup(function() {
|
||||
realSetMessageHandler = navigator.mozSetMessageHandler;
|
||||
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
|
||||
MockNavigatormozSetMessageHandler.mSetup();
|
||||
|
||||
realBluetoothHelper = window.BluetoothHelper;
|
||||
window.BluetoothHelper = MockBluetoothHelper;
|
||||
mrc = new MediaRemoteControls();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockNavigatormozSetMessageHandler.mTeardown();
|
||||
navigator.mozSetMessageHandler = realSetMessageHandler;
|
||||
|
||||
window.BluetoothHelper = realBluetoothHelper;
|
||||
});
|
||||
|
||||
suite('AVRCP commands', function() {
|
||||
test('AVRCP.PLAY_PRESS', function() {
|
||||
var playListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PLAY].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
mrc._commandHandler(AVRCP.PLAY_PRESS);
|
||||
assert.ok(playListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.PLAY_PAUSE_PRESS', function() {
|
||||
var playpauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY_PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.PLAY_PAUSE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener);
|
||||
mrc._commandHandler(AVRCP.PLAY_PAUSE_PRESS);
|
||||
assert.ok(playpauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.PAUSE_PRESS', function() {
|
||||
var pauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PAUSE].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
mrc._commandHandler(AVRCP.PAUSE_PRESS);
|
||||
assert.ok(pauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.STOP_PRESS', function() {
|
||||
var stopListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.STOP);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.STOP, stopListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.STOP].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.STOP, stopListener);
|
||||
mrc._commandHandler(AVRCP.STOP_PRESS);
|
||||
assert.ok(stopListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.NEXT_PRESS', function() {
|
||||
var nextListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.NEXT);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.NEXT, nextListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.NEXT].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.NEXT, nextListener);
|
||||
mrc._commandHandler(AVRCP.NEXT_PRESS);
|
||||
assert.ok(nextListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.PREVIOUS_PRESS', function() {
|
||||
var previousListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PREVIOUS);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PREVIOUS, previousListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PREVIOUS].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PREVIOUS, previousListener);
|
||||
mrc._commandHandler(AVRCP.PREVIOUS_PRESS);
|
||||
assert.ok(previousListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.FAST_FORWARD_PRESS', function() {
|
||||
var seekpressListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
var direction = event.detail.direction;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_PRESS);
|
||||
assert.strictEqual(direction, 1);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_PRESS, seekpressListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_PRESS].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_PRESS, seekpressListener);
|
||||
mrc._commandHandler(AVRCP.FAST_FORWARD_PRESS);
|
||||
assert.ok(seekpressListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.REWIND_PRESS', function() {
|
||||
var seekpressListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
var direction = event.detail.direction;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_PRESS);
|
||||
assert.strictEqual(direction, -1);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_PRESS, seekpressListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_PRESS].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_PRESS, seekpressListener);
|
||||
mrc._commandHandler(AVRCP.REWIND_PRESS);
|
||||
assert.ok(seekpressListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.FAST_FORWARD_RELEASE', function() {
|
||||
var seekreleaseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_RELEASE);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_RELEASE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener);
|
||||
mrc._commandHandler(AVRCP.FAST_FORWARD_RELEASE);
|
||||
assert.ok(seekreleaseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.REWIND_RELEASE', function() {
|
||||
var seekreleaseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_RELEASE);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_RELEASE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener);
|
||||
mrc._commandHandler(AVRCP.REWIND_RELEASE);
|
||||
assert.ok(seekreleaseListener.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('AVRCP commands(SCO is enabled)', function() {
|
||||
setup(function() {
|
||||
mrc._isSCOConnected = true;
|
||||
});
|
||||
|
||||
test('AVRCP.PLAY_PRESS', function() {
|
||||
var playListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PLAY].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
mrc._commandHandler(AVRCP.PLAY_PRESS);
|
||||
assert.ok(playListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.PLAY_PAUSE_PRESS', function() {
|
||||
var playpauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY_PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.PLAY_PAUSE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener);
|
||||
mrc._commandHandler(AVRCP.PLAY_PAUSE_PRESS);
|
||||
assert.ok(playpauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('AVRCP.PAUSE_PRESS', function() {
|
||||
var pauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PAUSE].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
mrc._commandHandler(AVRCP.PAUSE_PRESS);
|
||||
assert.ok(pauseListener.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('IAC commands', function() {
|
||||
test('IAC.PLAY_PRESS', function() {
|
||||
var playListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PLAY].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
mrc._commandHandler(IAC.PLAY_PRESS);
|
||||
assert.ok(playListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.PLAY_PAUSE_PRESS', function() {
|
||||
var playpauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY_PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.PLAY_PAUSE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener);
|
||||
mrc._commandHandler(IAC.PLAY_PAUSE_PRESS);
|
||||
assert.ok(playpauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.PAUSE_PRESS', function() {
|
||||
var pauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, false);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PAUSE].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
mrc._commandHandler(IAC.PAUSE_PRESS);
|
||||
assert.ok(pauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.STOP_PRESS', function() {
|
||||
var stopListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.STOP);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.STOP, stopListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.STOP].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.STOP, stopListener);
|
||||
mrc._commandHandler(IAC.STOP_PRESS);
|
||||
assert.ok(stopListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.NEXT_PRESS', function() {
|
||||
var nextListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.NEXT);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.NEXT, nextListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.NEXT].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.NEXT, nextListener);
|
||||
mrc._commandHandler(IAC.NEXT_PRESS);
|
||||
assert.ok(nextListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.PREVIOUS_PRESS', function() {
|
||||
var previousListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PREVIOUS);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PREVIOUS, previousListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PREVIOUS].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PREVIOUS, previousListener);
|
||||
mrc._commandHandler(IAC.PREVIOUS_PRESS);
|
||||
assert.ok(previousListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.FAST_FORWARD_PRESS', function() {
|
||||
var seekpressListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
var direction = event.detail.direction;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_PRESS);
|
||||
assert.strictEqual(direction, 1);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_PRESS, seekpressListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_PRESS].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_PRESS, seekpressListener);
|
||||
mrc._commandHandler(IAC.FAST_FORWARD_PRESS);
|
||||
assert.ok(seekpressListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.REWIND_PRESS', function() {
|
||||
var seekpressListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
var direction = event.detail.direction;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_PRESS);
|
||||
assert.strictEqual(direction, -1);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_PRESS, seekpressListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_PRESS].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_PRESS, seekpressListener);
|
||||
mrc._commandHandler(IAC.REWIND_PRESS);
|
||||
assert.ok(seekpressListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.FAST_FORWARD_RELEASE', function() {
|
||||
var seekreleaseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_RELEASE);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_RELEASE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener);
|
||||
mrc._commandHandler(IAC.FAST_FORWARD_RELEASE);
|
||||
assert.ok(seekreleaseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.REWIND_RELEASE', function() {
|
||||
var seekreleaseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.SEEK_RELEASE);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.SEEK_RELEASE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.SEEK_RELEASE, seekreleaseListener);
|
||||
mrc._commandHandler(IAC.REWIND_RELEASE);
|
||||
assert.ok(seekreleaseListener.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('IAC commands(SCO is enabled)', function() {
|
||||
setup(function() {
|
||||
mrc._isSCOConnected = true;
|
||||
});
|
||||
|
||||
test('IAC.PLAY_PRESS', function() {
|
||||
var playListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PLAY].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY, playListener);
|
||||
mrc._commandHandler(IAC.PLAY_PRESS);
|
||||
assert.ok(playListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.PLAY_PAUSE_PRESS', function() {
|
||||
var playpauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PLAY_PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.PLAY_PAUSE].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PLAY_PAUSE, playpauseListener);
|
||||
mrc._commandHandler(IAC.PLAY_PAUSE_PRESS);
|
||||
assert.ok(playpauseListener.calledOnce);
|
||||
});
|
||||
|
||||
test('IAC.PAUSE_PRESS', function() {
|
||||
var pauseListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.PAUSE);
|
||||
var isSCOConnected = event.detail.isSCOConnected;
|
||||
assert.equal(isSCOConnected, true);
|
||||
mrc.removeCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
assert.equal(mrc._commandListeners[REMOTE_CONTROLS.PAUSE].length, 0);
|
||||
});
|
||||
mrc.addCommandListener(REMOTE_CONTROLS.PAUSE, pauseListener);
|
||||
mrc._commandHandler(IAC.PAUSE_PRESS);
|
||||
assert.ok(pauseListener.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('UPDATE commands', function() {
|
||||
test('REMOTE_CONTROLS.UPDATE_METADATA', function() {
|
||||
var updatemetadataListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.UPDATE_METADATA);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.UPDATE_METADATA, updatemetadataListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.UPDATE_METADATA].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(
|
||||
REMOTE_CONTROLS.UPDATE_METADATA, updatemetadataListener
|
||||
);
|
||||
mrc._commandHandler(REMOTE_CONTROLS.UPDATE_METADATA);
|
||||
assert.ok(updatemetadataListener.calledOnce);
|
||||
});
|
||||
|
||||
test('REMOTE_CONTROLS.UPDATE_PLAYSTATUS', function() {
|
||||
var updateplaystatusListener = this.sinon.spy(function(event) {
|
||||
var command = event.detail.command;
|
||||
assert.equal(command, REMOTE_CONTROLS.UPDATE_PLAYSTATUS);
|
||||
mrc.removeCommandListener(
|
||||
REMOTE_CONTROLS.UPDATE_PLAYSTATUS, updateplaystatusListener
|
||||
);
|
||||
assert.equal(
|
||||
mrc._commandListeners[REMOTE_CONTROLS.UPDATE_PLAYSTATUS].length, 0
|
||||
);
|
||||
});
|
||||
mrc.addCommandListener(
|
||||
REMOTE_CONTROLS.UPDATE_PLAYSTATUS, updateplaystatusListener
|
||||
);
|
||||
mrc._commandHandler(REMOTE_CONTROLS.UPDATE_PLAYSTATUS);
|
||||
assert.ok(updateplaystatusListener.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setup Bluetooth', function() {
|
||||
test('_bluetoothHelper is ready', function() {
|
||||
mrc._setupBluetooth();
|
||||
|
||||
assert.equal(mrc._bluetoothHelper, MockBluetoothHelperInstance);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Bluetooth commands', function() {
|
||||
setup(function() {
|
||||
mrc._bluetoothHelper = new MockBluetoothHelper();
|
||||
});
|
||||
|
||||
test('notifyMetadataChanged', function() {
|
||||
this.sinon.spy(MockBluetoothHelperInstance, 'sendMediaMetaData');
|
||||
this.sinon.spy(mrc, '_postMessage');
|
||||
var metadata = this.sinon.stub();
|
||||
mrc.notifyMetadataChanged(metadata);
|
||||
|
||||
assert.ok(MockBluetoothHelperInstance.sendMediaMetaData
|
||||
.calledWith(metadata));
|
||||
assert.ok(mrc._postMessage.called);
|
||||
});
|
||||
|
||||
test('notifyStatusChanged', function() {
|
||||
this.sinon.spy(MockBluetoothHelperInstance, 'sendMediaPlayStatus');
|
||||
this.sinon.spy(mrc, '_postMessage');
|
||||
var status = 'good';
|
||||
mrc.notifyStatusChanged(status);
|
||||
|
||||
assert.ok(MockBluetoothHelperInstance.sendMediaPlayStatus
|
||||
.calledWith(status));
|
||||
assert.ok(mrc._postMessage.called);
|
||||
});
|
||||
|
||||
test('notifyStatusChanged not called when status is mozinterruptbegin',
|
||||
function() {
|
||||
this.sinon.spy(MockBluetoothHelperInstance, 'sendMediaPlayStatus');
|
||||
this.sinon.spy(mrc, '_postMessage');
|
||||
var status = 'mozinterruptbegin';
|
||||
mrc.notifyStatusChanged(status);
|
||||
|
||||
assert.ok(!MockBluetoothHelperInstance.sendMediaPlayStatus.called);
|
||||
assert.ok(!mrc._postMessage.called);
|
||||
});
|
||||
});
|
||||
});
|
||||
3
gaia/dev_apps/music-oga/test/unit/setup.js
Executable file
3
gaia/dev_apps/music-oga/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
94
gaia/dev_apps/music-oga/test/unit/tabbar_test.js
Executable file
94
gaia/dev_apps/music-oga/test/unit/tabbar_test.js
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
/* global TabBar, Sanitizer, MocksHelper */
|
||||
/* exported ListView, TilesView */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/shared/js/accessibility_helper.js');
|
||||
require('/js/ui/tab_bar.js');
|
||||
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/test/unit/ui/views/mock_list_view.js');
|
||||
require('/test/unit/ui/views/mock_tiles_view.js');
|
||||
|
||||
var mocksForTabBarHelper = new MocksHelper([
|
||||
'ListView',
|
||||
'TilesView',
|
||||
'ModeManager'
|
||||
]).init();
|
||||
|
||||
suite('TabBar', function() {
|
||||
|
||||
mocksForTabBarHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
this.element = document.createElement('div');
|
||||
this.element.setAttribute('id', 'tabs');
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<ul role="tablist" class="bb-tablist skin-dark">
|
||||
<li id="mix" role="presentation">
|
||||
<a id="tabs-mix" data-option="mix" role="tab"
|
||||
aria-selected="false" aria-controls="views-tiles"></a>
|
||||
</li>
|
||||
<li id="playlists" role="presentation">
|
||||
<a id="tabs-playlists" data-option="playlist" role="tab"
|
||||
aria-selected="false" aria-controls="views-list"></a>
|
||||
</li>
|
||||
<li id="artists" role="presentation">
|
||||
<a id="tabs-artists" data-option="artist" role="tab"
|
||||
aria-selected="false" aria-controls="views-list"></a>
|
||||
</li>
|
||||
<li id="albums" role="presentation">
|
||||
<a id="tabs-albums" data-option="album" role="tab"
|
||||
aria-selected="false" aria-controls="views-list"></a>
|
||||
</li>
|
||||
<li id="songs" role="presentation">
|
||||
<a id="tabs-songs" data-option="title" role="tab"
|
||||
aria-selected="false" aria-controls="views-list"></a>
|
||||
</li>
|
||||
</ul>`
|
||||
));
|
||||
document.body.appendChild(this.element);
|
||||
this.links = this.element.querySelectorAll('a');
|
||||
this.tabs = TabBar;
|
||||
this.tabs.init();
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
this.links = null;
|
||||
});
|
||||
|
||||
// send a fake touchend event to el
|
||||
function sendTouch(el) {
|
||||
var touchendEvent = document.createEvent('TouchEvent');
|
||||
touchendEvent.initTouchEvent('touchend', true, true, window,
|
||||
0, false, false, false, false,
|
||||
null, null, null);
|
||||
el.dispatchEvent(touchendEvent);
|
||||
}
|
||||
|
||||
// This are hardcoded based on tab_bar.js
|
||||
var tabOptions = ['mix', 'playlist', 'artist', 'album', 'title'];
|
||||
|
||||
[0, 1, 2, 3, 4].forEach(function(index) {
|
||||
var ariaStates = ['false', 'false', 'false', 'false', 'false'];
|
||||
ariaStates[index] = 'true';
|
||||
|
||||
suite('test click link ' + (index + 1), function() {
|
||||
setup(function() {
|
||||
sendTouch(this.links[index]);
|
||||
});
|
||||
test('test aria-selected set', function() {
|
||||
assert.equal(TabBar.option, tabOptions[index]);
|
||||
ariaStates.forEach(function(value, index) {
|
||||
assert.equal(this.links[index].getAttribute('aria-selected'), value);
|
||||
}, this);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
76
gaia/dev_apps/music-oga/test/unit/titlebar_test.js
Executable file
76
gaia/dev_apps/music-oga/test/unit/titlebar_test.js
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
/* global App, TitleBar, Sanitizer, MocksHelper, ModeManager, LazyLoader */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/shared/js/accessibility_helper.js');
|
||||
require('/js/ui/title_bar.js');
|
||||
|
||||
require('/test/unit/mock_music.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/test/unit/ui/views/mock_player_view.js');
|
||||
require('/test/unit/metadata/mock_album_art_cache.js');
|
||||
require('/test/unit/mock_lazy_loader.js');
|
||||
|
||||
var mocksForTitleBarHelper = new MocksHelper([
|
||||
'App',
|
||||
'LazyLoader',
|
||||
'AlbumArtCache',
|
||||
'ModeManager',
|
||||
'PlayerView'
|
||||
]).init();
|
||||
|
||||
suite('TitleBar', function() {
|
||||
|
||||
mocksForTitleBarHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.setAttribute('id', 'title');
|
||||
this.element.setAttribute('action', 'back');
|
||||
this.element.setAttribute('no-font-fit', null);
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<h1 id="title-text"><bdi data-l10n-id="music"></bdi></h1>
|
||||
<button id="title-player" data-icon="play-circle"
|
||||
data-l10n-id="go-to-player-view"></button>
|
||||
<button id="title-done" data-l10n-id="done">Done</button>`
|
||||
));
|
||||
document.body.appendChild(this.element);
|
||||
this.titleBar = TitleBar;
|
||||
this.titleBar.init();
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
});
|
||||
|
||||
suite('test player icon', function() {
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(ModeManager, 'push');
|
||||
var titlePlayer = document.getElementById('title-player');
|
||||
titlePlayer.click();
|
||||
});
|
||||
test('test modemanager push called', function() {
|
||||
assert.ok(spy.called);
|
||||
}, this);
|
||||
});
|
||||
|
||||
suite('test done button', function() {
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
App.pendingPick = { postResult: function() {} };
|
||||
spy = this.sinon.spy(LazyLoader, 'load');
|
||||
var titleDone = document.getElementById('title-done');
|
||||
titleDone.click();
|
||||
});
|
||||
test('test album art fetching triggered', function() {
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
});
|
||||
});
|
||||
18
gaia/dev_apps/music-oga/test/unit/ui/mock_tab_bar.js
Executable file
18
gaia/dev_apps/music-oga/test/unit/ui/mock_tab_bar.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/* exported MockTabBar */
|
||||
'use strict';
|
||||
|
||||
var MockTabBar = {
|
||||
init: function() {},
|
||||
|
||||
playlistArray: {
|
||||
indexOf: function() {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
|
||||
view: {
|
||||
classList: {
|
||||
toggle: function() {}
|
||||
}
|
||||
}
|
||||
};
|
||||
18
gaia/dev_apps/music-oga/test/unit/ui/mock_title_bar.js
Executable file
18
gaia/dev_apps/music-oga/test/unit/ui/mock_title_bar.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/* exported MockTitleBar */
|
||||
'use strict';
|
||||
|
||||
var MockTitleBar = {
|
||||
init: function() {},
|
||||
|
||||
view: function() {
|
||||
return document.getElementById('title');
|
||||
},
|
||||
|
||||
playerIcon: function() {
|
||||
return document.getElementById('title-player');
|
||||
},
|
||||
|
||||
showBackArrow: function() {},
|
||||
|
||||
changeTitleText: function() {}
|
||||
};
|
||||
130
gaia/dev_apps/music-oga/test/unit/ui/views/list_view_test.js
Executable file
130
gaia/dev_apps/music-oga/test/unit/ui/views/list_view_test.js
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
/* global ListView, Sanitizer, MocksHelper, ModeManager, FocusEvent,
|
||||
MODE_LIST */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/js/utils.js');
|
||||
require('/js/ui/views/list_view.js');
|
||||
|
||||
require('/test/unit/mock_db.js');
|
||||
require('/test/unit/mock_music.js');
|
||||
require('/test/unit/metadata/mock_album_art_cache.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/test/unit/ui/views/mock_subList_view.js');
|
||||
require('/test/unit/mock_lazy_loader.js');
|
||||
|
||||
var mocksForListViewHelper = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'AlbumArtCache',
|
||||
'ModeManager',
|
||||
'SubListView',
|
||||
'App',
|
||||
'Database'
|
||||
]).init();
|
||||
|
||||
suite('ListView test', function() {
|
||||
|
||||
mocksForListViewHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
|
||||
ModeManager._view = ListView;
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.id = 'views-list';
|
||||
document.body.appendChild(this.element);
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<form id="views-list-search" role="search" class="skin-dark">
|
||||
<button id="views-list-search-close" data-l10n-id="search-close"
|
||||
type="submit">close</button>
|
||||
<p>
|
||||
<input id="views-list-search-input" type="search"
|
||||
x-inputmode="verbatim"
|
||||
data-l10n-id="search-music" placeholder="SearcH MusiC">
|
||||
<button id="views-list-search-clear" type="reset">Clear</button>
|
||||
</p>
|
||||
</form>
|
||||
<div id="views-list-anchor" role="listbox"></div>`
|
||||
));
|
||||
|
||||
this.listView = ListView;
|
||||
|
||||
this.listView.init();
|
||||
|
||||
this.listView.activate( {
|
||||
key: 'metadata.album',
|
||||
range: null,
|
||||
direction: 'nextunique',
|
||||
option: 'album'
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
});
|
||||
|
||||
suite('activate', function() {
|
||||
|
||||
test('content', function() {
|
||||
// Mock data has 4 albums, with 3 different letters. So, 7 list items
|
||||
var children = this.listView.anchor.children;
|
||||
var count = children.length;
|
||||
assert.equal(count, 7);
|
||||
|
||||
children = this.listView.anchor.querySelectorAll('.list-item');
|
||||
count = children.length;
|
||||
assert.equal(count, 4);
|
||||
|
||||
var first = this.listView.anchor.firstChild;
|
||||
assert.equal(first.tagName, 'LI');
|
||||
assert.ok(first.classList.contains('list-header'));
|
||||
assert.equal(first.getAttribute('role'), 'heading');
|
||||
assert.equal(first.textContent, 'C');
|
||||
|
||||
first = this.listView.anchor.querySelector('.list-item');
|
||||
assert.equal(first.tagName, 'LI');
|
||||
assert.equal(first.getAttribute('role'), 'presentation');
|
||||
assert.equal(first.textContent, 'Crime of the CenturySupertramp');
|
||||
|
||||
var second = children[1];
|
||||
assert.equal(second.tagName, 'LI');
|
||||
assert.equal(first.getAttribute('role'), 'presentation');
|
||||
assert.equal(second.textContent, 'Crisis? What Crisis?Supertramp');
|
||||
});
|
||||
});
|
||||
|
||||
suite('events', function() {
|
||||
var spy;
|
||||
|
||||
test('click tile', function() {
|
||||
spy = this.sinon.spy(ListView, 'activateSubListView');
|
||||
var listItem = document.querySelector('.list-item > [role="option"]');
|
||||
assert.ok(listItem);
|
||||
listItem.click();
|
||||
assert.ok(spy.called,
|
||||
'ListView.activateSubListView() should have been called');
|
||||
});
|
||||
|
||||
test('click search close', function() {
|
||||
spy = this.sinon.spy(ListView, 'hideSearch');
|
||||
|
||||
document.getElementById('views-list-search-close').click();
|
||||
assert.ok(spy.called, 'ListView.hideSearch() should have been called');
|
||||
});
|
||||
|
||||
test('focus on search', function() {
|
||||
// make sure this return the right value
|
||||
this.sinon.stub(ModeManager, 'currentMode', () => MODE_LIST );
|
||||
var spy = this.sinon.spy(ModeManager, 'start');
|
||||
var input = document.getElementById('views-list-search-input');
|
||||
input.dispatchEvent(new FocusEvent('focus'));
|
||||
|
||||
assert.ok(spy.called, 'ModeManager.start() should have been called');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
9
gaia/dev_apps/music-oga/test/unit/ui/views/mock_list_view.js
Executable file
9
gaia/dev_apps/music-oga/test/unit/ui/views/mock_list_view.js
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
/* exported MockListView */
|
||||
'use strict';
|
||||
|
||||
var MockListView = {
|
||||
init: function() {},
|
||||
activate: function() {},
|
||||
cancelEnumeration: function () {},
|
||||
update: function() {}
|
||||
};
|
||||
31
gaia/dev_apps/music-oga/test/unit/ui/views/mock_modemanager.js
Executable file
31
gaia/dev_apps/music-oga/test/unit/ui/views/mock_modemanager.js
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
/* exported MockModeManager, MODE_LIST, MODE_SUBLIST, MODE_TILES, MODE_PLAYER,
|
||||
MODE_PICKER, MODE_SEARCH_FROM_TILES, MODE_SEARCH_FROM_LIST,
|
||||
SETTINGS_OPTION_KEY */
|
||||
'use strict';
|
||||
|
||||
var MODE_LIST = 0;
|
||||
var MODE_TILES = 1;
|
||||
var MODE_PLAYER = 2;
|
||||
var MODE_SUBLIST = 3;
|
||||
var MODE_SEARCH_FROM_TILES = 5;
|
||||
var MODE_SEARCH_FROM_LIST = 6;
|
||||
var MODE_PICKER = 7;
|
||||
|
||||
var SETTINGS_OPTION_KEY = 'settings_option_key';
|
||||
|
||||
var MockModeManager = {
|
||||
|
||||
_view: null,
|
||||
|
||||
get currentMode() {
|
||||
return 0;
|
||||
},
|
||||
start: function() {},
|
||||
push: function() {},
|
||||
waitForView: function(mode, callback) {
|
||||
assert.ok(this._view, 'You didn\'t set MockModeManager._view');
|
||||
callback(this._view);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
11
gaia/dev_apps/music-oga/test/unit/ui/views/mock_player_view.js
Executable file
11
gaia/dev_apps/music-oga/test/unit/ui/views/mock_player_view.js
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
/* exported MockPlayerView */
|
||||
'use strict';
|
||||
|
||||
var MockPlayerView = {
|
||||
get isQueued() { return true; },
|
||||
currentFileInfo: { metadata: {} },
|
||||
playingBlob: { type: null },
|
||||
|
||||
init: function() {},
|
||||
stop: function() {},
|
||||
};
|
||||
6
gaia/dev_apps/music-oga/test/unit/ui/views/mock_search_view.js
Executable file
6
gaia/dev_apps/music-oga/test/unit/ui/views/mock_search_view.js
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
/* exported MockSearchView */
|
||||
'use strict';
|
||||
|
||||
var MockSearchView = {
|
||||
init: function() {}
|
||||
};
|
||||
10
gaia/dev_apps/music-oga/test/unit/ui/views/mock_subList_view.js
Executable file
10
gaia/dev_apps/music-oga/test/unit/ui/views/mock_subList_view.js
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
/* exported MockSubListView */
|
||||
'use strict';
|
||||
|
||||
var MockSubListView = {
|
||||
init: function() {},
|
||||
|
||||
activate: function(option, data, index, keyRange, direction, callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
7
gaia/dev_apps/music-oga/test/unit/ui/views/mock_tiles_view.js
Executable file
7
gaia/dev_apps/music-oga/test/unit/ui/views/mock_tiles_view.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* exported MockTilesView */
|
||||
'use strict';
|
||||
|
||||
var MockTilesView = {
|
||||
init: function() {},
|
||||
hideSearch: function() {}
|
||||
};
|
||||
300
gaia/dev_apps/music-oga/test/unit/ui/views/mode_manager_test.js
Executable file
300
gaia/dev_apps/music-oga/test/unit/ui/views/mode_manager_test.js
Executable file
|
|
@ -0,0 +1,300 @@
|
|||
/* global MockL10n, ModeManager, MocksHelper, loadBodyHTML,
|
||||
MODE_TILES, MODE_LIST, MODE_SUBLIST, MODE_PLAYER,
|
||||
MODE_SEARCH_FROM_TILES, MODE_SEARCH_FROM_LIST, MODE_PICKER,
|
||||
TilesView, ListView, SubListView, PlayerView, SearchView */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_async_storage.js');
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/test/unit/mock_lazy_loader.js');
|
||||
require('/test/unit/mock_music.js');
|
||||
require('/test/unit/ui/mock_title_bar.js');
|
||||
require('/test/unit/ui/mock_tab_bar.js');
|
||||
require('/test/unit/ui/views/mock_tiles_view.js');
|
||||
require('/test/unit/ui/views/mock_list_view.js');
|
||||
require('/test/unit/ui/views/mock_subList_view.js');
|
||||
require('/test/unit/ui/views/mock_player_view.js');
|
||||
require('/test/unit/ui/views/mock_search_view.js');
|
||||
require('/js/ui/views/mode_manager.js');
|
||||
|
||||
var mocksForModeManagerHelper = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'asyncStorage',
|
||||
'App',
|
||||
'TitleBar',
|
||||
'TabBar',
|
||||
'TilesView',
|
||||
'ListView',
|
||||
'SubListView',
|
||||
'PlayerView',
|
||||
'SearchView'
|
||||
]).init();
|
||||
|
||||
suite('Mode Manager Test', function() {
|
||||
var realL10n;
|
||||
var mm;
|
||||
|
||||
mocksForModeManagerHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
loadBodyHTML('/index.html');
|
||||
|
||||
mm = ModeManager;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realL10n;
|
||||
});
|
||||
|
||||
suite('Start some views', function() {
|
||||
test('Start TILES', function() {
|
||||
mm.start(MODE_TILES, function() {
|
||||
assert.equal(mm.currentMode, MODE_TILES);
|
||||
});
|
||||
});
|
||||
|
||||
test('Start LIST', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
assert.equal(mm.currentMode, MODE_LIST);
|
||||
});
|
||||
});
|
||||
|
||||
test('Start PICKER', function() {
|
||||
mm.start(MODE_PICKER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PICKER);
|
||||
});
|
||||
});
|
||||
|
||||
test('Start SEARCH(From Tiles)', function() {
|
||||
mm.start(MODE_SEARCH_FROM_TILES, function() {
|
||||
assert.equal(mm.currentMode, MODE_SEARCH_FROM_TILES);
|
||||
});
|
||||
});
|
||||
|
||||
test('Start SEARCH(From List)', function() {
|
||||
mm.start(MODE_SEARCH_FROM_LIST, function() {
|
||||
assert.equal(mm.currentMode, MODE_SEARCH_FROM_LIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Start then push some views', function() {
|
||||
test('TILES > PLAYER', function() {
|
||||
mm.start(MODE_TILES, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > SUBLIST', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
assert.equal(mm.currentMode, MODE_SUBLIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > PLAYER', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('PICKER > PLAYER', function() {
|
||||
mm.start(MODE_PICKER, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(From Tiles) > PLAYER', function() {
|
||||
mm.start(MODE_SEARCH_FROM_TILES, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(From List) > PLAYER', function() {
|
||||
mm.start(MODE_SEARCH_FROM_LIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > SUBLIST > PLAYER', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(From Tiles) > SUBLIST > PLAYER', function() {
|
||||
mm.start(MODE_SEARCH_FROM_TILES, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(From List) > SUBLIST > PLAYER', function() {
|
||||
mm.start(MODE_SEARCH_FROM_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
assert.equal(mm.currentMode, MODE_PLAYER);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Start then pop some views', function() {
|
||||
test('TILES > PLAYER > TILES', function() {
|
||||
mm.start(MODE_TILES, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_TILES);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > PLAYER > LIST', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_LIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > SUBLIST > LIST', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_LIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('PICKER > PLAYER > PICKER', function() {
|
||||
mm.start(MODE_PICKER, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_PICKER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('LIST > SUBLIST > PLAYER > SUBLIST > LIST', function() {
|
||||
mm.start(MODE_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_LIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(Tiles) > SUBLIST > PLAYER > SUBLIST > SEARCH', function() {
|
||||
mm.start(MODE_SEARCH_FROM_TILES, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_SEARCH_FROM_TILES);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('SEARCH(List) > SUBLIST > PLAYER > SUBLIST > SEARCH', function() {
|
||||
mm.start(MODE_SEARCH_FROM_LIST, function() {
|
||||
mm.push(MODE_SUBLIST, function() {
|
||||
mm.push(MODE_PLAYER, function() {
|
||||
mm.pop();
|
||||
mm.pop();
|
||||
assert.equal(mm.currentMode, MODE_SEARCH_FROM_LIST);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Initialize some views', function() {
|
||||
setup(function() {
|
||||
for (var mode in mm.views) {
|
||||
mm.views[mode].isLoaded = false;
|
||||
}
|
||||
});
|
||||
|
||||
test('Initialize TILES', function() {
|
||||
var spy = this.sinon.spy(TilesView, 'init');
|
||||
|
||||
mm.waitForView(MODE_TILES, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Initialize LIST', function() {
|
||||
var spy = this.sinon.spy(ListView, 'init');
|
||||
|
||||
mm.waitForView(MODE_LIST, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Initialize SUBLIST', function() {
|
||||
var spy = this.sinon.spy(SubListView, 'init');
|
||||
|
||||
mm.waitForView(MODE_SUBLIST, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Initialize PLAYER', function() {
|
||||
var spy = this.sinon.spy(PlayerView, 'init');
|
||||
|
||||
mm.waitForView(MODE_PLAYER, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Initialize SEARCH(Tiles)', function() {
|
||||
var spy = this.sinon.spy(SearchView, 'init');
|
||||
|
||||
mm.waitForView(MODE_SEARCH_FROM_TILES, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Initialize SEARCH(List)', function() {
|
||||
var spy = this.sinon.spy(SearchView, 'init');
|
||||
|
||||
mm.waitForView(MODE_SEARCH_FROM_LIST, (view) => {
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.equal(view.isLoaded, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
197
gaia/dev_apps/music-oga/test/unit/ui/views/player_view_test.js
Executable file
197
gaia/dev_apps/music-oga/test/unit/ui/views/player_view_test.js
Executable file
|
|
@ -0,0 +1,197 @@
|
|||
/* global INTERRUPT_BEGIN, KeyboardEvent, KeyEvent, loadBodyHTML, MockL10n,
|
||||
MocksHelper, PlaybackQueue, PlayerView, PLAYSTATUS_PAUSED,
|
||||
PLAYSTATUS_PLAYING */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/js/ui/views/player_view.js');
|
||||
require('/js/queue.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_async_storage.js');
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
|
||||
var mocksForPlayerViewHelper = new MocksHelper([
|
||||
'asyncStorage'
|
||||
]).init();
|
||||
|
||||
function mockAudioElement() {
|
||||
// we need to replace the audio element
|
||||
var audioNode = document.getElementById('player-audio');
|
||||
var parent = audioNode.parentNode;
|
||||
parent.removeChild(audioNode);
|
||||
audioNode = document.createElement('div');
|
||||
audioNode.id = 'player-audio';
|
||||
audioNode.currentTime = 100;
|
||||
parent.appendChild(audioNode);
|
||||
}
|
||||
|
||||
suite('Player View Test', function() {
|
||||
var pv, ratings;
|
||||
var realL10n = navigator.mozL10n;
|
||||
|
||||
mocksForPlayerViewHelper.attachTestHelpers();
|
||||
|
||||
function testRatingsAriaChecked(checkedIndex) {
|
||||
pv.setRatings(checkedIndex);
|
||||
Array.prototype.forEach.call(ratings, function(rating, index) {
|
||||
assert.equal(checkedIndex - 1 === index ? 'true' : 'false',
|
||||
rating.getAttribute('aria-checked'));
|
||||
});
|
||||
}
|
||||
|
||||
suiteSetup(function(done) {
|
||||
navigator.mozL10n = MockL10n;
|
||||
//Insert the star-rating bar into the dom
|
||||
loadBodyHTML('/index.html');
|
||||
|
||||
//Initialize the Player View
|
||||
pv = PlayerView;
|
||||
|
||||
//Override #setSeekBar with stub to avoid excess work in init()
|
||||
sinon.stub(pv, 'setSeekBar');
|
||||
PlaybackQueue.loadSettings().then(() => {
|
||||
pv.init();
|
||||
ratings = pv.ratings;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realL10n;
|
||||
});
|
||||
|
||||
suite('#setRating accessibility', function() {
|
||||
test('aria-checked="true" when star matches star-rating. false otherwise',
|
||||
function() {
|
||||
[0, 1, 2, 3, 4, 5].forEach(testRatingsAriaChecked);
|
||||
});
|
||||
});
|
||||
|
||||
suite('show info', function() {
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(PlayerView, 'showInfo');
|
||||
});
|
||||
|
||||
test('on player cover', function() {
|
||||
['player-cover', 'player-cover-image-1',
|
||||
'player-cover-image-2'].forEach(function(id) {
|
||||
document.getElementById(id).click();
|
||||
assert.ok(spy.called,
|
||||
'PlayerView.showInfo should be called by click on ' + id);
|
||||
});
|
||||
});
|
||||
|
||||
test('on repeat', function() {
|
||||
document.getElementById('player-album-repeat').click();
|
||||
assert.ok(spy.called,
|
||||
'PlayerView.showInfo should be called by click on repeat');
|
||||
});
|
||||
});
|
||||
|
||||
suite('update remote play status on audio event', function() {
|
||||
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(PlayerView, 'updateRemotePlayStatus');
|
||||
});
|
||||
|
||||
function sendEventAndTest(event, status) {
|
||||
PlayerView.audio.dispatchEvent(new CustomEvent(event));
|
||||
assert.ok(spy.called);
|
||||
if (status) {
|
||||
assert.equal(PlayerView.playStatus, status);
|
||||
}
|
||||
}
|
||||
|
||||
test('on play', function() {
|
||||
sendEventAndTest('play', PLAYSTATUS_PLAYING);
|
||||
});
|
||||
|
||||
test('on pause', function() {
|
||||
sendEventAndTest('pause', PLAYSTATUS_PAUSED);
|
||||
});
|
||||
|
||||
test('on interrupt begin', function() {
|
||||
sendEventAndTest('mozinterruptbegin', INTERRUPT_BEGIN);
|
||||
});
|
||||
|
||||
test('on interrupt end', function() {
|
||||
sendEventAndTest('mozinterruptend', PLAYSTATUS_PLAYING);
|
||||
});
|
||||
});
|
||||
|
||||
suite('update seekbar', function() {
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(PlayerView, 'updateSeekBar');
|
||||
});
|
||||
|
||||
test('on duration change', function() {
|
||||
PlayerView.audio.dispatchEvent(new CustomEvent('durationchange'));
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
|
||||
test('on timeupdate', function() {
|
||||
PlayerView.audio.dispatchEvent(new CustomEvent('timeupdate'));
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
|
||||
test('on visibilitychange', function() {
|
||||
window.dispatchEvent(new CustomEvent('visibilitychange'));
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('context menu', function() {
|
||||
var spy;
|
||||
setup(function() {
|
||||
mockAudioElement();
|
||||
spy = this.sinon.spy(PlayerView, 'startFastSeeking');
|
||||
});
|
||||
|
||||
function sendEvent(id) {
|
||||
var el = document.getElementById(id);
|
||||
var ev = document.createEvent('MouseEvents');
|
||||
ev.initMouseEvent('contextmenu', true, false, window, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 2, null);
|
||||
el.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
test('previous button', function() {
|
||||
sendEvent('player-controls-previous');
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
|
||||
test('next button', function() {
|
||||
sendEvent('player-controls-next');
|
||||
assert.ok(spy.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('keyboard seek', function() {
|
||||
var spy;
|
||||
setup(function() {
|
||||
mockAudioElement();
|
||||
spy = this.sinon.spy(PlayerView, 'seekAudio');
|
||||
});
|
||||
|
||||
function sendEventAndTest(key) {
|
||||
PlayerView.seekSlider.dispatchEvent(new KeyboardEvent('keypress',
|
||||
{ keyCode: key }));
|
||||
assert.ok(spy.called);
|
||||
}
|
||||
|
||||
test('key up seek', function() {
|
||||
sendEventAndTest(KeyEvent.DOM_VK_UP);
|
||||
});
|
||||
|
||||
test('key down seek', function() {
|
||||
sendEventAndTest(KeyEvent.DOM_VK_DOWN);
|
||||
});
|
||||
});
|
||||
});
|
||||
138
gaia/dev_apps/music-oga/test/unit/ui/views/searchview_test.js
Executable file
138
gaia/dev_apps/music-oga/test/unit/ui/views/searchview_test.js
Executable file
|
|
@ -0,0 +1,138 @@
|
|||
/* global SearchView, Sanitizer, MocksHelper, FocusEvent */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/js/ui/views/search_view.js');
|
||||
|
||||
require('/test/unit/mock_music.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/test/unit/ui/mock_tab_bar.js');
|
||||
|
||||
var mocksForSearchViewHelper = new MocksHelper([
|
||||
'App',
|
||||
'ModeManager',
|
||||
'TabBar'
|
||||
]).init();
|
||||
|
||||
|
||||
suite('SearchView', function() {
|
||||
|
||||
mocksForSearchViewHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.setAttribute('id', 'views-search');
|
||||
this.element.setAttribute('action', 'back');
|
||||
this.element.setAttribute('no-font-fit', null);
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<form id="views-search-form" role="search" class="skin-dark">
|
||||
<button id="views-search-close" data-l10n-id="search-close"
|
||||
type="submit">close</button>
|
||||
<p>
|
||||
<input id="views-search-input" type="search" dir="auto"
|
||||
x-inputmode="verbatim"
|
||||
data-l10n-id="search-music" placeholder="SearcH MusiC">
|
||||
<button id="views-search-clear" type="reset">Clear</button>
|
||||
</p>
|
||||
</form>
|
||||
<div id="views-search-no-result" class="search-category hidden">
|
||||
<p id="search-no-result" data-l10n-id="search-no-result">No
|
||||
musiC founD</p>
|
||||
</div>
|
||||
<div id="views-search-anchor">
|
||||
<div id="views-search-artists" class="search-category hidden">
|
||||
<gaia-subheader skin="dark">
|
||||
<span data-l10n-id="search-artists">ArtistS</span>
|
||||
(<span class="search-result-count"></span>)
|
||||
</gaia-subheader>
|
||||
<div class="search-results" role="listbox"></div>
|
||||
</div>
|
||||
<div id="views-search-albums" class="search-category hidden">
|
||||
<gaia-subheader skin="dark">
|
||||
<span data-l10n-id="search-albums">AlbumS</span>
|
||||
(<span class="search-result-count"></span>)
|
||||
</gaia-subheader>
|
||||
<div class="search-results" role="listbox"></div>
|
||||
</div>
|
||||
<div id="views-search-titles" class="search-category hidden">
|
||||
<gaia-subheader skin="dark">
|
||||
<span data-l10n-id="search-titles">TrackS</span>
|
||||
(<span class="search-result-count"></span>)
|
||||
</gaia-subheader>
|
||||
<div class="search-results" role="listbox"></div>
|
||||
</div>
|
||||
</div>`
|
||||
));
|
||||
document.body.appendChild(this.element);
|
||||
this.searchView = SearchView;
|
||||
this.searchView.init();
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
});
|
||||
|
||||
// send a fake touchend event to el
|
||||
function sendTouch(el) {
|
||||
var touchendEvent = document.createEvent('TouchEvent');
|
||||
touchendEvent.initTouchEvent('touchend', true, true, window,
|
||||
0, false, false, false, false,
|
||||
null, null, null);
|
||||
el.dispatchEvent(touchendEvent);
|
||||
}
|
||||
|
||||
suite('test focus', function() {
|
||||
var spy;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(SearchView, 'switchContext');
|
||||
var searchInput = document.getElementById('views-search-input');
|
||||
searchInput.dispatchEvent(new FocusEvent('focus'));
|
||||
});
|
||||
|
||||
test('test context switched', function() {
|
||||
assert.ok(spy.called, 'SearchView.switchContext() should be called');
|
||||
assert.equal(SearchView.searchContext, SearchView.context.ALL);
|
||||
}, this);
|
||||
});
|
||||
|
||||
|
||||
suite('test tap clear', function() {
|
||||
var spy;
|
||||
var searchInput;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(SearchView, 'clear');
|
||||
searchInput = document.getElementById('views-search-input');
|
||||
searchInput.value = 'this is not the search you are looking for';
|
||||
var searchClear = document.getElementById('views-search-clear');
|
||||
sendTouch(searchClear);
|
||||
});
|
||||
|
||||
test('test that search is now empty', function() {
|
||||
assert.ok(spy.called, 'SearchView.clear() should be called');
|
||||
assert.equal(searchInput.value, '');
|
||||
});
|
||||
});
|
||||
|
||||
suite('test close', function() {
|
||||
var spy, spy2;
|
||||
|
||||
setup(function() {
|
||||
spy = this.sinon.spy(SearchView, 'hide');
|
||||
spy2 = this.sinon.spy(SearchView, 'openResult');
|
||||
var searchClose = document.getElementById('views-search-close');
|
||||
searchClose.click();
|
||||
});
|
||||
|
||||
test('test search is hidden', function() {
|
||||
assert.ok(spy.called, 'SearchView.hide() should be called');
|
||||
assert.ok(!spy2.called, 'SearchVie.openResult() should not be called');
|
||||
}, this);
|
||||
});
|
||||
|
||||
});
|
||||
121
gaia/dev_apps/music-oga/test/unit/ui/views/subList_view_test.js
Executable file
121
gaia/dev_apps/music-oga/test/unit/ui/views/subList_view_test.js
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
/* global SubListView, Sanitizer, MocksHelper, ModeManager, MockL10n */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/js/utils.js');
|
||||
require('/js/ui/views/subList_view.js');
|
||||
|
||||
require('/test/unit/mock_db.js');
|
||||
require('/test/unit/metadata/mock_album_art_cache.js');
|
||||
require('/test/unit/ui/mock_tab_bar.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/test/unit/mock_lazy_loader.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
|
||||
var mocksForSubListViewHelper = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'AlbumArtCache',
|
||||
'ModeManager',
|
||||
'TabBar',
|
||||
'Database'
|
||||
]).init();
|
||||
|
||||
suite('SubListView test', function() {
|
||||
var realL10n = navigator.mozL10n;
|
||||
|
||||
mocksForSubListViewHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function(done) {
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
ModeManager._view = SubListView;
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.id = 'views-sublist';
|
||||
document.body.appendChild(this.element);
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<div id="views-sublist-header">
|
||||
<img id="views-sublist-header-image">
|
||||
<div id="views-sublist-header-controls">
|
||||
<bdi id="views-sublist-header-name">Name</bdi>
|
||||
<button id="views-sublist-controls-play"
|
||||
class="album-controls-button" data-icon="play"
|
||||
data-l10n-id="playbackPlay"></button>
|
||||
<button id="views-sublist-controls-shuffle"
|
||||
class="album-controls-button" data-icon="shuffle"
|
||||
data-l10n-id="shuffle-toggle"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="views-scrollable-sublist-anchor">
|
||||
<div id="views-sublist-anchor" role="listbox">
|
||||
</div>
|
||||
</div>`
|
||||
));
|
||||
|
||||
this.subListView = SubListView;
|
||||
|
||||
this.subListView.init();
|
||||
var data = {
|
||||
metadata: {
|
||||
album: 'Crime of the Century',
|
||||
artist: 'Supertramp',
|
||||
title: 'School'
|
||||
}
|
||||
};
|
||||
this.subListView.activate('album', data, 0, null, 'next',
|
||||
() => done() );
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
|
||||
navigator.mozL10n = realL10n;
|
||||
});
|
||||
|
||||
suite('activate', function() {
|
||||
|
||||
test('content', function() {
|
||||
var children = this.subListView.anchor.children;
|
||||
var count = children.length;
|
||||
assert.equal(count, 4);
|
||||
|
||||
children = this.subListView.anchor.querySelectorAll('.list-item');
|
||||
count = children.length;
|
||||
assert.equal(count, 4);
|
||||
|
||||
var first = this.subListView.anchor.firstChild;
|
||||
assert.equal(first.tagName, 'LI');
|
||||
assert.equal(first.getAttribute('role'), 'presentation');
|
||||
assert.equal(first.textContent, 'School');
|
||||
|
||||
var second = children[1];
|
||||
assert.equal(second.tagName, 'LI');
|
||||
assert.equal(first.getAttribute('role'), 'presentation');
|
||||
assert.equal(second.textContent, 'Lady');
|
||||
});
|
||||
});
|
||||
|
||||
suite('events', function() {
|
||||
var spy;
|
||||
|
||||
test('click tile', function() {
|
||||
spy = this.sinon.spy(ModeManager, 'push');
|
||||
var listItem = document.querySelector('.list-item > [role="option"]');
|
||||
assert.ok(listItem);
|
||||
listItem.click();
|
||||
assert.ok(spy.called, 'ModeManager.push() should have been called');
|
||||
});
|
||||
|
||||
test('click shuffle', function() {
|
||||
spy = this.sinon.spy(ModeManager, 'push');
|
||||
|
||||
document.getElementById('views-sublist-controls-shuffle').click();
|
||||
assert.ok(spy.called, 'ModeManager.push() should have been called');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
115
gaia/dev_apps/music-oga/test/unit/ui/views/tiles_view_test.js
Executable file
115
gaia/dev_apps/music-oga/test/unit/ui/views/tiles_view_test.js
Executable file
|
|
@ -0,0 +1,115 @@
|
|||
/* global TilesView, Sanitizer, MocksHelper, ModeManager, FocusEvent,
|
||||
MODE_TILES */
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/sanitizer.js');
|
||||
|
||||
require('/js/ui/views/tiles_view.js');
|
||||
|
||||
require('/test/unit/mock_db.js');
|
||||
require('/test/unit/metadata/mock_album_art_cache.js');
|
||||
require('/test/unit/ui/views/mock_modemanager.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
|
||||
var mocksForTilesViewHelper = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'AlbumArtCache',
|
||||
'ModeManager',
|
||||
'Database'
|
||||
]).init();
|
||||
|
||||
|
||||
suite('Tiles view test', function() {
|
||||
|
||||
mocksForTilesViewHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.id = 'views-tiles';
|
||||
document.body.appendChild(this.element);
|
||||
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
|
||||
Sanitizer.createSafeHTML(
|
||||
`<form id="views-tiles-search" role="search" class="skin-dark">
|
||||
<button id="views-tiles-search-close" data-l10n-id="search-close"
|
||||
type="submit">close</button>
|
||||
<p>
|
||||
<input id="views-tiles-search-input" type="search" dir="auto"
|
||||
x-inputmode="verbatim"
|
||||
data-l10n-id="search-music" placeholder="SearcH MusiC">
|
||||
<button id="views-tiles-search-clear" type="reset">Clear</button>
|
||||
</p>
|
||||
</form>
|
||||
<div id="views-tiles-anchor"></div>`
|
||||
));
|
||||
|
||||
this.tilesView = TilesView;
|
||||
|
||||
this.tilesView.init();
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.body.removeChild(this.element);
|
||||
this.element = null;
|
||||
});
|
||||
|
||||
suite('activate', function() {
|
||||
setup(function(done) {
|
||||
this.tilesView.activate(() => done());
|
||||
});
|
||||
|
||||
test('content', function() {
|
||||
var children = this.tilesView.anchor.childNodes;
|
||||
var count = children.length;
|
||||
assert.equal(count, 4);
|
||||
|
||||
var first = this.tilesView.anchor.firstChild;
|
||||
assert.equal(first.tagName, 'DIV');
|
||||
assert.ok(first.classList.contains('main-tile'));
|
||||
assert.ok(first.classList.contains('tile'));
|
||||
assert.equal(first.textContent, 'SupertrampCrime of the Century');
|
||||
|
||||
var second = children[1];
|
||||
assert.equal(second.tagName, 'DIV');
|
||||
assert.ok(second.classList.contains('sub-tile'));
|
||||
assert.ok(second.classList.contains('tile'));
|
||||
assert.equal(second.textContent, 'Supertramp');
|
||||
});
|
||||
});
|
||||
|
||||
suite('events', function() {
|
||||
var spy;
|
||||
|
||||
setup(function(done) {
|
||||
this.tilesView.activate(() => done());
|
||||
});
|
||||
|
||||
test('click tile', function() {
|
||||
spy = this.sinon.spy(ModeManager, 'push');
|
||||
var tile = document.querySelector('.tile > [role="button"]');
|
||||
assert.ok(tile);
|
||||
tile.click();
|
||||
assert.ok(spy.called, 'ModeManager.push() should be called');
|
||||
});
|
||||
|
||||
test('click search close', function() {
|
||||
spy = this.sinon.spy(TilesView, 'hideSearch');
|
||||
|
||||
document.getElementById('views-tiles-search-close').click();
|
||||
assert.ok(spy.called, 'TilesView.hideSearch() should have been called');
|
||||
});
|
||||
|
||||
test('focus on search', function() {
|
||||
// make sure this return the right value
|
||||
this.sinon.stub(ModeManager, 'currentMode', () => MODE_TILES );
|
||||
var spy = this.sinon.spy(ModeManager, 'start');
|
||||
var input = document.getElementById('views-tiles-search-input');
|
||||
input.dispatchEvent(new FocusEvent('focus'));
|
||||
|
||||
assert.ok(spy.called, 'ModeManager.start should have been called');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
14
gaia/dev_apps/music-oga/test/unit/utils.js
Executable file
14
gaia/dev_apps/music-oga/test/unit/utils.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
/* exported pass, fail */
|
||||
'use strict';
|
||||
|
||||
function pass(done) {
|
||||
return function() { done(); };
|
||||
}
|
||||
|
||||
function fail(done, desc) {
|
||||
if (!desc) {
|
||||
desc = 'unknown error';
|
||||
}
|
||||
return function(err) { done(err || new Error(desc)); };
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue