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/apps/ringtones/test/build/integration/ringtones_test.js
Executable file
27
gaia/apps/ringtones/test/build/integration/ringtones_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Ringtones tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=ringtones make', function(done) {
|
||||
helper.exec('APP=ringtones make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/ringtones.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/* global Components, Services */
|
||||
'use strict';
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
Services.obs.addObserver(function(document) {
|
||||
if (!document || !document.location) {
|
||||
return;
|
||||
}
|
||||
|
||||
var window = document.defaultView.wrappedJSObject;
|
||||
var fakeTelephony = {};
|
||||
|
||||
window.navigator.__defineGetter__('mozTelephony', function() {
|
||||
return fakeTelephony;
|
||||
});
|
||||
|
||||
}, 'document-element-inserted', false);
|
||||
463
gaia/apps/ringtones/test/marionette/lib/ringtones.js
Executable file
463
gaia/apps/ringtones/test/marionette/lib/ringtones.js
Executable file
|
|
@ -0,0 +1,463 @@
|
|||
/* jshint node: true*/
|
||||
/* global marionetteScriptFinished */
|
||||
/* exported findElement */
|
||||
|
||||
'use strict';
|
||||
|
||||
function createElementGetters(object, parentName, elementNames) {
|
||||
elementNames.forEach(function(name) {
|
||||
Object.defineProperty(object.prototype, name, {
|
||||
get: function() {
|
||||
return this[parentName].findElement(object.Selectors[name]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/***** Popup windows *****/
|
||||
|
||||
function CustomDialog(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
CustomDialog.Selectors = Object.freeze({
|
||||
element: 'form#dialog-screen',
|
||||
cancelButton: 'button#dialog-no',
|
||||
confirmButton: 'button#dialog-yes'
|
||||
});
|
||||
|
||||
CustomDialog.prototype = {
|
||||
waitUntilCreated: function() {
|
||||
this.client.helper.waitForElement(CustomDialog.Selectors.element);
|
||||
}
|
||||
};
|
||||
createElementGetters(
|
||||
CustomDialog, 'client', ['cancelButton', 'confirmButton']
|
||||
);
|
||||
|
||||
function ActionsMenu(client) {
|
||||
this.client = client;
|
||||
this.element = this.client.findElement(ActionsMenu.Selectors.menu);
|
||||
}
|
||||
|
||||
ActionsMenu.Selectors = Object.freeze({
|
||||
menu: 'gaia-menu#ringtone-actions',
|
||||
shareButton: 'button[data-action="share"]',
|
||||
deleteButton: 'button[data-action="delete"]',
|
||||
cancelButton: 'button[data-action="cancel"]',
|
||||
shareMenu: 'form[data-z-index-level="action-menu"]'
|
||||
});
|
||||
|
||||
ActionsMenu.prototype = {
|
||||
waitUntilOpened: function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.element.displayed();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
waitForDialog: function() {
|
||||
var dialog = new CustomDialog(this.client);
|
||||
dialog.waitUntilCreated();
|
||||
return dialog;
|
||||
},
|
||||
|
||||
// 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(ActionsMenu.Selectors.shareMenu);
|
||||
},
|
||||
|
||||
shareWith: function(appName) {
|
||||
var shareMenu = this.shareMenu;
|
||||
var list = shareMenu.findElements('button');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].text() === appName) {
|
||||
return list[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
createElementGetters(
|
||||
ActionsMenu, 'element', ['shareButton', 'deleteButton', 'cancelButton']
|
||||
);
|
||||
|
||||
// ***** Sounds *****
|
||||
|
||||
function BaseSound(client, element) {
|
||||
this.client = client;
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
BaseSound.Selectors = Object.freeze({
|
||||
name: '.name',
|
||||
subtitle: 'p.subtitle'
|
||||
});
|
||||
|
||||
BaseSound.prototype = {
|
||||
get name() {
|
||||
return this.element.findElement(BaseSound.Selectors.name).text();
|
||||
},
|
||||
|
||||
get subtitle() {
|
||||
var subtitle = this.element.findElement(BaseSound.Selectors.subtitle);
|
||||
return subtitle ? subtitle.text() : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
function ManageSound(client, element) {
|
||||
BaseSound.call(this, client, element);
|
||||
}
|
||||
ManageSound.prototype = Object.create(BaseSound.prototype);
|
||||
ManageSound.prototype.constructor = ManageSound;
|
||||
|
||||
ManageSound.Selectors = Object.freeze({
|
||||
description: 'div.desc',
|
||||
actionsButton: 'a.actions-button'
|
||||
});
|
||||
|
||||
ManageSound.prototype.tap = function() {
|
||||
this.element.findElement(ManageSound.Selectors.description).tap(20, 20);
|
||||
};
|
||||
|
||||
ManageSound.prototype.openActions = function() {
|
||||
this.element.findElement(ManageSound.Selectors.actionsButton).tap();
|
||||
var actionsMenu = new ActionsMenu(this.client);
|
||||
actionsMenu.waitUntilOpened();
|
||||
return actionsMenu;
|
||||
};
|
||||
|
||||
Object.defineProperty(ManageSound.prototype, 'playing', {
|
||||
get: function() {
|
||||
return this.element.getAttribute('data-playing') === 'true';
|
||||
}
|
||||
});
|
||||
|
||||
function PickSound(client, element) {
|
||||
BaseSound.call(this, client, element);
|
||||
}
|
||||
PickSound.prototype = Object.create(BaseSound.prototype);
|
||||
PickSound.prototype.constructor = PickSound;
|
||||
|
||||
PickSound.Selectors = Object.freeze({
|
||||
input: 'gaia-radio',
|
||||
subtitle: 'gaia-radio details'
|
||||
});
|
||||
|
||||
Object.defineProperty(PickSound.prototype, 'selected', {
|
||||
get: function() {
|
||||
return Boolean(this.element.findElement(PickSound.Selectors.input)
|
||||
.scriptWith(function(el) {
|
||||
return el.wrappedJSObject.checked;
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Object.defineProperty(PickSound.prototype, 'subtitle', {
|
||||
get: function() {
|
||||
var subtitle = this.element.findElement(PickSound.Selectors.subtitle);
|
||||
return subtitle ? subtitle.text() : undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
PickSound.prototype.select = function() {
|
||||
this.element.tap();
|
||||
this.client.waitFor(function() {
|
||||
return this.selected;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// ***** Sound Lists *****
|
||||
|
||||
function BaseSoundList(client, element, Sound) {
|
||||
this.client = client;
|
||||
this.element = element;
|
||||
this.Sound = Sound;
|
||||
}
|
||||
|
||||
BaseSoundList.Selectors = Object.freeze({
|
||||
header: 'gaia-subheader > span',
|
||||
sound: 'li'
|
||||
});
|
||||
|
||||
BaseSoundList.prototype = {
|
||||
client: null,
|
||||
|
||||
displayed: function(callback) {
|
||||
return this.element.displayed(callback);
|
||||
},
|
||||
|
||||
get sounds() {
|
||||
// hack to find quicker that we have no elements
|
||||
var quickly = this.client.scope({ searchTimeout: 50 });
|
||||
this.element.client = quickly;
|
||||
|
||||
var elts = this.element.findElements(
|
||||
BaseSoundList.Selectors.sound
|
||||
);
|
||||
|
||||
this.element.client = this.client;
|
||||
|
||||
return elts.map(function(element) {
|
||||
return new this.Sound(this.client, element);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
createElementGetters(BaseSoundList, 'element', ['header']);
|
||||
|
||||
function ManageSoundList(client, element) {
|
||||
BaseSoundList.call(this, client, element, ManageSound);
|
||||
}
|
||||
ManageSoundList.prototype = Object.create(BaseSoundList.prototype);
|
||||
ManageSoundList.prototype.constructor = ManageSoundList;
|
||||
|
||||
function PickSoundList(client, element) {
|
||||
BaseSoundList.call(this, client, element, PickSound);
|
||||
}
|
||||
PickSoundList.prototype = Object.create(BaseSoundList.prototype);
|
||||
PickSoundList.prototype.constructor = PickSoundList;
|
||||
|
||||
Object.defineProperty(PickSoundList.prototype, 'selectedSound', {
|
||||
get: function() {
|
||||
var sounds = this.sounds;
|
||||
for (var i = 0; i < sounds.length; i++) {
|
||||
if (sounds[i].selected) {
|
||||
return sounds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ***** Ringtones Containers *****
|
||||
|
||||
function BaseRingtonesContainer(client, SoundList) {
|
||||
this.client = client;
|
||||
this.SoundList = SoundList;
|
||||
this.client.helper.waitForElement(
|
||||
BaseRingtonesContainer.Selectors.readyBody
|
||||
);
|
||||
}
|
||||
|
||||
BaseRingtonesContainer.Selectors = Object.freeze({
|
||||
readyBody: 'body[data-ready]',
|
||||
soundLists: 'article#list-parent > section'
|
||||
});
|
||||
|
||||
BaseRingtonesContainer.prototype = {
|
||||
client: null,
|
||||
|
||||
get soundLists() {
|
||||
return this.client.findElements(
|
||||
BaseRingtonesContainer.Selectors.soundLists
|
||||
).map(function(element) {
|
||||
return new this.SoundList(this.client, element);
|
||||
}.bind(this));
|
||||
},
|
||||
};
|
||||
|
||||
function ManageRingtonesContainer(client) {
|
||||
BaseRingtonesContainer.call(this, client, ManageSoundList);
|
||||
}
|
||||
ManageRingtonesContainer.prototype = Object.create(
|
||||
BaseRingtonesContainer.prototype
|
||||
);
|
||||
ManageRingtonesContainer.prototype.constructor = ManageRingtonesContainer;
|
||||
|
||||
ManageRingtonesContainer.Selectors = Object.freeze({
|
||||
backButton: '#header',
|
||||
addButton: 'button#add',
|
||||
shareFrame: 'iframe[data-url$="share.html"]'
|
||||
});
|
||||
|
||||
createElementGetters(
|
||||
ManageRingtonesContainer, 'client', ['backButton', 'addButton']
|
||||
);
|
||||
|
||||
ManageRingtonesContainer.prototype.addCustomRingtone = function(info) {
|
||||
return this.client.executeAsyncScript(function(info) {
|
||||
var customRingtones = window.wrappedJSObject.customRingtones;
|
||||
customRingtones.add(JSON.parse(info)).then(function(tone) {
|
||||
marionetteScriptFinished({
|
||||
// Use the private members of tone, since the getters are getting Xrayed
|
||||
// into oblivion. See bug 987111.
|
||||
name: tone._name, subtitle: tone._subtitle, id: tone._id
|
||||
});
|
||||
}, function(error) {
|
||||
marionetteScriptFinished(null);
|
||||
});
|
||||
}, [JSON.stringify(info)]);
|
||||
};
|
||||
|
||||
ManageRingtonesContainer.prototype.waitUntilLoaded = function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.soundLists.length === 2;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ManageRingtonesContainer.prototype.waitForNewRingtoneWindow =
|
||||
function(callback) {
|
||||
this.client.switchToFrame();
|
||||
var win = this.client.helper.waitForElement(
|
||||
ManageRingtonesContainer.Selectors.shareFrame
|
||||
);
|
||||
this.client.switchToFrame(win);
|
||||
|
||||
callback(new NewRingtoneContainer(this.client));
|
||||
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.switchToApp(Ringtones.URL);
|
||||
};
|
||||
|
||||
function PickRingtoneContainer(client) {
|
||||
BaseRingtonesContainer.call(this, client, PickSoundList);
|
||||
}
|
||||
PickRingtoneContainer.prototype = Object.create(
|
||||
BaseRingtonesContainer.prototype
|
||||
);
|
||||
PickRingtoneContainer.prototype.constructor = PickRingtoneContainer;
|
||||
|
||||
PickRingtoneContainer.Selectors = Object.freeze({
|
||||
cancelButton: '#header',
|
||||
setButton: 'button#set'
|
||||
});
|
||||
|
||||
PickRingtoneContainer.prototype.waitUntilLoaded = function() {
|
||||
this.client.waitFor(function() {
|
||||
return this.soundLists.length === 3;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
createElementGetters(
|
||||
PickRingtoneContainer, 'client', ['cancelButton', 'setButton']
|
||||
);
|
||||
|
||||
Object.defineProperty(PickRingtoneContainer.prototype, 'selectedSound', {
|
||||
get: function() {
|
||||
var lists = this.soundLists;
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
if (lists[i].selectedSound) {
|
||||
return lists[i].selectedSound;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function NewRingtoneContainer(client) {
|
||||
this.client = client;
|
||||
|
||||
// For some reason, we can't add blobs to indexedDB in Marionette
|
||||
// tests, so just null out our blob.
|
||||
this.client.executeScript(function() {
|
||||
var customRingtones = window.wrappedJSObject.customRingtones;
|
||||
var oldAdd = customRingtones.add;
|
||||
customRingtones.add = function(info) {
|
||||
return oldAdd.call(
|
||||
customRingtones, {name: info.name, subtitle: info.subtitle}
|
||||
);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
NewRingtoneContainer.Selectors = Object.freeze({
|
||||
cancelButton: '#header',
|
||||
saveButton: 'button#save',
|
||||
|
||||
songTitle: '#songtitle',
|
||||
artist: '#artist'
|
||||
});
|
||||
|
||||
NewRingtoneContainer.prototype = {
|
||||
get songTitle() {
|
||||
return this.client.findElement(
|
||||
NewRingtoneContainer.Selectors.songTitle
|
||||
).text();
|
||||
},
|
||||
|
||||
get artist() {
|
||||
return this.client.findElement(
|
||||
NewRingtoneContainer.Selectors.artist
|
||||
).text();
|
||||
},
|
||||
|
||||
waitForSongInfo: function(expectedTitle, expectedArtist) {
|
||||
this.client.waitFor(function() {
|
||||
return this.songTitle === expectedTitle && this.artist === expectedArtist;
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
createElementGetters(
|
||||
NewRingtoneContainer, 'client', ['cancelButton', 'saveButton']
|
||||
);
|
||||
|
||||
var SETTINGS_URL = 'app://settings.gaiamobile.org';
|
||||
|
||||
// XXX: This is a workaround for changes in the system app; see bug 950673.
|
||||
// I neither know (nor care) why this works, but we should probably fix it
|
||||
// up when a better way comes along.
|
||||
function switchToAppFixed(client, url) {
|
||||
client.switchToFrame();
|
||||
var frame = client.findElement('iframe[src*="' + url + '"]');
|
||||
client.switchToFrame(frame, {focus: true});
|
||||
client.helper.wait(500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction around Ringtones app.
|
||||
* @constructor
|
||||
* @param {Marionette.Client} client for operations.
|
||||
*/
|
||||
function Ringtones(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type String Origin of Ringtones app
|
||||
*/
|
||||
Ringtones.URL = 'app://ringtones.gaiamobile.org';
|
||||
|
||||
module.exports = Ringtones;
|
||||
|
||||
Ringtones.prototype = {
|
||||
client: null,
|
||||
|
||||
switchToMe: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.switchToApp(Ringtones.URL);
|
||||
},
|
||||
|
||||
inAlertTones: function(soundPanel, callback) {
|
||||
soundPanel.clickAlertToneSelect();
|
||||
this.switchToMe();
|
||||
|
||||
callback(new PickRingtoneContainer(this.client));
|
||||
|
||||
switchToAppFixed(this.client, SETTINGS_URL);
|
||||
},
|
||||
|
||||
inRingTones: function(soundPanel, callback) {
|
||||
soundPanel.clickRingToneSelect();
|
||||
this.switchToMe();
|
||||
|
||||
callback(new PickRingtoneContainer(this.client));
|
||||
|
||||
switchToAppFixed(this.client, SETTINGS_URL);
|
||||
},
|
||||
|
||||
inManager: function(soundPanel, callback) {
|
||||
soundPanel.clickManageTones();
|
||||
this.switchToMe();
|
||||
|
||||
callback(new ManageRingtonesContainer(this.client));
|
||||
|
||||
switchToAppFixed(this.client, SETTINGS_URL);
|
||||
},
|
||||
|
||||
inShare: function(callback) {
|
||||
this.switchToMe();
|
||||
callback(new NewRingtoneContainer(this.client));
|
||||
}
|
||||
};
|
||||
35
gaia/apps/ringtones/test/marionette/lib/sd_card.js
Executable file
35
gaia/apps/ringtones/test/marionette/lib/sd_card.js
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
/* jshint node: true*/
|
||||
/* exported injectTone */
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
function injectTone(client, details) {
|
||||
var BASE_DIRS = {
|
||||
'ringtone': 'Ringtones',
|
||||
'alerttone': 'Notifications'
|
||||
};
|
||||
if (!(details.type in BASE_DIRS)) {
|
||||
throw new Error('tone type not supported: ' + details.type);
|
||||
}
|
||||
|
||||
var toneFolder = path.join(
|
||||
client.fileManager.deviceStorage.getDeviceStoragePath(),
|
||||
BASE_DIRS[details.type]
|
||||
);
|
||||
if (!fs.existsSync(toneFolder)) {
|
||||
fs.mkdirSync(toneFolder);
|
||||
}
|
||||
var musicFolder = path.join(toneFolder, 'music');
|
||||
if (!fs.existsSync(musicFolder)) {
|
||||
fs.mkdirSync(musicFolder);
|
||||
}
|
||||
client.fileManager.add([
|
||||
{ type: path.join(BASE_DIRS[details.type], 'music'),
|
||||
filePath: details.filePath, filename: details.filename }
|
||||
]);
|
||||
}
|
||||
|
||||
module.exports = {injectTone: injectTone};
|
||||
347
gaia/apps/ringtones/test/marionette/manage_test.js
Executable file
347
gaia/apps/ringtones/test/marionette/manage_test.js
Executable file
|
|
@ -0,0 +1,347 @@
|
|||
/* jshint node: true*/
|
||||
/* global marionette, setup, suite, test*/
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var sd_card = require('./lib/sd_card');
|
||||
var Ringtones = require('./lib/ringtones');
|
||||
var Settings = require('../../../settings/test/marionette/app/app');
|
||||
var Music = require('../../../music/test/marionette/lib/music');
|
||||
|
||||
function assert_sorted(array, message) {
|
||||
if (array.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var prevElt = array[0];
|
||||
for (var i = 1; i < array.length; i++) {
|
||||
assert(prevElt.localeCompare(array[i]) < 0, message);
|
||||
prevElt = array[i];
|
||||
}
|
||||
}
|
||||
|
||||
marionette('Ringtone management', function() {
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
}
|
||||
},
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var app, settingsApp;
|
||||
var soundPanel;
|
||||
|
||||
setup(function() {
|
||||
settingsApp = new Settings(client);
|
||||
app = new Ringtones(client);
|
||||
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{ type: 'music', filePath: 'test_media/samples/Music/b2g.ogg' }
|
||||
]);
|
||||
|
||||
settingsApp.launch();
|
||||
soundPanel = settingsApp.soundPanel;
|
||||
});
|
||||
|
||||
suite('Initial load', function() {
|
||||
|
||||
test('Load list of sounds', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var soundLists = container.soundLists;
|
||||
assert.equal(soundLists.length, 4,
|
||||
'Should have found 4 sound lists, found ' +
|
||||
soundLists.length);
|
||||
|
||||
var expectedInfo = [
|
||||
{l10nID: 'section-title-builtin-ringtone', visible: true},
|
||||
{l10nID: 'section-title-custom-ringtone', visible: false},
|
||||
{l10nID: 'section-title-builtin-alerttone', visible: true},
|
||||
{l10nID: 'section-title-custom-alerttone', visible: false}
|
||||
];
|
||||
var getName = function(element) {
|
||||
return element.name.toLocaleLowerCase();
|
||||
};
|
||||
for (var i = 0; i < soundLists.length; i++) {
|
||||
// Check basic properties of each section.
|
||||
assert.equal(soundLists[i].header.getAttribute('data-l10n-id'),
|
||||
expectedInfo[i].l10nID);
|
||||
assert.equal(soundLists[i].sounds.length !== 0,
|
||||
expectedInfo[i].visible);
|
||||
assert.equal(soundLists[i].displayed(), expectedInfo[i].visible);
|
||||
|
||||
// Check that the tones are in sorted order.
|
||||
var names = soundLists[i].sounds.map(getName);
|
||||
assert_sorted(names, 'Tones should be sorted by name');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Load custom sound', function() {
|
||||
// Inject a custom ringtone, then close and reopen the app to show it.
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var tone = container.addCustomRingtone({
|
||||
name: 'My ringtone', subtitle: 'Bob\'s Ringtones'
|
||||
});
|
||||
assert.equal(tone.name, 'My ringtone');
|
||||
assert.equal(tone.subtitle, 'Bob\'s Ringtones');
|
||||
container.backButton.tap(25, 25);
|
||||
});
|
||||
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var customRingtones = container.soundLists[1];
|
||||
assert.equal(customRingtones.sounds.length, 1,
|
||||
'Custom ringtone list should have one item, found ' +
|
||||
customRingtones.sounds.length);
|
||||
assert.equal(customRingtones.displayed(), true,
|
||||
'Custom ringtone list should be displayed');
|
||||
|
||||
var myTone = customRingtones.sounds[0];
|
||||
assert.equal(myTone.name, 'My ringtone');
|
||||
assert.equal(myTone.subtitle, 'Bob\'s Ringtones');
|
||||
});
|
||||
});
|
||||
|
||||
test('Load sd card ringtone', function() {
|
||||
sd_card.injectTone(client, {
|
||||
type: 'ringtone', filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
});
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var customRingtones = container.soundLists[1];
|
||||
assert.equal(customRingtones.sounds.length, 1,
|
||||
'Custom ringtone list should have one item, found ' +
|
||||
customRingtones.sounds.length);
|
||||
assert.equal(customRingtones.displayed(), true,
|
||||
'Custom ringtone list should be displayed');
|
||||
|
||||
var customAlertTones = container.soundLists[3];
|
||||
assert.equal(customAlertTones.sounds.length, 0,
|
||||
'Custom alert tone list should have no items, found ' +
|
||||
customAlertTones.sounds.length);
|
||||
assert.equal(customAlertTones.displayed(), false,
|
||||
'Custom alert tone list should be hidden');
|
||||
|
||||
var myTone = customRingtones.sounds[0];
|
||||
assert.equal(myTone.name, 'b2g');
|
||||
assert.equal(myTone.subtitle, 'SD Card');
|
||||
});
|
||||
});
|
||||
|
||||
test('Load sd card alert tone', function() {
|
||||
sd_card.injectTone(client, {
|
||||
type: 'alerttone', filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
});
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var customRingtones = container.soundLists[1];
|
||||
assert.equal(customRingtones.sounds.length, 0,
|
||||
'Custom ringtone list should have no items, found ' +
|
||||
customRingtones.sounds.length);
|
||||
assert.equal(customRingtones.displayed(), false,
|
||||
'Custom ringtone list should be hidden');
|
||||
|
||||
var customAlertTones = container.soundLists[3];
|
||||
assert.equal(customAlertTones.sounds.length, 1,
|
||||
'Custom alert tone list should have one item, found ' +
|
||||
customAlertTones.sounds.length);
|
||||
assert.equal(customAlertTones.displayed(), true,
|
||||
'Custom alert tone list should be displayed');
|
||||
|
||||
var myTone = customAlertTones.sounds[0];
|
||||
assert.equal(myTone.name, 'b2g');
|
||||
assert.equal(myTone.subtitle, 'SD Card');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Creation', function() {
|
||||
|
||||
test('Create new ringtone', function() {
|
||||
var musicApp = new Music(client);
|
||||
|
||||
app.inManager(soundPanel, function(container) {
|
||||
container.addButton.tap();
|
||||
|
||||
musicApp.switchToMe();
|
||||
musicApp.playFirstSong();
|
||||
musicApp.finishPick();
|
||||
|
||||
container.waitForNewRingtoneWindow(function(win) {
|
||||
win.waitForSongInfo('Boot To Gecko (B2G)', 'Minute With');
|
||||
win.saveButton.tap();
|
||||
});
|
||||
|
||||
var customSounds = container.soundLists[1];
|
||||
client.waitFor(function() {
|
||||
return customSounds.sounds.length === 1;
|
||||
});
|
||||
assert.equal(customSounds.displayed(), true,
|
||||
'Custom ringtone list should be displayed');
|
||||
var myTone = customSounds.sounds[0];
|
||||
assert.equal(myTone.name, 'Boot To Gecko (B2G)');
|
||||
assert.equal(myTone.subtitle, 'Minute With');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Previewing', function() {
|
||||
|
||||
test('Previewing shows play icon', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
// Pick an alert tone, since they're short.
|
||||
var tone = container.soundLists[2].sounds[0];
|
||||
|
||||
tone.tap();
|
||||
client.waitFor(function() { return tone.playing; });
|
||||
|
||||
// The play icon should go away on its own once the tone ends.
|
||||
client.waitFor(function() { return !tone.playing; });
|
||||
});
|
||||
});
|
||||
|
||||
test('Tapping twice hides play icon', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var tone = container.soundLists[0].sounds[0];
|
||||
|
||||
tone.tap();
|
||||
client.waitFor(function() { return tone.playing; });
|
||||
|
||||
tone.tap();
|
||||
client.waitFor(function() { return !tone.playing; });
|
||||
});
|
||||
});
|
||||
|
||||
test('Tapping another tone hides first play icon', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var tone1 = container.soundLists[0].sounds[0];
|
||||
var tone2 = container.soundLists[0].sounds[1];
|
||||
|
||||
tone1.tap();
|
||||
client.waitFor(function() { return tone1.playing; });
|
||||
|
||||
tone2.tap();
|
||||
client.waitFor(function() { return tone2.playing; });
|
||||
client.waitFor(function() { return !tone1.playing; });
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Actions', function() {
|
||||
var toneInfos = [
|
||||
{location: 'built-in', type: 'ringtone', deletable: false,
|
||||
listIndex: 0, setup: function() {}},
|
||||
|
||||
{location: 'built-in', type: 'alerttone', deletable: false, listIndex: 2,
|
||||
setup: function() {}},
|
||||
|
||||
{location: 'custom', type: 'ringtone', deletable: true, listIndex: 1,
|
||||
setup: function() {
|
||||
// Inject a custom ringtone, then close and reopen the app to show it.
|
||||
app.inManager(soundPanel, function(container) {
|
||||
container.addCustomRingtone({
|
||||
name: 'My ringtone', subtitle: 'Bob\'s Ringtones', blob: {
|
||||
name: 'my_ringtone.mp3'
|
||||
}
|
||||
});
|
||||
container.backButton.tap(25, 25);
|
||||
});
|
||||
}},
|
||||
|
||||
{location: 'sd card', type: 'ringtone', deletable: true, listIndex: 1,
|
||||
setup: function() {
|
||||
sd_card.injectTone(client, {
|
||||
type: 'ringtone', filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
});
|
||||
}},
|
||||
|
||||
{location: 'sd card', type: 'alerttone', deletable: true, listIndex: 3,
|
||||
setup: function() {
|
||||
sd_card.injectTone(client, {
|
||||
type: 'alerttone', filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
});
|
||||
}}
|
||||
];
|
||||
|
||||
toneInfos.forEach(function(info) {
|
||||
suite('Actions for ' + info.location + ' ' + info.type, function() {
|
||||
|
||||
setup(info.setup);
|
||||
|
||||
test('Open action menu', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var list = container.soundLists[info.listIndex];
|
||||
var actionsMenu = list.sounds[0].openActions();
|
||||
|
||||
assert.equal(actionsMenu.shareButton.getAttribute('data-l10n-id'),
|
||||
'actions-share-' + info.type);
|
||||
assert.equal(actionsMenu.shareButton.displayed(), true,
|
||||
'Share button should be displayed');
|
||||
assert.equal(actionsMenu.deleteButton.getAttribute('data-l10n-id'),
|
||||
'actions-delete-' + info.type);
|
||||
assert.equal(actionsMenu.deleteButton.displayed(), info.deletable,
|
||||
'Delete button should be ' +
|
||||
(info.deletable ? 'displayed' : 'hidden'));
|
||||
});
|
||||
});
|
||||
|
||||
test('Share ringtone', function() {
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var list = container.soundLists[info.listIndex];
|
||||
// Make sure we get at least as far as being able to share to the
|
||||
// SMS app. XXX: Later, this could be more detailed and actually
|
||||
// check that the right data was sent...
|
||||
var actionsMenu = list.sounds[0].openActions();
|
||||
actionsMenu.shareButton.tap();
|
||||
|
||||
// Make sure we can't share with ourselves, since that's weird.
|
||||
assert.equal(actionsMenu.shareWith('Ringtones'), null,
|
||||
'Ringtones app shouldn\'t be in Share menu');
|
||||
|
||||
// Share with the messages app.
|
||||
actionsMenu.shareWith('Messages').tap();
|
||||
});
|
||||
});
|
||||
|
||||
if (info.deletable) {
|
||||
test('Delete ringtone', function() {
|
||||
var numTones;
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var list = container.soundLists[info.listIndex];
|
||||
numTones = list.sounds.length;
|
||||
|
||||
var actionsMenu = list.sounds[0].openActions();
|
||||
actionsMenu.deleteButton.tap();
|
||||
var dialog = actionsMenu.waitForDialog();
|
||||
dialog.cancelButton.tap();
|
||||
assert.equal(list.sounds.length, numTones,
|
||||
'Tone should not have been deleted');
|
||||
|
||||
actionsMenu = list.sounds[0].openActions();
|
||||
actionsMenu.deleteButton.tap();
|
||||
dialog = actionsMenu.waitForDialog();
|
||||
dialog.confirmButton.tap();
|
||||
assert.equal(list.sounds.length, numTones - 1,
|
||||
'Tone should have been deleted');
|
||||
|
||||
container.backButton.tap(25, 25);
|
||||
});
|
||||
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var list = container.soundLists[info.listIndex];
|
||||
assert.equal(list.sounds.length, numTones - 1,
|
||||
'Tone should have remained deleted');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
219
gaia/apps/ringtones/test/marionette/pick_test.js
Executable file
219
gaia/apps/ringtones/test/marionette/pick_test.js
Executable file
|
|
@ -0,0 +1,219 @@
|
|||
/* jshint node: true*/
|
||||
/* global marionette, setup, suite, test*/
|
||||
'use strict';
|
||||
|
||||
var Ringtones = require('./lib/ringtones');
|
||||
var Settings = require('../../../settings/test/marionette/app/app');
|
||||
var assert = require('assert');
|
||||
var sd_card = require('./lib/sd_card');
|
||||
|
||||
function assert_sorted(array, message) {
|
||||
if (array.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var prevElt = array[0];
|
||||
for (var i = 1; i < array.length; i++) {
|
||||
assert(prevElt.localeCompare(array[i]) < 0, message);
|
||||
prevElt = array[i];
|
||||
}
|
||||
}
|
||||
|
||||
marionette('Ringtone picker', function() {
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
}
|
||||
},
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var settingsApp;
|
||||
var soundPanel;
|
||||
var app;
|
||||
|
||||
setup(function() {
|
||||
settingsApp = new Settings(client);
|
||||
app = new Ringtones(client, settingsApp);
|
||||
|
||||
client.fileManager.removeAllFiles();
|
||||
|
||||
client.contentScript.inject(__dirname +
|
||||
'/lib/mocks/mock_navigator_moz_telephony.js');
|
||||
|
||||
settingsApp.launch();
|
||||
soundPanel = settingsApp.soundPanel;
|
||||
});
|
||||
|
||||
// We want to run the same tests on both the alerttones and ringtones,
|
||||
// so we'll write the tests once and then generate a pair of suites.
|
||||
var suiteInfos = [
|
||||
{ name: 'Alert tones picker',
|
||||
opener: 'inAlertTones',
|
||||
type: 'alerttone',
|
||||
allowNone: true
|
||||
},
|
||||
{ name: 'Ringtones picker',
|
||||
opener: 'inRingTones',
|
||||
type: 'ringtone',
|
||||
allowNone: false
|
||||
}
|
||||
];
|
||||
|
||||
suiteInfos.forEach(function(suiteInfo) {
|
||||
suite(suiteInfo.name, function() {
|
||||
var otherType = suiteInfo.type === 'alerttone' ? 'ringtone' : 'alerttone';
|
||||
|
||||
test('Load list of sounds', function() {
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
var soundLists = container.soundLists;
|
||||
assert.equal(soundLists.length, 4,
|
||||
'Should have found 4 sound lists, found ' +
|
||||
soundLists.length);
|
||||
|
||||
var expectedInfo = [
|
||||
{l10nID: 'section-title-builtin-' + suiteInfo.type, visible: true},
|
||||
{l10nID: 'section-title-custom-' + suiteInfo.type, visible: false},
|
||||
{l10nID: 'section-title-builtin-' + otherType, visible: true},
|
||||
{l10nID: 'section-title-custom-' + otherType, visible: false}
|
||||
];
|
||||
var getName = function(element) {
|
||||
return element.name.toLocaleLowerCase();
|
||||
};
|
||||
for (var i = 0; i < soundLists.length; i++) {
|
||||
// Check basic properties of each section.
|
||||
assert.equal(soundLists[i].header.getAttribute('data-l10n-id'),
|
||||
expectedInfo[i].l10nID);
|
||||
assert.equal(soundLists[i].sounds.length !== 0,
|
||||
expectedInfo[i].visible);
|
||||
assert.equal(soundLists[i].displayed(), expectedInfo[i].visible);
|
||||
|
||||
// Check that the tones are in sorted order.
|
||||
var names = soundLists[i].sounds.map(getName);
|
||||
if (i === 0 && suiteInfo.allowNone) {
|
||||
names.shift();
|
||||
}
|
||||
assert_sorted(names, 'Tones should be sorted by name');
|
||||
}
|
||||
|
||||
// Make sure the "None" ringtone is listed first, if appropriate.
|
||||
assert.equal(soundLists[0].sounds[0].name === 'None',
|
||||
suiteInfo.allowNone,
|
||||
'"None" ringtone should ' +
|
||||
(suiteInfo.allowNone ? '' : 'not ') +
|
||||
'be shown');
|
||||
});
|
||||
});
|
||||
|
||||
test('Load custom sound', function() {
|
||||
// Inject a custom ringtone, then close and reopen the app to show it.
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var tone = container.addCustomRingtone({
|
||||
name: 'My ringtone', subtitle: 'Bob\'s Ringtones'
|
||||
});
|
||||
assert.equal(tone.name, 'My ringtone');
|
||||
assert.equal(tone.subtitle, 'Bob\'s Ringtones');
|
||||
container.backButton.tap(25, 25);
|
||||
});
|
||||
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
var index = suiteInfo.type === 'ringtone' ? 1 : 3;
|
||||
var customRingtones = container.soundLists[index];
|
||||
assert.equal(customRingtones.sounds.length, 1,
|
||||
'Custom ringtone list should have one item, found ' +
|
||||
customRingtones.sounds.length);
|
||||
assert.equal(customRingtones.displayed(), true,
|
||||
'Custom ringtone list should be displayed');
|
||||
|
||||
var myTone = customRingtones.sounds[0];
|
||||
assert.equal(myTone.name, 'My ringtone');
|
||||
assert.equal(myTone.subtitle, 'Bob\'s Ringtones');
|
||||
});
|
||||
});
|
||||
|
||||
test('Load sd card sound', function() {
|
||||
sd_card.injectTone(client, {
|
||||
type: suiteInfo.type, filePath: 'test_media/samples/Music/b2g.ogg'
|
||||
});
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
var customTones = container.soundLists[1];
|
||||
assert.equal(
|
||||
customTones.sounds.length, 1,
|
||||
'Custom ' + suiteInfo.type + ' list should have one item, found ' +
|
||||
customTones.sounds.length
|
||||
);
|
||||
assert.equal(
|
||||
customTones.displayed(), true,
|
||||
'Custom ' + suiteInfo.type + ' list should be displayed'
|
||||
);
|
||||
|
||||
var customOtherTones = container.soundLists[3];
|
||||
assert.equal(
|
||||
customOtherTones.sounds.length, 0,
|
||||
'Custom ' + otherType + ' list should have no items, found ' +
|
||||
customOtherTones.sounds.length
|
||||
);
|
||||
assert.equal(
|
||||
customOtherTones.displayed(), false,
|
||||
'Custom ' + otherType + ' list should be hidden'
|
||||
);
|
||||
|
||||
var myTone = customTones.sounds[0];
|
||||
assert.equal(myTone.name, 'b2g');
|
||||
assert.equal(myTone.subtitle, 'SD Card');
|
||||
});
|
||||
});
|
||||
|
||||
test('Set selected sound', function() {
|
||||
var originalButtonText = soundPanel.getSelectedTone(suiteInfo.type);
|
||||
var toneIndex, toneName;
|
||||
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
// Select the first sound with a different name from the current
|
||||
// button's text.
|
||||
var tone = container.soundLists[0].sounds[0];
|
||||
toneIndex = 0;
|
||||
if (tone.name === originalButtonText) {
|
||||
tone = container.soundLists[0].sounds[1];
|
||||
toneIndex = 1;
|
||||
}
|
||||
|
||||
toneName = tone.name;
|
||||
tone.select();
|
||||
container.setButton.tap();
|
||||
});
|
||||
|
||||
var buttonText = soundPanel.getSelectedTone(suiteInfo.type);
|
||||
assert.strictEqual(buttonText, toneName);
|
||||
|
||||
// Check that reopening the picker selects the right sound.
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
assert.ok(container.soundLists[0].sounds[toneIndex].selected);
|
||||
});
|
||||
});
|
||||
|
||||
test('Cancel setting selected sound', function() {
|
||||
var originalButtonText = soundPanel.getSelectedTone(suiteInfo.type);
|
||||
|
||||
app[suiteInfo.opener](soundPanel, function(container) {
|
||||
// Select the first sound with a different name from the current
|
||||
// button's text.
|
||||
var tone = container.soundLists[0].sounds[0];
|
||||
if (tone.name === originalButtonText) {
|
||||
tone = container.soundLists[0].sounds[1];
|
||||
}
|
||||
|
||||
tone.select();
|
||||
container.cancelButton.tap(25, 25);
|
||||
});
|
||||
|
||||
var buttonText = soundPanel.getSelectedTone(suiteInfo.type);
|
||||
assert.strictEqual(buttonText, originalButtonText);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
71
gaia/apps/ringtones/test/marionette/share_test.js
Executable file
71
gaia/apps/ringtones/test/marionette/share_test.js
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
/* jshint node: true*/
|
||||
/* global marionette, setup, suite, test*/
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Ringtones = require('./lib/ringtones');
|
||||
var Settings = require('../../../settings/test/marionette/app/app');
|
||||
var Music = require('../../../music/test/marionette/lib/music');
|
||||
|
||||
marionette('Share as ringtone', function() {
|
||||
|
||||
var client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'device.storage.enabled': true,
|
||||
'device.storage.testing': true,
|
||||
'device.storage.prompt.testing': true
|
||||
}
|
||||
}
|
||||
});
|
||||
var app;
|
||||
|
||||
setup(function() {
|
||||
app = new Ringtones(client);
|
||||
|
||||
client.fileManager.removeAllFiles();
|
||||
client.fileManager.add([
|
||||
{ type: 'music', filePath: 'test_media/samples/Music/b2g.ogg' }
|
||||
]);
|
||||
});
|
||||
|
||||
suite('Share activity', function() {
|
||||
|
||||
test('Create new ringtone', function() {
|
||||
try {
|
||||
var musicApp = new Music(client);
|
||||
var settingsApp = new Settings(client);
|
||||
|
||||
musicApp.launch();
|
||||
musicApp.waitForFirstTile();
|
||||
musicApp.switchToSongsView();
|
||||
musicApp.playFirstSong();
|
||||
musicApp.shareWith('Ringtones');
|
||||
|
||||
app.inShare(function(container) {
|
||||
container.waitForSongInfo('Boot To Gecko (B2G)', 'Minute With');
|
||||
container.saveButton.tap();
|
||||
});
|
||||
|
||||
client.switchToFrame();
|
||||
settingsApp.launch();
|
||||
var soundPanel = settingsApp.soundPanel;
|
||||
|
||||
app.inManager(soundPanel, function(container) {
|
||||
var customSounds = container.soundLists[1];
|
||||
assert.equal(customSounds.sounds.length, 1,
|
||||
'Custom ringtone list should have one item');
|
||||
assert.equal(customSounds.displayed(), true,
|
||||
'Custom ringtone list should be displayed');
|
||||
|
||||
var myTone = customSounds.sounds[0];
|
||||
assert.equal(myTone.name, 'Boot To Gecko (B2G)');
|
||||
assert.equal(myTone.subtitle, 'Minute With');
|
||||
});
|
||||
} catch(e) {
|
||||
assert.ok(false, 'Exception ' + e.stack);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
57
gaia/apps/ringtones/test/unit/built_in_ringtones_test.js
Executable file
57
gaia/apps/ringtones/test/unit/built_in_ringtones_test.js
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
/* global assert, require, suite, test */
|
||||
'use strict';
|
||||
|
||||
require('/js/built_in_ringtones.js');
|
||||
|
||||
suite('built-in ringtones', function() {
|
||||
var toneTypes = ['ringtone', 'alerttone'];
|
||||
var baseURLs = {
|
||||
'ringtone': '/shared/resources/media/ringtones/',
|
||||
'alerttone': '/shared/resources/media/notifications/'
|
||||
};
|
||||
|
||||
test('toneTypes', function() {
|
||||
assert.equal(window.builtInRingtones.toneTypes.toString(),
|
||||
toneTypes.toString());
|
||||
});
|
||||
|
||||
toneTypes.forEach(function(toneType) {
|
||||
|
||||
test('list(\'' + toneType + '\')', function(done) {
|
||||
window.builtInRingtones.list(toneType).then(function(tones) {
|
||||
done(function() {
|
||||
assert.ok(Array.isArray(tones));
|
||||
tones.forEach(function(tone) {
|
||||
assert.ok(new RegExp('^builtin:' + toneType + '/').test(tone.id));
|
||||
assert.equal(tone.url.substr(0, baseURLs[toneType].length),
|
||||
baseURLs[toneType]);
|
||||
assert.equal(tone.shareable, true);
|
||||
assert.equal(tone.deletable, false);
|
||||
});
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('get(toneID)', function(done) {
|
||||
window.builtInRingtones.list(toneType).then(function(tones) {
|
||||
var expectedTone = tones[0];
|
||||
var toneID = expectedTone.id;
|
||||
window.builtInRingtones.get(toneID).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.id, expectedTone.id);
|
||||
assert.equal(tone.l10nID, expectedTone.l10nID);
|
||||
assert.equal(tone.url.substr(0, baseURLs[toneType].length),
|
||||
baseURLs[toneType]);
|
||||
assert.equal(tone.shareable, true);
|
||||
assert.equal(tone.deletable, false);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
200
gaia/apps/ringtones/test/unit/custom_ringtones_test.js
Executable file
200
gaia/apps/ringtones/test/unit/custom_ringtones_test.js
Executable file
|
|
@ -0,0 +1,200 @@
|
|||
/* global assert, require, setup, suite, test */
|
||||
'use strict';
|
||||
|
||||
require('/js/custom_ringtones.js');
|
||||
|
||||
suite('custom ringtones', function() {
|
||||
var testBlob;
|
||||
|
||||
setup(function(done) {
|
||||
testBlob = new Blob();
|
||||
window.customRingtones.clear().then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('empty database', function() {
|
||||
|
||||
test('list()', function(done) {
|
||||
window.customRingtones.list().then(function(tones) {
|
||||
done(function() {
|
||||
assert.ok(Array.isArray(tones));
|
||||
assert.equal(tones.length, 0);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add()', function(done) {
|
||||
var info = {name: 'My ringtone', blob: testBlob};
|
||||
// Add a ringtone, and then get the blob from it and test everything!
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
return tone.getBlob().then(function(blob) {
|
||||
return {tone: tone, blob: blob};
|
||||
});
|
||||
}).then(function(result) {
|
||||
done(function() {
|
||||
assert.equal(result.tone.name, 'My ringtone');
|
||||
assert.ok(/^custom:/.test(result.tone.id));
|
||||
assert.ok(/^blob:/.test(result.tone.url));
|
||||
assert.equal(result.blob, testBlob);
|
||||
assert.equal(result.tone.shareable, true);
|
||||
assert.equal(result.tone.deletable, true);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('non-empty database', function() {
|
||||
|
||||
var testTone;
|
||||
|
||||
var toneDeepEqual = function (test, expected, msg) {
|
||||
assert.equal(test.name, expected.name, msg);
|
||||
assert.equal(test.blob, expected.blob, msg);
|
||||
assert.equal(test.id, expected.id, msg);
|
||||
assert.equal(test.subtitle, expected.subtitle, msg);
|
||||
};
|
||||
|
||||
setup(function(done) {
|
||||
var info = {name: 'My ringtone', blob: testBlob};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
testTone = tone;
|
||||
done();
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('list()', function(done) {
|
||||
window.customRingtones.list().then(function(tones) {
|
||||
done(function() {
|
||||
assert.ok(Array.isArray(tones));
|
||||
assert.equal(tones.length, 1);
|
||||
toneDeepEqual(tones[0], testTone);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('remove()', function(done) {
|
||||
window.customRingtones.remove(testTone.id).then(function() {
|
||||
return window.customRingtones.list().then(function(tones) {
|
||||
return tones;
|
||||
});
|
||||
}).then(function(tones) {
|
||||
done(function() {
|
||||
assert.equal(tones.length, 0);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('get()', function(done) {
|
||||
window.customRingtones.get(testTone.id).then(function(tone) {
|
||||
done(function() {
|
||||
toneDeepEqual(tone, testTone);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('deduplication', function() {
|
||||
|
||||
var toneID;
|
||||
|
||||
setup(function(done) {
|
||||
var info = {name: 'My ringtone'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
toneID = tone.id;
|
||||
done();
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add with explicit num', function(done) {
|
||||
var info = {name: 'My other ringtone (2)'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add with explicit num of 0', function(done) {
|
||||
var info = {name: 'My other ringtone (0)'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add duplicate', function(done) {
|
||||
var info = {name: 'My ringtone'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name + ' (1)');
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add duplicate with different subtitle', function(done) {
|
||||
var info = {name: 'My ringtone', subtitle: 'Bob\'s Ringtones'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('add duplicate with explicit num', function(done) {
|
||||
var info = {name: 'My ringtone'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
var info2 = {name: 'My ringtone (1)'};
|
||||
return window.customRingtones.add(info2);
|
||||
}).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name + ' (2)');
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
// This test ensure that if we have "My ringtone (1)" but no "My ringtone",
|
||||
// we can add a ringtone named "My ringtone" (without a unique num)
|
||||
test('add duplicate with missing unique nums', function(done) {
|
||||
var info = {name: 'My ringtone'};
|
||||
window.customRingtones.add(info).then(function(tone) {
|
||||
return window.customRingtones.remove(toneID);
|
||||
}).then(function() {
|
||||
return window.customRingtones.add(info);
|
||||
}).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, info.name);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
30
gaia/apps/ringtones/test/unit/mock_enumerate_all.js
Executable file
30
gaia/apps/ringtones/test/unit/mock_enumerate_all.js
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
var MockEnumerateAll = function(storages, dir, options) {
|
||||
var callback = null;
|
||||
var index = 0;
|
||||
|
||||
function nextFile(self) {
|
||||
if (!(dir in MockEnumerateAll.files)) {
|
||||
self.result = null;
|
||||
} else if (index == MockEnumerateAll.files[dir].length) {
|
||||
self.result = null;
|
||||
} else {
|
||||
self.result = { name: dir + '/' + MockEnumerateAll.files[dir][index++] };
|
||||
}
|
||||
callback.call(self);
|
||||
}
|
||||
|
||||
return {
|
||||
result: null,
|
||||
set onsuccess(cb) {
|
||||
callback = cb;
|
||||
nextFile(this);
|
||||
},
|
||||
continue: function() {
|
||||
nextFile(this);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
MockEnumerateAll.files = {};
|
||||
113
gaia/apps/ringtones/test/unit/sd_card_ringtones_test.js
Executable file
113
gaia/apps/ringtones/test/unit/sd_card_ringtones_test.js
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
/* global MockGetDeviceStorage, MockGetDeviceStorages, MockEnumerateAll,
|
||||
assert, require, setup, suite, suiteSetup, suiteTeardown,
|
||||
teardown, test */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_navigator_getdevicestorage.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_getdevicestorages.js');
|
||||
require('/test/unit/mock_enumerate_all.js');
|
||||
|
||||
suite('sd card ringtones', function() {
|
||||
var toneTypes = ['ringtone', 'alerttone'];
|
||||
var folderNames = {'ringtone': 'Ringtones', 'alerttone': 'Notifications'};
|
||||
|
||||
var realDeviceStorage, realDeviceStorages;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
realDeviceStorages = navigator.getDeviceStorages;
|
||||
navigator.getDeviceStorages = MockGetDeviceStorages;
|
||||
|
||||
realDeviceStorage = navigator.getDeviceStorage;
|
||||
navigator.getDeviceStorage = MockGetDeviceStorage;
|
||||
|
||||
window.enumerateAll = MockEnumerateAll;
|
||||
require('/js/sd_card_ringtones.js', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.getDeviceStorages = realDeviceStorages;
|
||||
realDeviceStorages = null;
|
||||
|
||||
navigator.getDeviceStorage = realDeviceStorage;
|
||||
realDeviceStorage = null;
|
||||
});
|
||||
|
||||
toneTypes.forEach(function(toneType) {
|
||||
suite(toneType, function() {
|
||||
|
||||
suite('empty sd card', function() {
|
||||
test('list()', function(done) {
|
||||
window.sdCardRingtones.list(toneType).then(function(tones) {
|
||||
done(function() {
|
||||
assert.ok(Array.isArray(tones));
|
||||
assert.equal(tones.length, 0);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('non-empty sd card', function() {
|
||||
setup(function() {
|
||||
MockEnumerateAll.files[folderNames.ringtone] = [
|
||||
'ringtone-1.mp3', 'folder/ringtone-2.mp3', '.ringtone-3.mp3',
|
||||
'.hidden/ringtone-4.mp3'
|
||||
];
|
||||
MockEnumerateAll.files[folderNames.alerttone] = [
|
||||
'alerttone-1.mp3', 'folder/alerttone-2.mp3', '.alerttone-3.mp3',
|
||||
'.hidden/alerttone-4.mp3'];
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockEnumerateAll.files = {};
|
||||
});
|
||||
|
||||
test('list()', function(done) {
|
||||
window.sdCardRingtones.list(toneType).then(function(tones) {
|
||||
done(function() {
|
||||
assert.ok(Array.isArray(tones));
|
||||
assert.equal(tones.length, 2);
|
||||
|
||||
var expected = [
|
||||
{ name: toneType + '-1',
|
||||
filename: folderNames[toneType] + '/' + toneType + '-1.mp3' },
|
||||
{ name: toneType + '-2',
|
||||
filename: folderNames[toneType] + '/folder/' + toneType +
|
||||
'-2.mp3' }
|
||||
];
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
assert.equal(tones[i].name, expected[i].name);
|
||||
assert.equal(tones[i].filename, expected[i].filename);
|
||||
assert.equal(tones[i].id, 'sdcard:' + expected[i].filename);
|
||||
assert.equal(tones[i].shareable, true);
|
||||
assert.equal(tones[i].deletable, true);
|
||||
}
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
test('get()', function(done) {
|
||||
var filename = folderNames[toneType] + '/' + toneType + '-1.mp3';
|
||||
var id = 'sdcard:' + filename;
|
||||
window.sdCardRingtones.get(id).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.name, toneType + '-1');
|
||||
assert.equal(tone.filename, filename);
|
||||
assert.equal(tone.id, id);
|
||||
assert.equal(tone.shareable, true);
|
||||
assert.equal(tone.deletable, true);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
198
gaia/apps/ringtones/test/unit/system_tones_test.js
Executable file
198
gaia/apps/ringtones/test/unit/system_tones_test.js
Executable file
|
|
@ -0,0 +1,198 @@
|
|||
/* global assert, MockNavigatorSettings, Promise, require, setup, suite,
|
||||
teardown, test */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/js/built_in_ringtones.js');
|
||||
require('/js/system_tones.js');
|
||||
|
||||
function DummyTone(name, id, blob) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
DummyTone.prototype = {
|
||||
getBlob: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(this.blob);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
function getSettingsBase(toneType) {
|
||||
switch (toneType) {
|
||||
case 'ringtone':
|
||||
return 'dialer.ringtone';
|
||||
case 'alerttone':
|
||||
return 'notification.ringtone';
|
||||
default:
|
||||
throw new Error('tone type not supported');
|
||||
}
|
||||
}
|
||||
|
||||
function getSettings(settingsKeys) {
|
||||
return Promise.all(settingsKeys.map(function(setting) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var req = navigator.mozSettings.createLock().get(setting);
|
||||
req.onsuccess = function() {
|
||||
resolve(req.result[setting]);
|
||||
};
|
||||
req.onerror = function() {
|
||||
reject(req.error);
|
||||
};
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
suite('system tones', function() {
|
||||
|
||||
var nativeSettings;
|
||||
|
||||
setup(function() {
|
||||
nativeSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
|
||||
// Mock mozL10n so that setTone works.
|
||||
navigator.mozL10n = {
|
||||
once: function(callback) {
|
||||
callback();
|
||||
},
|
||||
|
||||
get: function(key) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockNavigatorSettings.mTeardown();
|
||||
navigator.mozSettings = nativeSettings;
|
||||
});
|
||||
|
||||
suite('systemTones.set()', function() {
|
||||
['ringtone', 'alerttone'].forEach(function(toneType) {
|
||||
|
||||
test(toneType, function(done) {
|
||||
var tone = new DummyTone('My Tone', 'id', null);
|
||||
var key = getSettingsBase(toneType);
|
||||
var settingsKeys = [key, key + '.name', key + '.id'];
|
||||
|
||||
window.systemTones.set(toneType, tone).then(
|
||||
getSettings.bind(null, settingsKeys)
|
||||
).then(
|
||||
function(settings) {
|
||||
done(function() {
|
||||
assert.equal(settings[0], null);
|
||||
assert.equal(settings[1], 'My Tone');
|
||||
assert.equal(settings[2], 'id');
|
||||
});
|
||||
},
|
||||
function(error) {
|
||||
done(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('systemTones.getDefault()', function() {
|
||||
var defaultIDs = {};
|
||||
|
||||
setup(function(done) {
|
||||
var settings = {};
|
||||
settings[getSettingsBase('ringtone') + '.default.id'] =
|
||||
defaultIDs.ringtone = 'builtin:ringtone/ringer_firefox';
|
||||
settings[getSettingsBase('alerttone') + '.default.id'] =
|
||||
defaultIDs.alerttone = 'builtin:alerttone/notifier_firefox';
|
||||
|
||||
var req = navigator.mozSettings.createLock().set(settings);
|
||||
req.onsuccess = function() { done(); };
|
||||
req.onerror = function() {
|
||||
done(req.error);
|
||||
};
|
||||
});
|
||||
|
||||
['ringtone', 'alerttone'].forEach(function(toneType) {
|
||||
|
||||
test(toneType, function(done) {
|
||||
window.systemTones.getDefault(toneType).then(function(tone) {
|
||||
done(function() {
|
||||
assert.equal(tone.id, defaultIDs[toneType]);
|
||||
assert.equal(tone.shareable, true);
|
||||
assert.equal(tone.deletable, false);
|
||||
});
|
||||
}, function(error) {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('systemTones.isInUse()', function() {
|
||||
['ringtone', 'alerttone'].forEach(function(toneType) {
|
||||
|
||||
test(toneType + ', in use', function(done) {
|
||||
var tone = new DummyTone('My Tone', 'id', null);
|
||||
|
||||
window.systemTones.set(toneType, tone).then(function() {
|
||||
return window.systemTones.isInUse(tone);
|
||||
}).then(
|
||||
function(inUseAs) {
|
||||
done(function() {
|
||||
assert.equal(inUseAs.length, 1);
|
||||
assert.equal(inUseAs[0], toneType);
|
||||
});
|
||||
},
|
||||
function(error) {
|
||||
done(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test(toneType + ', not in use', function(done) {
|
||||
var tone = new DummyTone('My Tone', 'id', null);
|
||||
var unusedTone = new DummyTone('My Other Tone', 'id2', null);
|
||||
|
||||
window.systemTones.set(toneType, tone).then(function() {
|
||||
return window.systemTones.isInUse(unusedTone);
|
||||
}).then(
|
||||
function(inUseAs) {
|
||||
done(function() {
|
||||
assert.equal(inUseAs.length, 0);
|
||||
});
|
||||
},
|
||||
function(error) {
|
||||
done(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('ringtone and alerttone, in use', function(done) {
|
||||
var tone = new DummyTone('My Tone', 'id', null);
|
||||
|
||||
window.systemTones.set('ringtone', tone).then(function() {
|
||||
return window.systemTones.set('alerttone', tone);
|
||||
}).then(function() {
|
||||
return window.systemTones.isInUse(tone);
|
||||
}).then(
|
||||
function(inUseAs) {
|
||||
done(function() {
|
||||
assert.equal(inUseAs.length, 2);
|
||||
assert.equal(inUseAs[0], 'ringtone');
|
||||
assert.equal(inUseAs[1], 'alerttone');
|
||||
});
|
||||
},
|
||||
function(error) {
|
||||
done(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Reference in a new issue