Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,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();
});
});
});

View 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>

View file

@ -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();
});

View 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"
}
}

View 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>

View 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();

View 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": {}
}
}
}

View 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>

View 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();

View 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":{}
}
}

View 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;
}
}
}
};

View 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();
}
};

View 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);
}
};

View 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;

View 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();
}
};

View 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;

View 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;

View 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;

View 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));
}
};

View 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');
});
});
});

View file

@ -0,0 +1 @@
[Player_test.js]

View 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');
});
});
});

View 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); }
});
});
});

View 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>&amp; 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>&amp; 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); }
});
});
});

View 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');
});
});
});