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

17
gaia/shared/test/.jshintrc Executable file
View file

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

View file

@ -0,0 +1,4 @@
{
"extends": "../.jshintrc",
"node": true
}

View file

@ -0,0 +1,120 @@
/* global Buffer, process */
'use strict';
/**
* Everything.me server for fixtures. Serves stubbed Everything.me responses.
*/
var http = require('http');
var fs = require('fs');
var fsPath = require('path');
var EventEmitter = require('events').EventEmitter;
function writeJSON(res, object) {
object = object || {};
var json = JSON.stringify(object);
res.writeHead(200, {
'Content-Length': Buffer.byteLength(json),
'Content-Type': 'application/json'
});
res.end(json);
}
/**
* @param {String} root directory where content is.@
* @param {Object} routes for the server.
* @constructor
*/
function EmeServer(root, routes) {
EventEmitter.call(this);
// ensure we always have the slash suffix
if (root[root.length - 1] !== '/') {
root += '/';
}
this.root = root;
this.routes = routes;
this._settings = {};
this.fail = false;
}
EmeServer.prototype = {
__proto__: EventEmitter.prototype,
requestHandler: function() {
var routes = this.routes;
return function(req, res) {
// Fail requests
if (req.url.indexOf('/settings') !== 0 && this.fail) {
process.nextTick(function() {
req.socket.destroy();
});
return;
}
var handler = routes[req.url] || routes['*'];
// routes are always invoked with the context of the server.
return handler.call(this, req, res);
}.bind(this);
},
};
var emeServer = new EmeServer(process.argv[2], {
'/Categories/list': function(req, res) {
var json = JSON.parse(fs.readFileSync(this.root + 'categories_list.json'));
writeJSON(res, json);
},
'/Apps/nativeInfo': function(req, res) {
var json = JSON.parse(fs.readFileSync(this.root + 'apps_nativeinfo.json'));
writeJSON(res, json);
},
'/Apps/search': function(req, res) {
var json = JSON.parse(fs.readFileSync(this.root + 'apps_search.json'));
writeJSON(res, json);
},
'/Search/bgimage': function(req, res) {
var json = JSON.parse(fs.readFileSync(this.root + 'search_bgimage.json'));
writeJSON(res, json);
},
'/Search/suggestions': function(req, res) {
var file = fs.readFileSync(this.root + 'search_suggestions.json');
var json = JSON.parse(file);
writeJSON(res, json);
},
'/settings/failAll': function(req, res) {
this.fail = true;
writeJSON(res);
},
'/settings/unfailAll': function(req, res) {
this.fail = false;
writeJSON(res);
},
/**
* Catch all handler which serves up static assets.
*/
'*': function(req, res) {
var url = req.url;
var filePath = fsPath.join(this.root, url);
console.log('Uncaught request: ', url, filePath);
res.writeHead(404);
res.end();
return;
}
});
var server = http.createServer(emeServer.requestHandler());
server.listen(0, function() {
process.send({ type: 'started', port: server.address().port });
});

View file

@ -0,0 +1,6 @@
{
"errorCode": 1,
"processingTime": 0,
"response": {
}
}

View file

@ -0,0 +1,113 @@
{
"errorCode": 1,
"processingTime": 1,
"response": {
"spellingCorrection": [],
"ads": null,
"disambiguation": [],
"adultContent": false,
"apps": [
{
"appUrl": "http://mozilla1.org",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 1,
"name": "Mozilla"
},
{
"appUrl": "http://mozilla2.org/firefox",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 2,
"name": "Firefox"
},
{
"appUrl": "http://mozilla3.org/Thunderbird",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 3,
"name": "Thunderbird"
},
{
"appUrl": "http://mozilla4.org/Persona",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 4,
"name": "Persona"
},
{
"appUrl": "http://mozilla5.org/FxOS",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 5,
"name": "FxOS"
},
{
"appUrl": "http://mozilla6.org/Servo",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 6,
"name": "Servo"
},
{
"appUrl": "http://mozilla7.org/rust",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 7,
"name": "Rust"
},
{
"appUrl": "http://mozilla8.org/Javascript",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 8,
"name": "Javascript"
},
{
"appUrl": "http://mozilla9.org/HTML",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 9,
"name": "HTML"
},
{
"appUrl": "http://mozilla10.org/CSS",
"favUrl": "@",
"icon": "",
"adult": false,
"type": "app",
"id": 10,
"name": "CSS"
}
],
"paging": {
"max": 20,
"limit": 10,
"first": 0
},
"experiences": [
"music"
],
"query": "music"
},
"errorString": "OK",
"requestId": null
}

View file

@ -0,0 +1,60 @@
{
"errorCode": 1,
"processingTime": 0,
"response": {
"locale": "en_US",
"categories": [
{
"appIds": [
1,
2,
3
],
"locale": "en_US",
"canonicalName": "around me",
"query": "Around Me",
"categoryId": 1
},
{
"appIds": [
1,
2,
3
],
"locale": "en_US",
"canonicalName": "astrology",
"query": "Astrology",
"categoryId": 2
},
{
"appIds": [
1,
2,
3
],
"locale": "en_US",
"canonicalName": "autos",
"query": "Autos",
"categoryId": 3
},
{
"appIds": [
1,
2,
3
],
"locale": "en_US",
"canonicalName": "entertainment",
"query": "Entertainment",
"categoryId": 376
}
],
"icons": {
"1": "/blank.png",
"2": "/blank.png",
"3": "/blank.png"
}
},
"errorString": "OK",
"requestId": "0"
}

View file

@ -0,0 +1,14 @@
{
"processingTime": 1,
"errorString": "OK",
"checksum": "1",
"errorCode": 1,
"requestId": "1",
"response": {
"source": "",
"image": {
"MIMEType": "image/gif",
"data": "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="
}
}
}

View file

@ -0,0 +1,12 @@
{
"errorCode":1,
"processingTime":14.3,
"response": [
"[ab]out.com",
"ab",
"[ab]ercrombie & fitch",
"[ab]ner louima"
],
"errorString":"OK",
"requestId":"8WxZsKjO_VU"
}

View file

@ -0,0 +1,89 @@
'use strict';
/* global module, __dirname */
var fork = require('child_process').fork;
/**
* Issue a POST request via marionette.
*/
function post(client, url, json) {
json = json || {};
// must run in chrome so we can do cross domain xhr
client = client.scope({ context: 'chrome' });
return client.executeAsyncScript(function(url, json) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
marionetteScriptFinished(xhr.response);
};
xhr.send(json);
}, [url, JSON.stringify(json)]);
}
function EmeServer(root, marionette, port, proc) {
this.root = root;
this.marionette = marionette;
this.url = 'http://localhost:' + port;
this.process = proc;
}
EmeServer.prototype = {
close: function(callback) {
this.process.kill();
this.process.once('exit', callback.bind(this, null));
},
/**
* Indicate to the server that all requests should be given a response with
* headers but then the socket should be closed shortly after that time.
*/
failAll: function() {
return post(this.marionette, this.url + '/settings/failAll');
},
/**
* Allow requests to to proceed without failure after calling `.failAll`.
*/
unfailAll: function() {
return post(this.marionette, this.url + '/settings/unfailAll');
},
};
/**
* Create an everything.me server for use in marionette tests.
*
* @param {Marionette.Client} client for marionette.
* @param {Function} callback [Error]
*/
module.exports = function create(client, callback) {
var stubRoot = __dirname + '/fixtures';
var proc = fork(__dirname + '/child.js', [stubRoot]);
proc.once('error', callback);
proc.on('message', function(msg) {
if (msg.type !== 'started') {
return;
}
proc.removeListener('error', callback);
callback(null, new EmeServer(stubRoot, client, msg.port, proc));
});
};
module.exports.EmeServer;
/**
* Updates the everything.me API setting.
* @param {Object} client The marionette client.
* @param {Object} server An everything.me server instance.
*/
module.exports.setServerURL = function(client, server) {
var chrome = client.scope({ context: 'chrome' });
chrome.executeAsyncScript(function(url) {
var request = navigator.mozSettings.createLock().set({
'appsearch.url': url
});
request.onsuccess = function() {
marionetteScriptFinished();
};
}, [server.url + '/{resource}']);
};

View file

@ -0,0 +1,8 @@
{
"blacklist": {
"apps/calendar/test/marionette/alarm_test.js": "Bug 994976 - Perma-red TBPL test, alarm > without alarm mocks create two events with simultaneous reminders",
"apps/calendar/test/marionette/caldav_test.js": "Bug 972631 - CalDAV setup causing tbpl timeouts",
"apps/calendar/test/marionette/server_test.js": "",
"apps/system/test/marionette/net_error_test.js": "Bug 1148165 - Disable intermittent test"
}
}

View file

@ -0,0 +1,58 @@
/* global module, __dirname */
'use strict';
var fs = require('fs');
/**
* MockManager is in charge of finding and injecting the mock files. They can be
* located either at the app level in apps/$APP/test/marionette/mocks/ or at the
* global level in shared/test/integration
* @param {Marionette.Client} client Marionette client to use.
* @param {String} app App folder to look into
* @constructor
*/
function MockManager(client, appName) {
this.client = client;
this.potentialDirs = [
__dirname + '/../../../apps/' + appName + '/test/marionette/mocks/',
__dirname + '/' // points to shared/test/integration/
];
}
MockManager.prototype = {
inject: function(mocks) {
mocks = Array.isArray(mocks) ? mocks : [mocks];
mocks.forEach(function(mockName) {
this._injectSingle(mockName);
}, this);
},
_injectSingle: function(mockName) {
var fileName = 'mock_' + mockName + '.js';
var filePath = this._findFilePath(fileName);
this.client.contentScript.inject(filePath);
},
_findFilePath: function(fileName) {
var index = this.potentialDirs.findIndex(function(dir) {
return this._isFileAccessible(dir + fileName);
}, this);
if(index === -1) {
throw new Error(fileName + ' was not found');
}
return this.potentialDirs[index] + fileName;
},
_isFileAccessible: function(filePath) {
try {
fs.accessSync(filePath);
return true;
} catch(e) {
return false;
}
}
};
module.exports = MockManager;

View file

@ -0,0 +1,21 @@
[include:../../../tests/integration/devtools/manifest.ini]
[include:../../../apps/keyboard/test/marionette/manifest.ini]
[include:../../../apps/music/test/marionette/manifest.ini]
[include:../../../apps/browser/test/marionette/manifest.ini]
[include:../../../apps/homescreen/test/marionette/manifest.ini]
[include:../../../apps/search/test/marionette/manifest.ini]
[include:../../../apps/ftu/test/marionette/manifest.ini]
[include:../../../apps/settings/test/marionette/tests/manifest.ini]
[include:../../../apps/verticalhome/test/marionette/manifest.ini]
[include:../../../apps/homescreen/test/marionette/manifest.ini]
[include:../../../apps/system/test/marionette/manifest.ini]
[include:../../../apps/ringtones/test/marionette/manifest.ini]
[include:../../../apps/sms/test/marionette/manifest.ini]
[include:../../../apps/gallery/test/marionette/manifest.ini]
[include:../../../apps/email/test/marionette/manifest.ini]
[include:../../../apps/findmydevice/test/marionette/manifest.ini]
[include:../../../apps/clock/test/marionette/manifest.ini]
[include:../../../apps/calendar/test/marionette/manifest.ini]
[include:../../../apps/communications/dialer/test/marionette/manifest.ini]
[include:../../../apps/communications/contacts/test/marionette/manifest.ini]
[include:../../../apps/video/test/marionette/manifest.ini]

View file

@ -0,0 +1,66 @@
'use strict';
var MockManager = require('./manager_mock.js');
var COMMS_APPS = ['dialer', 'contacts'];
/**
* @param {Marionette.Client} client Marionette client to use.
* @constructor
*/
function MarionetteLoader(client) {
this.client = client;
}
module.exports = MarionetteLoader;
/**
* Make a new helper.
* @param {Marionette.Client} client Marionette client to use.
* @param {Object} options Optional map of attributes for Apps.
* @return {Apps} instance.
*/
MarionetteLoader.setup = function(client, options) {
return new MarionetteLoader(client);
};
MarionetteLoader.prototype = {
/**
* @type {Marionette.Client}
*/
client: null,
/**
* Includes the main entry point for an application.
* @param {String} app
* @param {String} region
* @param {String} [baseFolder=apps] base folder of app.
*/
getAppClass: function(app, region, baseFolder) {
region = region || app;
app = COMMS_APPS.indexOf(app) === -1 ? app : 'communications/' + app;
baseFolder = baseFolder || 'apps';
var AppClass = require(
__dirname + '/../../../' +
baseFolder + '/' + app + '/test/marionette/lib/' + region);
return new AppClass(this.client);
},
/**
* Returns an instance of MockManager that will look into the given app and the
* shared folder.
* @param {String} app
*/
getMockManager: function(app) {
return new MockManager(this.client, app);
},
/**
* Gets the marionette-client Actions class and instantiates it.
* @param {String} helper.
*/
getActions: function() {
var Actions = require('marionette-client').Actions;
return new Actions(this.client);
}
};

View file

@ -0,0 +1,12 @@
'use strict';
var win = document.defaultView;
var battery = {
level: 0,
charging: false,
addEventListener: function() {}
};
win.wrappedJSObject.navigator.__defineGetter__('battery', function() {
return battery;
});

View file

@ -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;
Object.defineProperty(window.wrappedJSObject.navigator, 'mozIccManager', {
configurable: false,
writable: true,
value: Components.utils.cloneInto({iccIds: []}, window)
});
}, 'document-element-inserted', false);

View file

@ -0,0 +1,41 @@
/*global Components, Services*/
/**
* @fileoverview This script mocks the mozSetMessageHandler API
* (developer.mozilla.org/docs/Web/API/Navigator.mozSetMessageHandler)
* for app integration tests. You should use and improve this if you
* want to write a marionette test for app functionality which gets
* triggered by a system message (activities, alarms, etc.).
*/
'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 messageHandlers = {};
window.navigator.__defineGetter__('mozSetMessageHandler', function() {
return function(type, callback) {
messageHandlers[type] = callback;
};
});
window.__defineGetter__('fireMessageHandler', function() {
return function(data, type) {
// For historical purposes, this only supported alarm callbacks. To avoid
// force updating code that still assumes that, still assume that as a
// default.
type = type || 'alarm';
var messageHandler = messageHandlers[type];
if (messageHandler && typeof(messageHandler) === 'function') {
messageHandler(data);
return true;
}
return false;
};
});
}, 'document-element-inserted', false);

View file

@ -0,0 +1,76 @@
/*global Components, Services*/
/**
* @fileoverview This script mocks the mozAlarms API
* (developer.mozilla.org/docs/WebAPI/Alarm) for app integration tests.
* You should use and improve this if you want to write a marionette test
* which verifies that your app sets alarms correctly.
*/
'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 fakeAlarms = [];
function makeSafeObject(obj) {
var exposedProps = {};
Object.keys(obj).forEach(function(key) {
exposedProps[key] = 'wr';
});
obj.__exposedProps__ = exposedProps;
return obj;
}
function makeFakeRequest(result) {
var request = makeSafeObject({
onsuccess: null,
onerror: null
});
window.setTimeout(function() {
if (request.onsuccess) {
request.onsuccess(result);
}
});
return request;
}
window.navigator.__defineGetter__('mozAlarms', function() {
return {
__exposedProps__: {
remove: 'r',
add: 'r',
getAll: 'r'
},
remove: function() {
// just ignore
},
add: function(date, respectTimezone, data) {
fakeAlarms.push(makeSafeObject(data));
return makeFakeRequest({});
},
getAll: function() {
return makeFakeRequest(makeSafeObject({
target: makeSafeObject({
result: []
})
}));
}
};
});
window.navigator.__defineGetter__('__mozFakeAlarms', function() {
return fakeAlarms;
});
}, 'document-element-inserted', false);

View file

@ -0,0 +1,64 @@
/**
* @fileoverview This script mocks the vibration API
* (https://developer.mozilla.org/en-US/docs/Web/Guide/API/Vibration) for
* app integration tests. You should use and improve this if you want to
* write a marionette test which verifies that your app uses vibration
* correctly.
*/
'use strict';
(function() {
var fake_vibrations = 0;
var document_visibility_state = true;
var vibrationPattern = null;
var screenEnabled = window.wrappedJSObject.ScreenManager.screenEnabled;
window.navigator.wrappedJSObject.__defineGetter__('vibrate', function() {
return function(pattern) {
fake_vibrations++;
vibrationPattern = pattern;
return true;
};
});
window.wrappedJSObject.__defineGetter__('__fakeVibrationsNo', function() {
return fake_vibrations;
});
window.document.wrappedJSObject.__defineGetter__('hidden', function() {
return !document_visibility_state;
});
window.wrappedJSObject.__defineGetter__(
'__setDocumentVisibility', function() {
return function(visible) {
document_visibility_state = visible;
return true;
};
});
window.wrappedJSObject.__defineGetter__('__fakeVibrationPattern',
function() {
return vibrationPattern;
});
window.wrappedJSObject.ScreenManager.__defineGetter__(
'turnScreenOff', function() {
return function(_dim) {
screenEnabled = false;
};
});
window.wrappedJSObject.ScreenManager.__defineGetter__(
'turnScreenOn', function() {
return function(_dim) {
screenEnabled = true;
};
});
window.wrappedJSObject.ScreenManager.__defineGetter__(
'screenEnabled', function() {
return screenEnabled;
});
})();

View file

@ -0,0 +1,377 @@
/* global TVSource, TVChannel, TVProgram */
'use strict';
/**
* This mock service contains mock channels and programs that make testing
* easier.
*/
(function(exports) {
var channelInfo = [
{
name: 'HBO',
number: '1'
}, {
name: 'TVBS',
number: '2'
}, {
name: 'Discovery',
number: '3'
}, {
name: 'Fox',
number: '5'
}, {
name: 'ABC',
number: '8'
}, {
name: 'CNN',
number: '9'
}, {
name: 'AXS TV',
number: '10'
}, {
name: 'MTV',
number: '10-1'
}, {
name: 'TNT',
number: '10-2'
}, {
name: 'Disney',
number: '13'
}, {
name: 'Oxygen',
number: '14'
}, {
name: 'Lifetime',
number: '15'
}, {
name: 'AXN',
number: '19'
}, {
name: 'ESON',
number: '20'
}, {
name: 'Mozilla',
number: '25'
}
];
(function overrideTVSourceFunction() {
// Override native TVChannel constructor in order to create mock channels.
window.TVChannel = function() {};
// Delete native property in order to override it
delete TVSource.prototype.currentChannel;
var channels = [];
for(var i = 0; i < channelInfo.length; i++) {
var channel = new TVChannel();
channel.name = channelInfo[i].name;
channel.number = channelInfo[i].number;
channels.push(channel);
}
TVSource.prototype.getChannels = function() {
var lastChannel;
var lastChannelNumber = localStorage.getItem('_Mock-Channel-Number');
channels.some(function(channel) {
if (channel.number === lastChannelNumber) {
lastChannel = channel;
return true;
}
return false;
}.bind(this));
this.currentChannel = lastChannel;
return {
then: function(success) {
setTimeout(function() {
success(channels);
}, 500);
}
};
};
TVSource.prototype.setCurrentChannel = function(channelNumber) {
var resolve;
if (this._lastTimeoutId) {
clearTimeout(this._lastTimeoutId);
}
this._lastTimeoutId = setTimeout(function() {
this.currentChannel = channels[channelNumber];
localStorage.setItem('_Mock-Channel-Number', channelNumber);
if (resolve) {
resolve();
}
}.bind(this), 500);
return {
then: function(success) {
resolve = success;
}
};
};
})();
var programMeta = [{
title: 'The Dark Knight',
description: 'When the menace known as the Joker wreaks havoc and chaos' +
'on the people of Gotham, the caped crusader must come to terms with' +
' one of the greatest psychological tests of his ability to fight' +
' injustice.'
}, {
title: 'Pulp Fiction',
description: 'The lives of two mob hit men, a boxer, a gangster\'s wife' +
', and a pair of diner bandits intertwine in four tales of violence' +
' and redemption.'
}, {
title: 'The Lord of the Rings: The Return of the King',
description: 'Gandalf and Aragorn lead the World of Men against' +
' Sauron\'s army to draw his gaze from Frodo and Sam as they approach' +
' Mount Doom with the One Ring.'
}, {
title: 'Star Wars: Episode V - The Empire Strikes Back',
description: 'After the rebels have been brutally overpowered by the ' +
'Empire on their newly established base, Luke Skywalker takes ' +
'advanced Jedi training with Master Yoda, while his friends are ' +
'pursued by Darth Vader as part of his plan to capture Luke.'
}, {
title: 'Forrest Gump',
description: 'Forrest Gump, while not intelligent, has accidentally ' +
'been present at many historic moments, but his true love, Jenny ' +
'Curran, eludes him.'
}, {
title: 'Inception',
description: 'A thief who steals corporate secrets through use of ' +
'dream-sharing technology is given the inverse task of planting an ' +
'idea into the mind of a CEO.'
}, {
title: 'The Matrix',
description: 'A computer hacker learns from mysterious rebels about ' +
'the true nature of his reality and his role in the war against ' +
'its controllers.'
}, {
title: 'Interstellar',
description: 'A team of explorers travel through a wormhole in an ' +
'attempt to ensure humanity\'s survival.'
}, {
title: 'Whiplash',
description: 'A promising young drummer enrolls at a cut-throat music' +
' conservatory where his dreams of greatness are mentored by an ' +
'instructor who will stop at nothing to realize a student\'s potential.'
}, {
title: 'The Intouchables',
description: 'After he becomes a quadriplegic from a paragliding ' +
'accident, an aristocrat hires a young man from the projects to be ' +
'his caregiver.'
}, {
title: 'Gladiator',
description: 'When a Roman general is betrayed and his family murdered' +
' by an emperor\'s corrupt son, he comes to Rome as a gladiator to' +
' seek revenge.'
}, {
title: 'Alien',
description: 'The commercial vessel Nostromo receives a distress call' +
' from an unexplored planet. After searching for survivors, the crew' +
' heads home only to realize that a deadly bioform has joined them.'
}, {
title: 'The Lion King',
description: 'Tricked into thinking that he caused the death of his ' +
'father, a lion cub flees and abandons his destiny as the future king.'
}, {
title: 'American Beauty',
description: 'A sexually frustrated suburban father has a mid-life ' +
'crisis after becoming infatuated with his daughter\'s best friend.'
}, {
title: 'Toy Story 3',
description: 'The toys are mistakenly delivered to a day-care center' +
' instead of the attic right before Andy leaves for college, and ' +
'it\'s up to Woody to convince the other toys that they weren\'t' +
' abandoned and to return home.'
}, {
title: 'Indiana Jones and the Last Crusade',
description: 'When Dr. Henry Jones Sr. suddenly goes missing while ' +
'pursuing the Holy Grail, eminent archaeologist Indiana Jones must ' +
'follow in his father\'s footsteps and stop the Nazis.'
}, {
title: 'The Wolf of Wall Street',
description: 'Based on the true story of Jordan Belfort, from his rise ' +
'to a wealthy stock-broker living the high life to his fall involving' +
' crime, corruption and the federal government.'
}, {
title: 'Gone Girl',
description: 'With his wife\'s disappearance having become the focus ' +
'of an intense media circus, a man sees the spotlight turned on him' +
' when it\'s suspected that he may not be innocent.'
}, {
title: 'A Beautiful Mind',
description: 'After a brilliant but asocial mathematician accepts ' +
'secret work in cryptography, his life takes a turn for the ' +
'nightmarish.'
}, {
title: 'The Grand Budapest Hotel',
description: 'The adventures of Gustave H, a legendary concierge at a' +
' famous hotel from the fictional Republic of Zubrowka between the ' +
'first and second World Wars, and Zero Moustafa, the lobby boy who ' +
'becomes his most trusted friend.'
}, {
title: 'Monsters, Inc.',
description: 'Monsters generate their city\'s power by scaring ' +
'children, but they are terribly afraid themselves of being ' +
'contaminated by children, so when one enters Monstropolis, top ' +
'scarer Sulley finds his world disrupted.'
}, {
title: 'The Hobbit: The Battle of the Five Armies',
description: 'Bilbo and Company are forced to engage in a war against ' +
'an array of combatants and keep the Lonely Mountain from falling ' +
'into the hands of a rising darkness.'
}, {
title: 'Maleficent',
description: 'A vengeful fairy is driven to curse an infant princess, ' +
'only to discover that the child may be the one person who can ' +
'restore peace to their troubled land.'
}, {
title: 'X-Men: Days of Future Past',
description: 'The X-Men send Wolverine to the past in a desperate ' +
'effort to change history and prevent an event that results in ' +
'doom for both humans and mutants.'
}, {
title: 'Captain America: The Winter Soldier',
description: 'As Steve Rogers struggles to embrace his role in the ' +
'modern world, he teams up with another super soldier, the black widow,' +
' to battle a new threat from old history: an assassin known as the ' +
'Winter Soldier.'
}, {
title: 'The Amazing Spider-Man 2',
description: 'When New York is put under siege by Oscorp, it is up to ' +
'Spider-Man to save the city he swore to protect as well as his ' +
'loved ones'
}, {
title: 'Dawn of the Planet of the Apes',
description: 'An ape empire conflicts with a sustaining human ' +
'rebellion, as well as a rogue renegade due to the catastrophic ' +
'events ten years earlier.'
}, {
title: 'Fast & Furious 6',
description: 'Hobbs has Dominic and Brian reassemble their crew to ' +
'take down a team of mercenaries: Dominic unexpectedly gets ' +
'convoluted also facing his presumed deceased girlfriend, Letty.'
}, {
title: 'Iron Man 3',
description: 'When Tony Stark\'s world is torn apart by a formidable ' +
'terrorist called the Mandarin, he starts an odyssey of rebuilding ' +
'and retribution.'
}, {
title: 'Frozen',
description: 'When the newly crowned Queen Elsa accidentally uses her ' +
'power to turn things into ice to curse her home in infinite winter,' +
' her sister, Anna, teams up with a mountain man, his playful ' +
'reindeer, and a snowman to change the weather condition.'
}, {
title: '2012',
description: 'A frustrated writer struggles to keep his family alive ' +
'when a series of global catastrophes threatens to annihilate mankind.'
}, {
title: 'Magic Mike',
description: 'A male stripper teaches a younger performer how to party,' +
' pick up women, and make easy money.'
}, {
title: 'The Hunger Games',
description: 'Katniss Everdeen voluntarily takes her younger sister\'s' +
' place in the Hunger Games, a televised fight to the death in which' +
' two teenagers from each of the twelve Districts of Panem are chosen' +
' at random to compete.'
}, {
title: 'Dredd',
description: 'In a violent, futuristic city where the police have the' +
' authority to act as judge, jury and executioner, a cop teams with ' +
'a trainee to take down a gang that deals the reality-altering drug,' +
' SLO-MO.'
}, {
title: 'Silver Linings Playbook',
description: 'After a stint in a mental institution, former teacher Pat' +
' Solitano moves back in with his parents and tries to reconcile ' +
'with his ex-wife. Things get more challenging when Pat meets ' +
'Tiffany, a mysterious girl with problems of her own.'
}, {
title: 'The Hobbit: An Unexpected Journey',
description: 'A reluctant hobbit, Bilbo Baggins, sets out to the ' +
'Lonely Mountain with a spirited group of dwarves to reclaim their ' +
'mountain home - and the gold within it - from the dragon Smaug.'
}, {
title: '21 Jump Street',
description: 'A pair of underachieving cops are sent back to a local ' +
'high school to blend in and bring down a synthetic drug ring.'
}, {
title: 'The Amazing Spider-Man',
description: 'After Peter Parker is bitten by a genetically altered ' +
'spider, he gains newfound, spider-like powers and ventures out to ' +
'solve the mystery of his parent\'s mysterious death.'
}, {
title: 'Skyfall',
description: 'Bond\'s loyalty to M is tested when her past comes back ' +
'to haunt her. Whilst MI6 comes under attack, 007 must track down ' +
'and destroy the threat, no matter how personal the cost.'
}, {
title: 'Argo',
description: 'Acting under the cover of a Hollywood producer scouting ' +
'a location for a science fiction film, a CIA agent launches a ' +
'dangerous operation to rescue six Americans in Tehran during the ' +
'U.S. hostage crisis in Iran in 1980.'
}];
(function overrideTVChannelFunction() {
// Override native TVProgram constructor in order to create mock programs.
window.TVProgram = function() {};
TVChannel.prototype._generatePrograms = function() {
var quarterHour = 15 * 60000; // 15 min
var halfHour = 30 * 60000;
var startTime =
new Date((Math.floor(Date.now() / halfHour) - 20) * halfHour)
.getTime();
var duration;
var programs = [];
var program;
var index;
var tmp;
var i;
for(i = 0; i < programMeta.length; i++) {
index = Math.floor(Math.random() * (programMeta.length - i)) + i;
tmp = programMeta[i];
programMeta[i] = programMeta[index];
programMeta[index] = tmp;
duration = (Math.floor(Math.random() * 4) + 2 ) * quarterHour;
program = new TVProgram();
program.title = programMeta[i].title;
program.description = programMeta[i].description;
program.startTime = startTime;
program.duration = duration;
programs.push(program);
startTime += Math.ceil(duration / halfHour) * halfHour;
}
this._programs = programs;
};
TVChannel.prototype.getPrograms = function() {
var promise = {
then: function(callback) {
if (!this._programs) {
this._generatePrograms();
}
setTimeout(function() {
callback(this._programs);
}.bind(this), 800);
return promise;
}.bind(this),
catch: function() {
return promise;
}
};
return promise;
};
})();
})(window);

View file

@ -0,0 +1,32 @@
'use strict';
/* global module */
var appRoot = require('app-root-path');
// TODO Export this helper in its own npm package
function FromApp(app) {
this.app = app;
return this;
}
FromApp.prototype = {
filePath: function(relativePath) {
var app = this.app;
if (['dialer', 'contacts'].indexOf(app) !== -1) {
app = 'communications/' + app;
}
return appRoot + '/apps/' + app + '/test/marionette/' + relativePath;
},
require: function(resource) {
return require(this.filePath(resource));
}
};
module.exports = function(app) {
return new FromApp(app);
};

View file

@ -0,0 +1,92 @@
'use strict';
function Server(root, port, child) {
this.root = root;
this.port = port;
this.child = child;
}
Server.prototype = {
/**
* Formats given input with localhost and port.
*
* @param {String} path part of url.
* @return {String} url of location.
*/
url: function(path) {
return 'http://localhost:' + this.port + '/' + path;
},
/**
* Sends signal to stop child process and stop server.
*/
stop: function() {
this.child.send({
action: 'stop'
});
this.child.kill();
},
/**
* Cork the response body of the given url while allowing headers.
* @param {String} url to cork
*/
cork: function(url) {
this.child.send({
action: 'cork',
args: url
});
},
/**
* Allow the body to be sent after calling `.cork`.
* @param {String} url to uncork
*/
uncork: function(url) {
this.child.send({
action: 'uncork',
args: url
});
},
/**
* Protects a URL using HTTP authentication.
* @param {String} url to protect
*/
protect: function(url) {
this.child.send({
action: 'protect',
args: url
});
},
/**
* Stops protecting a URL.
* @param {String} url to protect
*/
unprotect: function(url) {
this.child.send({
action: 'unprotect',
args: url
});
}
};
/**
* Spawn the child process where the http server lives.
*
* @param {Function} callback [Error err, Server server].
*/
Server.create = function(root, callback) {
var fork = require('child_process').fork;
var child = fork(__dirname + '/server_child.js', [root]);
// wait for start message ['start', PORT_NUMBER].
child.on('message', function(data) {
if (Array.isArray(data) && data[0] === 'start') {
callback(null, new Server(root, data[1], child));
}
});
};
module.exports = Server;

View file

@ -0,0 +1,117 @@
'use strict';
var http = require('http');
var emptyPort = require('empty-port');
var nodeStatic = require('node-static');
var EventEmitter = require('events').EventEmitter;
var root = process.argv[2];
var server;
function Server() {
EventEmitter.call(this);
}
Server.prototype = {
__proto__: EventEmitter.prototype,
/**
* Http server running in this process.
*/
http: null,
/**
* A map of corked URLs.
*/
corkedUrls: {},
/**
* A map of protected URLs.
*/
authUrls: {},
stop: function() {
if (this.http) {
this.http.close();
}
},
start: function(port) {
// using node-static for now we can do fancy stuff in the future.
var file = new nodeStatic.Server(root);
this.http = http.createServer(function(req, res) {
req.addListener('end', function() {
// Handle corked urls.
var fullUrl = 'http://' + req.headers.host + req.url;
if (server.corkedUrls[fullUrl]) {
server.once('uncorked ' + fullUrl, file.serve.bind(file, req, res));
return;
}
// Handle protected URLs.
// Users can login with 'username' and 'password'
if (server.authUrls[fullUrl] &&
req.headers.authorization !== 'Basic dXNlcm5hbWU6cGFzc3dvcmQ=') {
res.writeHead(401, {
'WWW-Authenticate': 'Basic realm="login required"'
});
res.end();
return;
}
// hand off request to node-static
file.serve(req, res);
}).resume();
}).listen(port);
},
cork: function(url) {
this.corkedUrls[url] = true;
},
uncork: function(url) {
delete this.corkedUrls[url];
this.emit('uncorked ' + url);
},
protect: function(url) {
this.authUrls[url] = true;
},
unprotect: function(url) {
delete this.authUrls[url];
}
};
server = new Server();
// figure out which port we are on
emptyPort({}, function(err, port) {
server.start(port);
process.send(['start', port]);
});
// handle process messages
process.on('message', function(data) {
switch (data.action) {
case 'cork':
server.cork(data.args);
break;
case 'uncork':
server.uncork(data.args);
break;
case 'protect':
server.protect(data.args);
break;
case 'unprotect':
server.unprotect(data.args);
break;
case 'stop':
server.stop();
break;
}
});

View file

@ -0,0 +1,7 @@
marionette.plugin('apps', require('marionette-apps'));
marionette.plugin('contentScript', require('marionette-content-script'));
marionette.plugin('fileManager', require('marionette-file-manager'));
marionette.plugin('forms', require('marionette-plugin-forms'));
marionette.plugin('helper', require('marionette-helper'));
marionette.plugin('loader', require(__dirname + '/marionette_loader'));
marionette.plugin('settings', require('marionette-settings-api'));

View file

@ -0,0 +1,79 @@
{
"blacklist": {
"apps/calendar/test/marionette/alarm_test.js": "Bug 994976 - Perma-red TBPL test, alarm > without alarm mocks create two events with simultaneous reminders",
"apps/calendar/test/marionette/caldav_test.js": "Bug 972631 - CalDAV setup causing tbpl timeouts",
"apps/calendar/test/marionette/create_event_test.js": "Bug 1175744 - Intermittent create_event_test.js",
"apps/calendar/test/marionette/day_view_test.js": "Bug 1076716 - TEST-UNEXPECTED-FAIL | day view before each hook",
"apps/calendar/test/marionette/month_view_test.js": "Bug 1179906 - Perma-fail month_view_test.js | month view should show event dots",
"apps/calendar/test/marionette/server_test.js": "",
"apps/calendar/test/marionette/toggle_calendar_test.js": "Bug 1190699 - Intermittent toggle_calendar_test.js",
"apps/calendar/test/marionette/week_view_test.js": "Bug 1060447 - Re-enable week_view_test.js",
"apps/camera/test/marionette/capture_test.js": "device only - missing desktop shims",
"apps/camera/test/marionette/hud_test.js": "device only - missing desktop shims",
"apps/camera/test/marionette/settings_test.js": "device only - missing desktop shims",
"apps/camera/test/marionette/zoom_test.js": "device only - missing desktop shims",
"apps/clock/test/marionette/alarm_test.js": "Bug 1035930 - TEST-UNEXPECTED-FAIL | Alarm System statusbar shows an icon when an alarm is active",
"apps/clock/test/marionette/hour_format_test.js": "Bug 1116311 - Intermittent hour_format_test.js",
"apps/clock/test/marionette/timer_test.js": "Bug 1233503 - TEST-UNEXPECTED-FAIL | apps/clock/test/marionette/timer_test.js",
"apps/communications/contacts/test/marionette/ice_test.js": "Bug 1147601 - Intermittent failures",
"apps/communications/contacts/test/marionette/list_test.js": "Bug 1106194 - Intermittent apps/communications/contacts/test/marionette/list_test.js | Contacts > List Visited contacts > number of visited is the same after leaving search view",
"apps/communications/contacts/test/marionette/multiple_select_test.js": "Bug 1235814 - Intermittent TEST-UNEXPECTED-FAIL | apps/communications/contacts/test/marionette/multiple_select_test.js | Contacts > MultipleSelect Import Importing all contacts",
"apps/communications/contacts/test/marionette/search_test.js": "Bug 1235525 - Intermittent TEST-UNEXPECTED-FAIL | apps/communications/contacts/test/marionette/search_test.js",
"apps/communications/contacts/test/marionette/settings_test.js": "Bug 1233517 - Intermittent TEST-UNEXPECTED-FAIL | apps/communications/contacts/test/marionette/settings_test.js | Contacts > Settings Export, delete and ice buttons disabled No contacts -> Disabled",
"apps/email/test/marionette/activity_create_email_account_complete_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/local_drafts_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/manual_setup_outgoing_credentials_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/message_list_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/next_previous_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/notification_click_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/notification_disable_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/notification_foreground_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/notification_set_interval_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/outbox_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/reply_imap_email_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/search_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/email/test/marionette/send_imap_email_test.js": "Bug 975588 - Re-enable email marionette js tests",
"apps/fm/test/marionette/fm_test.js": "Bug 1233777 - Intermittent TEST-UNEXPECTED-FAIL | apps/fm/test/marionette/fm_test.js | FM Radio test FM Radio favourites Test favouriting",
"apps/ringtones/test/marionette/manage_test.js": "Bug 1037716 - Disable intermittent test after TC change.",
"apps/ringtones/test/marionette/share_test.js": "Bug 1222154 - Failing share_test.js | Share as ringtone Share activity Create new ringtone",
"apps/search/test/marionette/app_installed_test.js": "Bug 1236308 - Intermittent app_installed_test.js | Search - Installed Apps Test app result after install/uninstall",
"apps/search/test/marionette/browser_test.js": "Bug 1236041 - TEST-UNEXPECTED-FAIL | apps/search/test/marionette/browser_test.js | Browser test",
"apps/search/test/marionette/home_button_test.js": "Bug 1188574 - Intermittent apps/search/test/marionette/home_button_test.js | Search - Home Button Press Home button returns to home screen",
"apps/search/test/marionette/places_search_test.js": "Bug 1180068 - Intermittent | Places tests Test url searching",
"apps/settings/test/marionette/tests/airplane_mode_settings_test.js": "",
"apps/settings/test/marionette/tests/app_permission_settings_test.js": "Bug 1004348 - Disable intermittent failing test, manipulate app permissions set geolocation of first app to Grant",
"apps/settings/test/marionette/tests/hotspot_wifi_settings_test.js": "Bug 1006527 - Disable intermittent failing travis test, manipulate hotspot wifi settings resets settings for second load",
"apps/settings/test/marionette/tests/message_settings_test.js": "",
"apps/system/fxa/test/marionette/fxa_screen_flow_test.js": "Bug 1064305 - [Marionette] FxA tests - intermittent timeout failures on system/fxa/test/marionette/fxa_screen_flow_test.js",
"apps/system/test/marionette/audio_channel_competing_test.js": "Bug 1239338 - Intermittent apps/system/test/marionette/audio_channel_competing_test.js | Audio channel competing Public notification audio channel competes with audio channels Public notification channel competes with system channel",
"apps/system/test/marionette/browser_clear_browsing_history_test.js": "Bug 1236038 - TEST-UNEXPECTED-FAIL | apps/system/test/marionette/browser_clear_browsing_history_test.js | Browser test Clear browsing history",
"apps/system/test/marionette/browser_site_loading_background_test.js": "Bug 1233864 - Intermittent browser_site_loading_background_test.js | Browser - Site loading background validate loading background color",
"apps/system/test/marionette/cell_broadcast_system_test.js": "Bug 1230362 - Intermittent cell_broadcast_system_test.js | mozApps - lockscreen enabled CellBroadcastSystem is shown when a message arrives, and clear notification",
"apps/system/test/marionette/keyboard_edge_resize_test.js": "Bug 1147618",
"apps/system/test/marionette/lockscreen_camera_button_test.js": "Bug 1235848 - Intermittent TEST-UNEXPECTED-FAIL | apps/system/test/marionette/lockscreen_camera_button_test.js | LockScreen > after launch the app, turning off and then on the screen should dismiss it",
"apps/system/test/marionette/lockscreen_media_playback_test.js": "Bug 1110913",
"apps/system/test/marionette/lockscreen_passcode_unlock_test.js": "Bug 1226340 - Intermittent lockscreen_passcode_unlock_test.js",
"apps/system/test/marionette/notification_behavior_test.js": "Bug 1148336",
"apps/system/test/marionette/notification_events_test.js": "Bug 1235884 - Intermittent TEST-UNEXPECTED-FAIL | apps/system/test/marionette/notification_events_test.js",
"apps/system/test/marionette/notification_test.js": "Bug 1191036 - Intermittent apps/system/test/marionette/notification_test.js",
"apps/system/test/marionette/software_home_lockscreen_power_menu_test.js": "Bug 1192249 - Intermittent apps/system/test/marionette/software_home_lockscreen_power_menu_test.js",
"apps/system/test/marionette/statusbar_icon_color_test.js": "Bug 1134018",
"apps/system/test/marionette/text_selection_test.js": "Bug 1163853 - Intermittent text_selection_test.js",
"apps/system/test/marionette/update_addon_test.js": "Bug 1225932 - Intermittent update_addon_test.js | Update add-on Basic add-on update test",
"apps/verticalhome/test/marionette/app_hosted_use_cached_icon_test.js": "Bug 1037194 - TEST-UNEXPECTED-FAIL | Vertical Home - Hosted app cached icon fetch fallback to default icon",
"apps/verticalhome/test/marionette/app_packaged_fail_test.js": "Bug 1197816 - Intermittent app_packaged_fail_test.js | Vertical Home - Packaged App Failed Download failed state then retry and launch",
"apps/verticalhome/test/marionette/group_collapse_drag_into_test.js": "Bug 1125696 - Intermittent failing test, TEST-UNEXPECTED-FAIL | Vertical - Group check dragging icon into collapsed group",
"tests/jsmarionette/plugins/marionette-helper/test/index_test.js": "Bug 1165660",
"tests/jsmarionette/plugins/marionette-helper/test/input_select_test.js": "Bug 1189608 - Intermittent TEST-UNEXPECTED-FAIL | MarionetteHelper.fillInputFieldAndtapSelectOption should set date on input",
"tests/jsmarionette/plugins/marionette-js-logger/test/client_test.js": "Bug 1222355 - Intermittent tests/jsmarionette/plugins/marionette-js-logger/test/client_test.js | client get logs from (nested) mozbrowser iframes",
"apps/communications/contacts/test/marionette/form_test.js": "Bug 1205896 - Intermittent form_test.js | Contacts > Form > Edit Contact Simple edition of contact details. ORG field",
"dev_apps/music-oga/test/marionette/music_test.js": "Bug 1236275 - Intermittent apps/music/test/marionette/music_test.js | Music ui tests Test open activity Player shows with one song in database.",
"dev_apps/music-oga/test/marionette/metadata_test.js": "Bug 1238411 - Intermittent dev_apps/music-oga/test/marionette/metadata_test.js | Music player metadata Album art display Check album art is displayed. moztrap:8749",
"dev_apps/music-oga/test/marionette/Player_test.js": "Bug 1238822 - Intermittent apps/music/test/marionette/Player_test.js | Music player tests Rating test Check Rating is saved. moztrap:2683",
"dev_apps/music-oga/test/marionette/search_test.js": "Bug 1238201 - Intermittent dev_apps/music-oga/test/marionette/search_test.js | Music player search \"before each\" hook for \"Check simple search results in tracks.\"",
"distros/raptor/apps/test-startup-limit/test/marionette/counter_test.js": "Bug 1171918 - Intermittent counter_test.js | counter + should increment count",
"tests/jsmarionette/client/marionette-client/test/integration/scope-test.js": "Bug 1181423 - Intermittent tests/jsmarionette/client/marionette-client/test/integration/scope-test.js",
"tests/jsmarionette/plugins/marionette-apps/test/app_test.js": "Bug 1189882 - Intermittent tests/jsmarionette/plugins/marionette-apps/test/app_test.js",
"tv_apps/smart-system/fxa/test/marionette/fxa_screen_flow_test.js": "Bug 1064305 - TEST-UNEXPECTED-FAIL | /builds/slave/test/gaia/apps/system/fxa/test/marionette/fxa_screen_flow_test.js | Firefox Accounts Screen Flow Test (UITest app) should walk screen flow for existing user"
}
}

View file

@ -0,0 +1,43 @@
(function(global) {
'use strict';
var parser = new DOMParser();
var bodyHTML = {};
function getFile(filename, callback) {
var req = new XMLHttpRequest();
req.open('GET', filename, false);
req.onerror = function() {
throw new Error('Unable to load ' + filename + ' for body HTML');
};
req.send();
if (req.status !== 200) {
req.onerror();
}
// parse and sanitize the document
var doc = parser.parseFromString(req.response, 'text/html');
if (!doc) {
throw new Error('Unable to parse ' + filename + ' for body HTML');
}
// remove scripts for safety
var scripts = doc.querySelectorAll('script');
Array.prototype.forEach.call(scripts, function(scriptNode) {
scriptNode.parentNode.removeChild(scriptNode);
});
// copy html from body tag
bodyHTML[filename] = doc.body.innerHTML;
}
function loadBodyHTML(filename) {
if (!(filename in bodyHTML)) {
getFile(filename);
}
document.body.innerHTML = bodyHTML[filename];
}
global.loadBodyHTML = loadBodyHTML;
}(this));

View file

@ -0,0 +1,10 @@
/* exported MockContactsButtons */
'use strict';
var MockContactsButtons = {
init: function() {},
renderPhones: function() {},
renderEmails: function() {},
reMark: function() {}
};

View file

@ -0,0 +1,86 @@
'use strict';
/* exported MockCall */
function MockCall(aNumber, aState, aServiceId) {
this._eventListeners = {
'connected': [],
'disconnected': [],
'statechange': []
};
this.id = { number: aNumber };
this.serviceId = (aServiceId === undefined) ? 1 : aServiceId;
this.state = aState;
this.switchable = true; // The call can be put on hold by default
this.mergeable = true; // The call can be merged by default
this.answer = function() {
this._connect();
};
this.hangUp = function() {
this._disconnect();
};
this.hold = function() {};
this.resume = function() {};
this.mEmergencyNumbers = ['112', '911'];
this.emergency = this.mEmergencyNumbers.indexOf(aNumber) >= 0;
this.mVoicemailNumbers = ['123'];
this.voicemail = this.mVoicemailNumbers.indexOf(aNumber) >= 0;
this.addEventListener = (function(type, handler) {
if (this._eventListeners[type]) {
this._eventListeners[type].push(handler);
}
}).bind(this);
this.removeEventListener = (function(type, handler) {
if (this._eventListeners[type]) {
var idx = this._eventListeners[type].indexOf(handler);
this._eventListeners[type].splice(idx, 1);
}
}).bind(this);
this.triggerEvent = function(type) {
this._mTriggerEventListeners(type);
},
// Mocking the events
this.mChangeState = (function(state) {
this.state = state;
this._mTriggerEventListeners('statechange');
}).bind(this);
this._connect = this.mChangeState.bind(this, 'connected');
this._disconnect = (function() {
this.mChangeState('disconnected');
this._mTriggerEventListeners('disconnected');
}).bind(this);
this._hold = (function() {
this.mChangeState('held');
}).bind(this);
this._resume = (function() {
this.mChangeState('connected');
}).bind(this);
this._mTriggerEventListeners = function(type) {
if (!this._eventListeners[type]) {
return;
}
var self = this;
this._eventListeners[type].forEach(function(callback) {
if (typeof callback === 'function') {
callback({call: self});
} else if (typeof callback == 'object' &&
typeof callback.handleEvent === 'function') {
callback.handleEvent({call: self});
}
});
};
}

View file

@ -0,0 +1,44 @@
'use strict';
/* exported MockCallsHandler */
var MockCallsHandler = {
mActiveCall: null,
mActiveCallForContactImage: null,
mIsFirstCallOnCdmaNetwork: false,
get activeCall() {
return this.mActiveCall;
},
get activeCallForContactImage() {
return this.mActiveCallForContactImage;
},
isFirstCallOnCdmaNetwork: function() {
return this.mIsFirstCallOnCdmaNetwork;
},
toggleCalls: function() {},
toggleMute: function() {},
unmute: function() {},
toggleSpeaker: function() {},
switchToSpeaker: function() {},
switchToDefaultOut: function() {},
switchToReceiver: function() {},
checkCalls: function() {},
end: function() {},
answer: function() {},
updateAllPhoneNumberDisplays: function() {},
updatePlaceNewCall: function() {},
mergeCalls: function() {},
holdOrResumeSingleCall: function() {},
updateMergeAndOnHoldStatus: function() {},
updateMuteAndSpeakerStatus: function() {},
setup: function () {},
mTeardown: function() {
this.mActiveCall = null;
this.mActiveCallForContactImage = null;
this.mIsFirstCallOnCdmaNetwork = false;
}
};

View file

@ -0,0 +1,60 @@
'use strict';
/* exported MockContacts */
var MockContacts = {
// We need to test calls without contact information
// so we'll return that info depending on the
// calling number
// '111': No contacts
// '222': More than 1 contact for that number
// Any other: 1 contact
findByNumber: function findByNumber(number, callback) {
this.mCalledWith = number;
this.mPhoto = 'test';
this.mName = 'test name';
var tel = {
value: this.mCalledWith,
carrier: this.mCarrier,
type: this.mType
};
var contact = {
id: this.mId,
name: [this.mName],
tel: [tel],
photo: [this.mPhoto]
};
var contactsWithSameNumber = 0;
switch (number) {
case '111':
this.mPhoto = null;
this.mName = null;
contact = null;
tel = null;
break;
case '222':
contactsWithSameNumber = 2;
break;
default:
}
callback(contact, tel, contactsWithSameNumber);
},
findListByNumber: function cm_getContactData(number, maxitems, callback) {
callback(this.mResult);
},
getRevision: function cm_getRevision(callback) { callback(0); },
mId: 'id',
mPhoto: null,
mName: null,
mCalledWith: null,
mCarrier: 'carrier',
mType: 'type',
mResult: null,
mTearDown: function tearDown() {
this._calledWith = null;
this.mCarrier = 'carrier';
this.mType = 'type';
this.mResult = [];
}
};

View file

@ -0,0 +1,18 @@
/* exported MockFontSizeManager */
'use strict';
var MockFontSizeManager = {
/**
* Possible scenarios as declared by FontSizeManager.
*/
DIAL_PAD: 0,
SINGLE_CALL: 1,
CALL_WAITING: 2,
STATUS_BAR: 3,
SECOND_INCOMING_CALL: 4,
adaptToSpace: function() {},
ensureFixedBaseline: function() {},
resetFixedBaseline: function() {}
};

View file

@ -0,0 +1,94 @@
/* globals HandledCall, MockNavigatorMozTelephony */
/* exported telephonyAddCall, telephonyAddCdmaCall */
'use strict';
function MockHandledCall(call) {
this.call = call;
this.node = document.createElement('section'); // fake dom
}
MockHandledCall.prototype.handleEvent = function hc_handle(evt) {
};
MockHandledCall.prototype.startTimer = function hc_startTimer() {
};
MockHandledCall.prototype.updateCallNumber = function hc_updateCallNumber() {
};
MockHandledCall.prototype.replaceAdditionalContactInfo =
function hc_replaceAdditionalContactInfo(additionalContactInfo) {
};
MockHandledCall.prototype.restoreAdditionalContactInfo =
function hc_restoreAdditionalContactInfo(additionalContactInfo) {
};
MockHandledCall.prototype.formatPhoneNumber =
function hc_formatPhoneNumber(ellipsisSide, maxFontSize) {
};
MockHandledCall.prototype.renderPhoneNumber =
function hc_renderPhoneNumber(phoneNumber, ellipsisSide) {
};
MockHandledCall.prototype.replacePhoneNumber =
function hc_replacePhoneNumber(phoneNumber, ellipsisSide) {
};
MockHandledCall.prototype.restorePhoneNumber =
function hc_restorePhoneNumber() {
};
MockHandledCall.prototype.updateDirection = function hc_updateDirection() {
};
MockHandledCall.prototype.remove = function hc_remove() {
};
MockHandledCall.prototype.connected = function hc_connected() {
};
MockHandledCall.prototype.busy = function hc_busy() {
};
MockHandledCall.prototype.disconnected = function hc_disconnected() {
};
MockHandledCall.prototype.show = function hc_show() {
};
MockHandledCall.prototype.hide = function hc_hide() {
};
// Should be called in the context of a suite
function telephonyAddCall(mockCall, opt) {
MockNavigatorMozTelephony.calls.push(mockCall);
var handledCall = new MockHandledCall(mockCall);
// not already stubed
if (!('restore' in HandledCall)) {
/* jshint validthis: true */
this.sinon.stub(window, 'HandledCall');
}
HandledCall.withArgs(mockCall).returns(handledCall);
if (opt && opt.trigger) {
MockNavigatorMozTelephony.mTriggerCallsChanged();
}
return handledCall;
}
/* Should be called in the context of a suite after one call has already been
* added via telephonyAddCall(). */
function telephonyAddCdmaCall(number, opt) {
MockNavigatorMozTelephony.calls[0].secondId = { number: number };
MockNavigatorMozTelephony.calls[0].state = 'connected';
if (opt && opt.trigger) {
MockNavigatorMozTelephony.mTriggerCallsChanged();
}
}

View file

@ -0,0 +1,29 @@
'use strict';
/* exported MockKeypadManager */
var MockKeypadManager = {
_phoneNumber: '',
mOnMakeCall: null,
lastCalled: '15555555555',
init: function() {},
updatePhoneNumber: function khm_updatePhoneNumber(number) {
this._phoneNumber = number;
},
makeCall: function makeCall() {
if (this.mOnMakeCall) {
if (this._phoneNumber) {
this.lastCalled = this._phoneNumber;
}
this.mOnMakeCall(this._phoneNumber);
}
},
phoneNumber: function phoneNumber() {
return this._phoneNumber;
},
fetchLastCalled: function() {
this._phoneNumber = this.lastCalled;
},
render: function() {},
restorePhoneNumber: function() {},
restoreAdditionalContactInfo: function() {}
};

View file

@ -0,0 +1,20 @@
'use strict';
/* exported MockTelephonyHelper */
var MockTelephonyHelper = {
mInUseSim: null,
mCallPromise: null,
call: function() {
if (!this.mCallPromise) {
this.mCallPromise = new Promise(() => {});
}
return this.mCallPromise;
},
getInUseSim: function() { return this.mInUseSim; },
mTeardown: function() {
this.mInUseSim = null;
this.mCallPromise = null;
}
};

View file

@ -0,0 +1,14 @@
'use strict';
/* exported MockTelephonyMessages */
var MockTelephonyMessages = {
REGULAR_CALL: 0,
NO_NETWORK: 1,
EMERGENCY_ONLY: 2,
displayMessage: function() {},
handleDisconnect: function() {},
handleError: function() {},
notifyBusyLine: function() {}
};

View file

@ -0,0 +1,10 @@
/* exported MockTonePlayer */
'use strict';
var MockTonePlayer = {
init: function() {},
playSequence: function() {},
start: function(frequencies, shortPress) {},
stop: function() {}
};

View file

@ -0,0 +1,72 @@
'use strict';
/* exported MockUtils */
var MockUtils = {
mCalledPrettyDate: false,
mCalledSetHeaderDate: false,
mCalledGetDayDate: false,
mCalledGetPhoneNumberPrimaryInfo: false,
mCalledGetPhoneNumberAdditionalInfo: false,
prettyDate: function ut_prettyDate(time) {
this.mCalledPrettyDate = true;
},
setHeaderDate: function ut_headerDate(elem, time) {
this.mCalledSetHeaderDate = true;
elem.textContent = time;
},
getDayDate: function re_getDayDate(timestamp) {
this.mCalledGetDayDate = true;
var date = new Date(timestamp);
var startDate = new Date(date.getFullYear(),
date.getMonth(), date.getDate());
return startDate.getTime();
},
getPhoneNumberAdditionalInfo:
function getPhoneNumberAdditionalInfo(matchingTel) {
return {
id: matchingTel.type,
args: {
carrier: matchingTel.carrier
}
};
},
addEllipsis: function ut_addEllipsis() {},
getNextFontSize: function ut_getNextFontSize(view, fakeView, maxFontSize,
minFontSize, fontStep) {
return maxFontSize;
},
mTearDown: function tearDown() {
this.mCalledPrettyDate = false;
this.mCalledHeaderDate = false;
this.mCalledGetDayDate = false;
this.mCalledGetPhoneNumberPrimaryInfo = false;
this.mCalledGetPhoneNumberAdditionalInfo = false;
},
getPhoneNumberPrimaryInfo: function ut_getPhoneNumberPrimaryInfo(matchingTel,
contact) {
this.mCalledGetPhoneNumberPrimaryInfo = true;
if (contact) {
if (contact.name && String(contact.name) !== '') {
return contact.name;
} else if (contact.org && String(contact.org) !== '') {
return contact.org;
}
}
if (matchingTel) {
return matchingTel.value;
}
return null;
},
prettyDuration: function(node, duration, l10nPrefix) {},
isPhoneType: function(type) {}
};

View file

@ -0,0 +1,77 @@
'use strict';
/**
* Helper for applying methods from a mock to an element in the DOM. This is
* needed when mocking web components. For instance, we may have to test that
* some code properly calls into a web component like so:
*
* ```html
* <!-- mock html -->
* <gaia-web-component id="myWC"></gaia-web-component>
* ```
*
* ```js
* // code to test
* var wc = document.getElementById('myWC');
* wc.foo();
* ```
*
* ```js
* // test
* var wc = document.getElementById('myWC');
* this.sinon.stub(wc, 'foo');
* // call method that will call foo()
* sinon.assert.calledOnce(wc.foo);
* ```
*
* This won't work because we aren't importing the actual gaia-web-component
* component. The solution here is to copy the methods from a mock to the
* gaia-web-component, like this:
*
* ```html
* <!-- mock html -->
* <gaia-web-component id="myWC"></gaia-web-component>
* ```
*
* ```js
* // code to test
* var wc = document.getElementById('myWC');
* wc.foo();
* ```
*
* ```js
* // mock
* var MockGaiaWebComponent = {
* foo: function() {}
* };
* ```
*
* ```js
* // test
*
* // use MocksHelper to set GaiaWebComponent = MockGaiaWebComponent
*
* var customElementsHelperForMyTest =
* new CustomElementsHelper(['GaiaWebComponent']);
* customElementsHelperForMyTest.resolve();
*
* // now stub and call it as before
* ```
*/
var CustomElementsHelper = function(mocks) {
this.mocks = mocks.sort();
};
CustomElementsHelper.prototype = {
resolve: function emh_resolve() {
this.mocks.forEach(function(objName) {
var eltName = objName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
var matchingElts = document.querySelectorAll(eltName);
for (var key in window[objName]) {
for (var i = 0; i < matchingElts.length; i++) {
matchingElts[i][key] = window[objName][key];
}
}
});
}
};

View file

@ -0,0 +1,8 @@
/* exported MockGaiaMenu */
'use strict';
var MockGaiaMenu = {
show: function() {},
hide: function() {}
};

View file

@ -0,0 +1,7 @@
/* exported MockGaiaSimPicker */
'use strict';
var MockGaiaSimPicker = {
getOrPick: function() { }
};

View file

@ -0,0 +1,12 @@
'use strict';
var Zlib = {};
Zlib.Gzip = function(data) {
this.data = data;
};
Zlib.Gzip.prototype.compress = function(payload) {
throw 'Gzip.compress error';
};

View file

@ -0,0 +1,27 @@
/* global Zlib */
'use strict';
importScripts('mock_gzip.min.js');
self.onmessage = function(message) {
var cmd = message.data.cmd;
if (cmd === 'gzip') {
// gzip function requires Uint8Array
var textEncoder = new TextEncoder('utf8');
var payload = message.data.payload;
var uint8Data = textEncoder.encode(payload);
try {
var gzip = new Zlib.Gzip(uint8Data);
var compressed = gzip.compress();
postMessage({returnResult: true, gzipData: compressed});
}
catch(e) {
postMessage({returnResult: false, error: e});
}
} else {
console.warn('GzipWorker, unsupported cmd:', cmd);
}
};

View file

@ -0,0 +1,7 @@
'use strict';
/* exported MockAccessibilityHelper */
var MockAccessibilityHelper = {
setAriaSelected: function(selectedTab, tabs) {}
};

View file

@ -0,0 +1,31 @@
/*
Copyright 2015, Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
/* exported MockAdvancedTelemetryHelper */
function MockAdvancedTelemetryHelper(type, name) {
this.type = type;
this.name = name;
}
MockAdvancedTelemetryHelper.HISTOGRAM_COUNT = 4;
MockAdvancedTelemetryHelper.HISTOGRAM_EXP = 0;
MockAdvancedTelemetryHelper.HISTOGRAM_LINEAR = 1;
MockAdvancedTelemetryHelper.prototype.add = function(number) {};

View file

@ -0,0 +1,11 @@
/*exported MockasyncStorage */
'use strict';
var MockasyncStorage = {
getItem: function(key, callback) {
callback();
},
setItem: function(key, value) {},
removeItem: function() {}
};

View file

@ -0,0 +1,87 @@
'use strict';
function MockAudio(src) {
MockAudio.instances.push(this);
this.src = src;
this.readyState = 1;
this.paused = true;
}
MockAudio.instances = [];
MockAudio.mSetup = function() {
MockAudio.instances = [];
};
MockAudio.mTeardown = function() {
MockAudio.instances = [];
};
MockAudio.prototype.HAVE_NOTHING = 0;
MockAudio.prototype.load = function() {
};
MockAudio.prototype.play = function() {
// FIXME can we replace playing with paused?
this.playing = true;
this.paused = false;
};
MockAudio.prototype.pause = function() {
this.playing = false;
this.paused = true;
};
MockAudio.prototype.removeAttribute = function() {
};
MockAudio.prototype.cloneNode = function() {
return this;
};
function MockAudioContext(channel) {
MockAudioContext.instances.push(this);
this.mozAudioChannelType = channel;
this.currentTime = 0;
this.sampleRate = 0;
this.destination = {};
}
MockAudioContext.instances = [];
MockAudioContext.mSetup = function() {
MockAudioContext.instances = [];
};
MockAudioContext.mTeardown = function() {
MockAudioContext.instances = [];
};
MockAudioContext.prototype.createBuffer = function() { return {}; };
MockAudioContext.prototype.createBufferSource = function() {
return {
connect: function() {},
start: function() {},
stop: function() {}
};
};
MockAudioContext.prototype.createGain = function() {
return {
connect() {},
gain: {
setValueAtTime() {},
setValueCurveAtTime() {}
}
};
};
MockAudioContext.prototype.createOscillator = function() {
return {
frequency: { value: 0 },
connect: function() {},
start: function() {},
stop: function() {}
};
};
MockAudioContext.prototype.addEventListener = function() {};
MockAudioContext.prototype.removeEventListener = function() {};

View file

@ -0,0 +1,25 @@
/* global Promise */
/* exported MockBasket */
'use strict';
var MockBasket = {
_err: null,
_data: {
'status': 'ok'
},
_dataStore: null,
send: function(data, callback) {
if (callback) {
callback(this._err, this._data);
}
},
getDataStore: function() {
var self = this;
return new Promise(function (resolve, reject) {
resolve(self._dataStore);
});
}
};

View file

@ -0,0 +1,33 @@
/* exported MockBluetoothHelper, MockBluetoothHelperInstance */
'use strict';
var MockBluetoothHelperInstance = {
profiles: {
'HFP': 0x111E,
'A2DP': 0x110D
},
answerWaitingCall: function() {},
ignoreWaitingCall: function() {},
toggleCalls: function() {},
getConnectedDevicesByProfile: function() {},
connectSco: function() {},
disconnectSco: function() {},
getPairedDevices: function() {},
getAddress: function() {},
setPairingConfirmation: function() {},
setPinCode: function() {},
setPasskey: function() {},
isScoConnected: function() {},
sendMediaMetaData: function() {},
sendMediaPlayStatus: function() {},
onhfpstatuschanged: null,
onscostatuschanged: null,
ona2dpstatuschanged: null,
onpairedstatuschanged: null,
onrequestmediaplaystatus: null
};
var MockBluetoothHelper = function() {
return MockBluetoothHelperInstance;
};

View file

@ -0,0 +1,32 @@
'use strict';
(function(exports) {
exports.MockBookmarksDatabase = {
get: function() {},
_callbacks: {},
getAll: function() {},
getRevisionId: function() {},
addEventListener: function(type, cb) {
this._callbacks[type] = cb;
},
removeEventListener: function(type) {
this._callbacks[type] = null;
},
add: function(data) {
if (this._callbacks.added) {
this._callbacks.added(data);
}
},
put: function(data) {
if (this._callbacks.added) {
this._callbacks.added(data);
}
},
remove: function(id) {
if (this._callbacks.removed) {
this._callbacks.removed(id);
}
},
clear: function() {}
};
}(window));

View file

@ -0,0 +1,16 @@
'use strict';
(function(exports) {
var MockCanvas = function() {
this.width = null;
this.height = null;
return this;
};
MockCanvas.prototype = {
getContext: function mc_getContext() {
}
};
exports.MockCanvas = MockCanvas;
})(window);

View file

@ -0,0 +1,18 @@
'use strict';
(function(exports) {
var MockCanvasRenderingContext2D = function() {
this.width = null;
this.height = null;
return this;
};
MockCanvasRenderingContext2D.prototype = {
drawImage: function mcrc2_drawImage() {
},
getImageData: function mcrc2_getImageData() {
},
};
exports.MockCanvasRenderingContext2D = MockCanvasRenderingContext2D;
})(window);

View file

@ -0,0 +1,35 @@
(function(exports) {
'use strict';
var MockCardStore = {
_dataInStore: {},
mPrepareData: function(id, data) {
this._dataInStore[id] = data;
},
mClearData: function() {
this._dataInStore = {};
},
getData: function(id) {
var that = this;
return new Promise(function(resolve, reject) {
if (that._dataInStore[id]) {
resolve(that._dataInStore[id]);
} else {
resolve(undefined);
}
});
},
saveData: function(id, data) {
var that = this;
return new Promise(function(resolve, reject) {
that._dataInStore[id] = data;
resolve();
});
}
};
exports.MockCardStore = MockCardStore;
}(window));

View file

@ -0,0 +1,22 @@
'use strict';
/* exported MockConfirmDialog */
var MockConfirmDialog = {
showing: false,
title: null,
text: null,
button: null,
show: function(title, text, button) {
this.showing = true;
this.title = title;
this.text = text;
this.button = button;
},
hide: function() {
this.showing = false;
this.title = null;
this.text = null;
this.button = null;
}
};

View file

@ -0,0 +1,98 @@
'use strict';
/* exported MockContactAllFields */
function MockContactAllFields(withoutPhoto) {
var photo;
if (!withoutPhoto) {
photo = getPhotoBlob();
}
return {
'id': '1',
'updated': new Date(),
'additionalName': ['Green'],
'adr': [
{
'type': ['home'],
'pref': true,
'countryName': 'Germany',
'locality': 'Chemnitz',
'region': 'Chemnitz',
'postalCode': '09034',
'streetAddress': 'Gotthardstrasse 22'
}
],
'bday': new Date(Date.UTC(1970, 0, 1)),
'email': [
{
'type': ['personal'],
'value': 'test@test.com'
},
{
'type': ['work'],
'value': 'test@work.com',
'pref': true
}
],
'honorificPrefix': ['Mr.'],
'familyName': ['Grillo'],
'givenName': ['Pepito'],
'nickname': ['PG'],
'jobTitle': ['Sr. Software Architect'],
'name': ['Pepito Grillo'],
'org': ['Test ORG'],
'tel': [
{
'value': '+346578888888',
'type': ['mobile'],
'carrier': 'TEF',
'pref': true
},
{
'value': '+3120777777',
'type': ['Home'],
'carrier': 'KPN'
}
],
'url' : [
{
'type' : ['fb_profile_photo'],
'value' : 'https://abcd1.jpg'
}
],
'category': ['favorite'],
'note': ['Note 1'],
'photo': [photo]
};
}
function getPhotoBlob() {
var b64 = 'R0lGODlhEAAQAMQfAKxoR8VkLxFw1feVPITSWv+eQv7Qo0Cc6OyIN/v7+3PLTSCZ' +
'EFy17Wa6XuT1x2bGQ3nNUU6vRXPAa9mLXMTkwJZEHJt7LL5aJ/z8/O2KONx3L/ubP/r6+rtV' +
'I////////yH5BAEAAB8ALAAAAAAQABAAAAWD4CeOZDlimOitnvlhXefFiyCs3NkZMe9QDMGi' +
'k3t1BgZDIcZgHCCxHAyxKRQmnYOkoYgaNYMNr3JoEB6dDBGmyWxihwNBgVZz2Js3YB+JWNpr' +
'HW15YgA2FxkaRB8JgoQxHQEbdiKNg4R5iYuVgpcZmkUjHDEapYqbJRyjkKouoqqhIyEAOw==';
return b64toBlob(b64, 'image/gif');
}
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 1024;
function charCodeFromCharacter(c) {
return c.charCodeAt(0);
}
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = Array.prototype.map.call(slice, charCodeFromCharacter);
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return new Blob(byteArrays, {type: contentType});
}

View file

@ -0,0 +1,21 @@
'use strict';
/* exported MockContactPhotoHelper */
var MockContactPhotoHelper = {
getThumbnail: function(contact) {
if (!contact || !contact.photo || !contact.photo.length) {
return null;
}
return contact.photo[0];
},
getFullResolution: function(contact) {
if (!contact || !contact.photo || !contact.photo.length) {
return null;
}
return contact.photo[0];
}
};

View file

@ -0,0 +1,41 @@
'use strict';
/*jshint maxlen:false*/
/*exported MockCropResizeRotate*/
var MockCropResizeRotate =
function(blob, cropRegion, outputSize, outputType, metadata, callback) {
// The 2nd, 3rd, 4th and 5th arguments are optional, so fix things up if we're
// called with fewer than 6 args. The last argument is always the callback.
switch (arguments.length) {
case 2:
callback = cropRegion;
cropRegion = outputSize = outputType = metadata = null;
break;
case 3:
callback = outputSize;
outputSize = outputType = metadata = null;
break;
case 4:
callback = outputType;
outputType = metadata = null;
break;
case 5:
callback = metadata;
metadata = null;
break;
case 6:
// everything fine. do nothing here
break;
default:
throw new Error('wrong number of arguments: ' + arguments.length);
}
if (typeof callback === 'function') {
callback(null, blob);
}
};

View file

@ -0,0 +1,38 @@
/* exported MockCustomDialog */
'use strict';
var MockCustomDialog = {
show: function(title, msg, cancel, confirm, element) {
this.mShown = true;
this.mShowedTitle = title;
this.mShowedMsg = msg;
this.mShowedCancel = cancel;
this.mShowedConfirm = confirm;
this.mShowedElement = element;
this.mShowedScreen = document.createElement('form');
return this.mShowedScreen;
},
hide: function() {
this.mShown = false;
},
mShown: false,
mShowedTitle: null,
mShowedMsg: null,
mShowedCancel: null,
mShowedConfirm: null,
mShowedElement: null,
mShowedScreen: null,
mTeardown: function teardown() {
this.mShown = false;
this.mShowedTitle = null;
this.mShowedMsg = null;
this.mShowedCancel = null;
this.mShowedConfirm = null;
this.mShowedElement = null;
this.mShowedScreen = null;
}
};

View file

@ -0,0 +1,26 @@
'use strict';
(function(exports) {
var MockCustomEvent = function(type, eventInitDict) {
this.type = type;
if (typeof eventInitDict === 'object') {
this.bubbles = eventInitDict.bubbles;
this.cancelable = eventInitDict.cancelable;
this.detail = eventInitDict.detail;
}
return this;
};
MockCustomEvent.prototype = {
initCustomEvent:
function mce_initCustomEvent(type, canBubble, cancelable, detail) {
this.type = type;
this.bubbles = canBubble;
this.cancelable = cancelable;
this.detail = detail;
}
};
exports.MockCustomEvent = MockCustomEvent;
}(window));

View file

@ -0,0 +1,30 @@
'use strict';
window.MockDefaultActivityHelper = {
getDefaultConfig: function(name, type) {
if (name === 'testactivity' && type === 'testtype') {
return {
name: 'testactivity',
type: ['testtype']
};
} else {
return undefined;
}
},
getDefaultAction: function(name, type) {
if (name === 'testactivity' && type === 'testtype') {
return {
'aa': 'kk'
};
} else {
return undefined;
}
},
setDefaultAction: function(name, type, choice) {
}
};

View file

@ -0,0 +1,85 @@
'use strict';
function MockDeviceStorage() {
MockDeviceStorage.instances.push(this);
this.entries = [
{name: 'mock1'},
{name: 'mock2'},
{name: 'mock3'},
{name: 'mock4'}
];
this.cursors = [];
}
MockDeviceStorage.mSetup = function() {
this.realGetDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = function() {
return new MockDeviceStorage();
};
MockDeviceStorage.instances = [];
};
MockDeviceStorage.mTeardown = function() {
navigator.getDeviceStorage = this.realGetDeviceStorage;
MockDeviceStorage.instances = [];
};
MockDeviceStorage.prototype.enumerate = function() {
var storage = this;
var req = {
onsuccess: null,
onerror: null
};
var cursor = {
index: -1,
done: false,
continue: function() {
this.index += 1;
if (this.index < storage.entries.length) {
this.result = storage.entries[this.index];
} else {
this.result = null;
this.done = true;
}
setTimeout(function() {
if (req.onsuccess) {
req.onsuccess.call(cursor);
}
});
},
result: null
};
this.cursors.push(cursor);
cursor.continue();
return req;
};
MockDeviceStorage.prototype.delete = function(name) {
for (var i = 0; i < this.entries.length; i++) {
if (this.entries[i] && this.entries[i].name === name) {
this.entries.splice(i, 1);
break;
}
}
if (i > this.entries.length) {
return;
}
for (var j = 0; j < this.cursors.length; j++) {
if (this.cursors[j].index >= i) {
this.cursors[j].index -= 1;
}
}
};

View file

@ -0,0 +1,149 @@
'use strict';
/* global MockEventTarget */
(function(exports) {
/**
* This is an reimplementation of DOMRequest in JavaScript,
* usable as a mock for tests.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/DOMRequest
* and http://dxr.mozilla.org/mozilla-central/source/dom/base/DOMRequest.h
* for the simulated real APIs.
*
* There are two additional methods (fireSuccess() and fireError()) for
* explicitly invoking success/error in test script.
*
* @class MockDOMRequest
* @requires MockEventTarget
*
*/
var MockDOMRequest = function MockDOMRequest() {
this._promise = null;
};
MockDOMRequest.prototype = new MockEventTarget();
/**
* The operation's result.
* @memberof MockDOMRequest.prototype
* @type {Mixed}
*/
MockDOMRequest.prototype.result = undefined;
/**
* Error information, if any.
* @type {Mixed}
*/
MockDOMRequest.prototype.error = undefined;
/**
* A callback handler called when the operation represented by the
* DOMRequest is completed.
* @memberof MockDOMRequest.prototype
* @type {Function}
*/
MockDOMRequest.prototype.onsuccess = null;
/**
* A callback handler that gets called when an error occurs while processing
* the operation.
* @memberof MockDOMRequest.prototype
* @type {Function}
*/
MockDOMRequest.prototype.onerror = null;
/**
* A string indicating whether or not the operation is finished running.
* Its value is either "done" or "pending".
* @memberof MockDOMRequest.prototype
* @type {String}
*/
MockDOMRequest.prototype.readyState = 'pending';
/**
* Fire success event with result given.
* @memberof MockDOMRequest.prototype
* @param {Mixed} result The result.
*/
MockDOMRequest.prototype.fireSuccess = function(result) {
if (this.readyState === 'done') {
throw 'The DOMRequest instance has already fired.';
}
this.readyState = 'done';
this.result = result;
// Fake event that only implements two properties.
// I am not interested in writing MockDOMEvent :-/
var evt = {
type: 'success'
};
this.dispatchEvent(evt);
// Remove callbacks since we will never file them again.
this._captureCallbacks = [];
this._bubbleCallbacks = [];
this._getInternalPromise().resolve(result);
};
/**
* Fire error event with error given.
* @memberof MockDOMRequest.prototype
* @param {Mixed} error The error.
*/
MockDOMRequest.prototype.fireError = function(error) {
if (this.readyState === 'done') {
throw 'The DOMRequest instance has already fired.';
}
this.readyState = 'done';
this.error = error;
// Fake event that only implements two properties.
// I am not interested in writing MockDOMEvent :-/
var evt = {
type: 'error'
};
this.dispatchEvent(evt);
// Remove callbacks since we will never file them again.
this._captureCallbacks = [];
this._bubbleCallbacks = [];
this._getInternalPromise().reject(error);
};
/**
* The then-able interface of DOMRequest. See bug 839838 for real impl.
* @returns {Mixed} A Promise instance returned by the then() method.
*/
MockDOMRequest.prototype.then = function(fulfillCallback, rejectCallback) {
return this._getInternalPromise().then(fulfillCallback, rejectCallback);
};
/**
* Get the internal promise instance. Internal only.
* Create the instance when being required for the first time.
* @returns {Mixed} A Promise instance.
*/
MockDOMRequest.prototype._getInternalPromise = function() {
if (this._promise) {
return this._promise;
}
var oResolve, oReject;
var p = this._promise = new Promise(function(resolve, reject) {
oResolve = resolve;
oReject = reject;
});
p.resolve = oResolve;
p.reject = oReject;
return p;
};
exports.MockDOMRequest = MockDOMRequest;
}(window));

View file

@ -0,0 +1,56 @@
'use strict';
var DEFAULT_PARAMS = {
id: '0',
totalBytes: 1800,
currentBytes: 100,
url: 'http://firefoxos.com/archivo.mp3',
path: '//SDCARD/Downloads/archivo.mp3',
state: 'downloading',
contentType: 'audio/mpeg',
startTime: new Date(),
error: null
};
function MockDownload(params) {
params = params || {};
this.id = params.id || '0';
this.totalBytes = params.totalBytes || DEFAULT_PARAMS.totalBytes;
this.currentBytes = params.currentBytes || DEFAULT_PARAMS.currentBytes;
this.url = params.url || DEFAULT_PARAMS.url;
this.path = params.path || DEFAULT_PARAMS.path;
this.state = params.state || DEFAULT_PARAMS.state;
this.contentType = params.contentType || DEFAULT_PARAMS.contentType;
this.startTime = params.startTime || DEFAULT_PARAMS.startTime;
this.error = params.error || DEFAULT_PARAMS.error;
this.listeners = [];
}
MockDownload.prototype = {
addEventListener: function(type, listener) {
if(this.listeners.indexOf(listener) == -1) {
this.listeners.push(listener);
}
},
removeEventListener: function(type, listener) {
var index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
},
pause: function() {
return {
then: function(success, error) { setTimeout(success, 0); }
};
},
resume: function() {
return {
then: function(success, error) { setTimeout(success, 0); }
};
}
};

View file

@ -0,0 +1,26 @@
'use strict';
/* exported MockDownloadFormatter */
var MockDownloadFormatter = {
getFormattedSize: function(bytes) {
return Promise.resolve('45 MB');
},
getPercentage: function(download) {
return 10;
},
getFileName: function(download) {
return 'file.mp3';
},
getTotalSize: function(download) {
return Promise.resolve('12 MB');
},
getDownloadedSize: function(download) {
return Promise.resolve('2 MB');
},
getDate: function(download, callback) {
callback && callback('Just now');
},
getUUID: function(download) {
return download.id;
}
};

View file

@ -0,0 +1,25 @@
'use strict';
/* exported MockDownloadHelper */
var MockDownloadHelper = {
open: function() {
return {
set onsuccess(s) { setTimeout(function() {
s();
}, 100); },
set onerror(e) {}
};
},
remove: function() {
return {
set onsuccess(s) { s(); },
set onerror(e) {}
};
},
handlerError: function(error, download, cb) {
},
showRemoveFileUI: function(download, cb) {
}
};

View file

@ -0,0 +1,34 @@
'use strict';
var MockDump = (function() {
var dump_off = function() {};
var dump_on = function(msg, optionalObject) {
var output = msg;
if (optionalObject) {
output += JSON.stringify(optionalObject);
}
console.log('[MockDUMP] ' + output);
};
return {
enable: function() {
return dump_on;
},
disable: function() {
return dump_off;
}
};
}());
MockDump.disable();
var realDUMP = null;
MockDump.mSuiteSetup = function() {
realDUMP = window.DUMP;
window.DUMP = MockDump.disable();
};
MockDump.mSuiteTeardown = function() {
window.DUMP = realDUMP;
};

View file

@ -0,0 +1,15 @@
'use strict';
var MockEnumerateAll = function(storages, dir, options) {
return {
result: {
name: MockEnumerateAll.certificateName
},
set onsuccess(callback) {
callback();
},
continue: function() {}
};
};
MockEnumerateAll.certificateName = 'filename';

View file

@ -0,0 +1,120 @@
'use strict';
(function(exports) {
/**
* This is an reimplementation of EventTarget in JavaScript,
* usable as a mock for tests, and creating interfaces that inherits
* from MockEventTarget.
*
* @class MockEventTarget
*/
var MockEventTarget = function MockEventTarget() {
};
/**
* Adding an event listener for the success/error event.
* @memberof MockEventTarget.prototype
* @param {String} type Event type, success or error.
* @param {Function|Object} handler Event handler.
* @param {Boolean} capture True if use capture mode.
*/
MockEventTarget.prototype.addEventListener =
function(type, handler, capture) {
if (typeof handler !== 'function' && handler &&
typeof handler.handleEvent !== 'function') {
return;
}
var eventCallbacks;
if (capture) {
eventCallbacks = this._captureCallbacks = this._captureCallbacks || {};
} else {
eventCallbacks = this._bubbleCallbacks = this._bubbleCallbacks || {};
}
eventCallbacks[type] = eventCallbacks[type] || [];
// the same handler should not be added twice.
if (eventCallbacks[type].indexOf(handler) !== -1) {
return;
}
eventCallbacks[type].push(handler);
};
/**
* Remove an event listener for the success/error event.
* @memberof MockEventTarget.prototype
* @param {String} type Event type, success or error.
* @param {Function|Object} handler Event handler.
* @param {Boolean} capture True if use capture mode.
*/
MockEventTarget.prototype.removeEventListener = function(type, handler,
capture) {
if (typeof handler !== 'function' && handler &&
typeof handler.handleEvent !== 'function') {
return;
}
var eventCallbacks;
if (capture) {
eventCallbacks = this._captureCallbacks = this._captureCallbacks || {};
} else {
eventCallbacks = this._bubbleCallbacks = this._bubbleCallbacks || {};
}
var callbacks = eventCallbacks[type] || [];
var index = callbacks.indexOf(handler);
if (index === -1) {
return;
}
callbacks.splice(index, 1);
};
/**
* Dispatch event.
* @memberof MockEventTarget.prototype
* @param {object} event Mocked event to dispatch (`type` is required).
*/
MockEventTarget.prototype.dispatchEvent = function(evt) {
var type = evt.type;
this._captureCallbacks = this._captureCallbacks || {};
this._bubbleCallbacks = this._bubbleCallbacks || {};
var captureCallbacks = this._captureCallbacks[type] || [];
var bubbleCallbacks = this._bubbleCallbacks[type] || [];
// Don't overwrite evt.target if it is assigned already.
// Sometimes we want to simulate event bubbling/capturing in the DOM tree
// even though this is not how this mock behaves.
if (!evt.target) {
evt.target = this;
}
evt.currentTarget = this;
captureCallbacks.forEach(function fireCaptureEvents(handler) {
if (typeof handler === 'function') {
handler.call(this, evt);
} else {
handler.handleEvent(evt);
}
}, this);
if (('on' + evt.type) in this &&
typeof this['on' + evt.type] === 'function') {
this['on' + evt.type](evt);
}
bubbleCallbacks.forEach(function fireCaptureEvents(handler) {
if (typeof handler === 'function') {
handler.call(this, evt);
} else {
handler.handleEvent(evt);
}
}, this);
};
exports.MockEventTarget = MockEventTarget;
}(window));

View file

@ -0,0 +1,52 @@
'use strict';
/* exported MockFbContacts */
var MockFbContactsObj = function(result, inError) {
this.mResult = result;
this.inError = inError;
};
MockFbContactsObj.prototype = {
_getRequest: function() {
if (this.inError === true) {
return {
error: {
name: 'UnknownError'
},
set onerror(cb) {
window.setTimeout(function() {
cb({
target: this
});
});
}
};
}
return {
result: this.mResult,
set onsuccess(cb) {
cb();
}
};
},
search: function(by, number) {
return this._getRequest();
},
get: function(uid) {
return this._getRequest();
},
getByPhone: function(number) {
return this._getRequest();
},
mTeardown: function() {
this.mResult = [];
}
};
var MockFbContacts = new MockFbContactsObj();

View file

@ -0,0 +1,20 @@
'use strict';
/* exported MockFbReaderUtilsObj */
var MockFbReaderUtilsObj = function() {
this.getContactByNumber = function(number, cb, errorCb) {
if (this.inError) {
errorCb({
name: 'Data in error'
});
return;
}
if (number !== this.targetFbNumber) {
cb(null);
return;
}
cb({ name: [this.fbContactName], isFbContact: true });
};
};

View file

@ -0,0 +1,8 @@
/* exported MockFontSizeUtils */
'use strict';
var MockFontSizeUtils = {
getMaxFontSizeInfo: function() {},
getOverflowCount: function() {}
};

View file

@ -0,0 +1,68 @@
/* exported MockGeolocation */
'use strict';
var MockGeolocation = {
mSetup: function() {
this.fakeCoords = {
latitude: 37.388590,
longitude: -122.061704,
accuracy: 5.0
};
this.fakePosition = {
timestamp: 1404756850457,
coords: this.fakeCoords
};
this.activeWatches = [];
this.realGetCurrentPosition = navigator.geolocation.getCurrentPosition;
navigator.geolocation.getCurrentPosition = (function(onsuccess, onerror) {
if (onsuccess) {
onsuccess(this.createFakePosition());
}
}).bind(this);
this.realWatchPosition = navigator.geolocation.watchPosition;
navigator.geolocation.watchPosition = (function(onsuccess, onerror) {
var watch = setInterval(function() {
if (onsuccess) {
onsuccess(this.createFakePosition());
}
}.bind(this), 4);
this.activeWatches.push(watch);
return watch;
}).bind(this);
this.realClearWatch = navigator.geolocation.clearWatch;
navigator.geolocation.clearWatch = (function(watch) {
var idx = this.activeWatches.indexOf(watch);
if (idx >= 0) {
this.activeWatches.splice(idx, 1);
clearInterval(watch);
}
}).bind(this);
this.createFakePosition = function() {
this.fakePosition = {
timestamp: Date.now(),
coords: this.fakeCoords
};
return this.fakePosition;
};
},
mTeardown: function() {
for (var i = 0; i < this.activeWatches.length; i++) {
clearInterval(this.activeWatches[i]);
}
this.activeWatches = [];
navigator.geolocation.getCurrentPosition = this.realGetCurrentPosition;
navigator.geolocation.watchPosition = this.realWatchPosition;
navigator.geolocation.clearWatch = this.realClearWatch;
}
};

View file

@ -0,0 +1,9 @@
'use strict';
var MockGestureDetector = function() {};
MockGestureDetector.prototype = {
startDetecting: function() {},
stopDetecting: function() {}
};

View file

@ -0,0 +1,80 @@
'use strict';
/* exported MockIccHelper */
var MockIccHelper = {
mProps: {'cardState': null, 'iccInfo': {}, 'retryCount': 0},
mEventListeners: {'cardstatechange': [], 'iccinfochange': []},
mSetup: function icch_suite_setup() {
this.mProps = {
'cardState': null,
'iccInfo': {},
'retryCount': 0
};
this.mEventListeners = {'cardstatechange': [], 'iccinfochange': []};
},
mTeardown: function icch_teardown() {
this.mProps = {};
this.mEventListeners = {};
},
mTriggerEventListeners: function icch_triggerEventListeners(type, evt) {
if (!this.mEventListeners[type]) {
return;
}
this.mEventListeners[type].forEach(function(callback) {
if (typeof callback === 'function') {
callback(evt);
} else if (typeof callback == 'object' &&
typeof callback.handleEvent === 'function') {
callback.handleEvent(evt);
}
});
if (typeof this['on' + type] === 'function') {
this['on' + type](evt);
}
},
get cardState() {
return this.mProps.cardState;
},
get iccInfo() {
return this.mProps.iccInfo;
},
getCardLockRetryCount: function(lockType, onresult) {
onresult(this.mProps.retryCount);
},
setProperty: function _setProperty(property, newState) {
this.mProps[property] = newState;
},
addEventListener: function icch_addEventListener(type, callback) {
if (this.mEventListeners[type]) {
this.mEventListeners[type][this.mEventListeners[type].length] = callback;
}
},
removeEventListener: function icch_removeEventListener(type, callback) {
if (this.mEventListeners[type]) {
var idx = this.mEventListeners[type].indexOf(callback);
this.mEventListeners[type].splice(idx, 1);
}
},
unlockCardLock: function() {
var settingsRequest = {
result: {},
set onsuccess(callback) {
callback.call(this);
},
set onerror(callback) {}
};
return settingsRequest;
}
};

View file

@ -0,0 +1,8 @@
/* exported MockIccManager */
'use strict';
var MockIccManager = {
iccIds: [],
getIccById: function() {},
addEventListener: function() {}
};

View file

@ -0,0 +1,73 @@
'use strict';
var MockIccManager = function() {
this.iccIds = [];
this.adnContacts = [];
this.sdnContacts = [];
this.isAdnOnError = false;
this.isSdnOnError = false;
this.async = false;
};
MockIccManager.prototype.getIccById = function(id) {
var self = this;
return {
'iccInfo': {
'iccid': id
},
'readContacts': function(type) {
if (type === 'adn') {
return new MockReadContactsRequest(self.isAdnOnError, self.adnContacts,
self.async);
}
if (type === 'sdn') {
return new MockReadContactsRequest(self.isSdnOnError, self.sdnContacts,
self.async);
}
}
};
};
function MockReadContactsRequest(willFail, result, async) {
this._result = result;
this._willFail = willFail;
this._async = async;
}
MockReadContactsRequest.prototype = {
set onsuccess(successCb) {
var self = this;
if (!this._willFail) {
this.result = this._result;
if (typeof successCb === 'function') {
if (this._async) {
setTimeout(function() {
successCb({ target: self });
});
} else {
successCb({ target: this });
}
}
}
},
set onerror(errorCb) {
var self = this;
if (this._willFail) {
this.error = { name: 'error' };
if (typeof errorCb === 'function') {
if (this._async) {
setTimeout(function() {
errorCb(self.error);
});
} else {
errorCb(this.error);
}
}
}
}
};

View file

@ -0,0 +1,14 @@
/* globals Promise */
/* exported MockICEStore */
'use strict';
var MockICEStore = {
setContacts: function mis_setContacts(ids) {
MockICEStore.ids = ids;
return Promise.resolve(ids);
},
getContacts: function mis_getContacts() {
return Promise.resolve(MockICEStore.ids);
},
onChange: function() {}
};

View file

@ -0,0 +1,38 @@
'use strict';
/* exported MockIconsHelper */
var _defaultIconSize = 0;
var MockIconsHelper = {
set _defaultIconSize(size) {
_defaultIconSize = size;
},
getIcon: function(url, placeObject, siteObject) {
return new Promise(resolve => {
resolve();
});
},
getIconBlob: function(uri, iconTargetSize, placeObj = {}, siteObj = {}) {
return Promise.resolve({
blob: 'abc',
originalUrl: 'http://example.com/icons/16.png',
timestamp: Date.now()
});
},
setElementIcon: Promise.resolve.bind(Promise),
getBestIconFromWebManifest: function() {},
getBestIconFromMetaTags: function() {},
fetchIcon: function() {},
fetchIconBlob: function() {},
get defaultIconSize() {
return _defaultIconSize;
},
clear: function() {}
};

View file

@ -0,0 +1,34 @@
'use strict';
/* exported MockImage */
var MockImage = function() {
this.complete = false;
this.onload = null;
this.onabort = null;
this.onerror = null;
MockImage.instances.push(this);
};
MockImage.instances = [];
MockImage.triggerEvent = function(name) {
MockImage.instances.forEach(function(aInstance) {
aInstance.triggerEvent(name);
});
};
MockImage.teardown = function() {
MockImage.instances = [];
};
MockImage.prototype.triggerEvent = function(name) {
var callback = this[name];
if (name === 'onload') {
this.complete = true;
}
callback && callback();
};

View file

@ -0,0 +1,244 @@
'use strict';
/* global sinon */
/* exported MockIndexedDB */
function MockIndexedDB() {
var dbs = [];
var deletedDbs = [];
this.options = {};
this.storedDataDbs = {};
var self = this;
var nextId = 1;
Object.defineProperty(this, 'dbs', {
get: function() {
return dbs;
}
});
Object.defineProperty(this, 'deletedDbs', {
get: function() {
return deletedDbs;
}
});
var FakeDB = function(name, storedData) {
var dummyFunction = function(obj) {
return obj;
};
this.receivedData = [];
this.receivedDataHash = {};
this.deletedData = [];
this.storedData = storedData || {};
this.options = {};
var objectStore = {
createIndex: dummyFunction
};
this.objectStoreNames = ['fakeObjStore'];
this.createObjectStore = dummyFunction.bind(undefined,
objectStore);
this.deleteObjectStore = dummyFunction;
this.transaction = sinon.stub();
this.objectStore = sinon.stub();
this.get = dummyFunction;
this.put = dummyFunction;
this.delete = dummyFunction;
this.openCursor = dummyFunction;
this.close = dummyFunction;
this.clear = dummyFunction;
this.index = dummyFunction;
this.transaction.returns(this);
this.objectStore.returns(this);
var self = this;
sinon.stub(this, 'index', function(indexName) {
var data = self.storedData;
if (self._indexedData && self._indexedData.by) {
data = self._indexedData.by[indexName] || self.storedData;
}
return new FakeIndex(indexName, data, self.options);
});
sinon.stub(this, 'close', function() {
self.isClosed = true;
});
sinon.stub(this, 'put', function(data) {
data.id = data.id || nextId++;
self.receivedDataHash[data.id] = data;
self.receivedData.push(data);
return _getRequest();
});
sinon.stub(this, 'get', function(key) {
return _getRequest(self.storedData[key] || self.receivedDataHash[key]);
});
sinon.stub(this, 'openCursor', function() {
if (self.options.cursorOpenInError === true) {
return _getRequest(null, {
isInError: true
});
}
if (Object.keys(self.storedData).length === 0) {
return _getRequest(null);
}
var cursor = new FakeCursor(self.storedData);
var req = _getRequest(cursor);
cursor.request = req;
return req;
});
sinon.stub(this, 'delete', function(id) {
if (Array.isArray(self.options.deleteInError) &&
self.options.deleteInError.indexOf(id) !== -1) {
return _getRequest(null, {
isInError: true
});
}
delete self.storedData[id];
delete self.receivedDataHash[id];
var idx = self.receivedData.findIndex(function(elem) {
return elem.id && elem.id === id;
});
if (idx !== -1) {
self.receivedData.splice(idx, 1);
}
self.deletedData.push(id);
return _getRequest(true);
});
sinon.stub(this, 'clear', function() {
self.storedData = {};
self.receivedDataHash = {};
self.receivedData = [];
return _getRequest();
});
};
function FakeIndex(indexName, data, options) {
this.openCursor = function() {
if (options.cursorOpenInError === true) {
return _getRequest(null, {
isInError: true
});
}
if (Object.keys(data).length === 0) {
return _getRequest(null);
}
var cursor = new FakeCursor(data);
var req = _getRequest(cursor);
cursor.request = req;
return req;
};
}
var FakeCursor = function(data) {
var _pointer = 0;
var keys = Object.keys(data);
Object.defineProperty(this, 'value', {
get: function() {
return data[keys[_pointer]];
}
});
this.continue = function() {
_pointer++;
if (_pointer < keys.length) {
this.request.done(this);
}
else {
this.request.done(null);
}
};
};
var openStub = sinon.stub(window.indexedDB, 'open', function(name) {
if (Array.isArray(self.options.inErrorDbs) &&
self.options.inErrorDbs.indexOf(name) !== -1) {
return _getRequest(null, {
isInError: true
});
}
var db = new FakeDB(name, self.storedDataDbs[name]);
dbs.push(db);
var outReq = _getRequest(db, {
upgradeNeeded: (Array.isArray(self.options.upgradeNeededDbs) &&
self.options.upgradeNeededDbs.indexOf(name) !== -1)
});
return outReq;
});
var deleteDatabaseStub = sinon.stub(window.indexedDB, 'deleteDatabase',
function(name) {
deletedDbs.push(name);
return _getRequest(null);
});
function _getRequest(result, opts) {
var options = opts || {};
return {
result: result,
error: null,
set onerror(cb) {
if (options.isInError) {
this.error = {
name: 'DB In Error'
};
cb({
target: this
});
}
},
set onsuccess(cb) {
this._doneCb = cb;
if (!options.isInError) {
cb({
target: this
});
}
},
set onupgradeneeded(cb) {
if (options.upgradeNeeded) {
cb({
target: this
});
}
},
done: function(result) {
this.result = result;
if (typeof this._doneCb === 'function') {
this._doneCb({
target: this
});
}
}
};
}
this.mTearDown = () => {
openStub.restore();
deleteDatabaseStub.restore();
};
}

View file

@ -0,0 +1,31 @@
(function(global) {
'use strict';
/* global mozIntl */
const helperCache = new Map();
global.MockIntlHelper = {
define: function(name, type, options) {
helperCache.set(name, {options, type});
},
get: function(name) {
if (!helperCache.has(name)) {
throw new Error('Intl Object with name "' + name + '" is not defined');
}
var formatter = helperCache.get(name);
switch (formatter.type) {
case 'number':
return Intl.NumberFormat(navigator.languages, formatter.options);
case 'datetime':
return Intl.DateTimeFormat(navigator.languages, formatter.options);
case 'mozdatetime':
return mozIntl.DateTimeFormat(navigator.languages, formatter.options);
case 'mozduration':
return mozIntl.DurationFormat(navigator.languages, formatter.options);
}
},
observe: function() {},
unobserve: function() {},
};
}(this));

View file

@ -0,0 +1,142 @@
'use strict';
var MockKeyboardHelper = {
fallbackLayouts: {},
mKeyboards: [
{
manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp',
manifest: {
name: 'app1',
description: 'app1',
permissions: {
'settings': { 'access': 'readwrite' },
'keyboard': {}
},
role: 'input',
launch_path: '/settings.html',
inputs: {
'en': {
'name': 'layout1',
'launch_path': '/index.html#layout1',
'description': 'layout1',
'types': ['url', 'text'],
enabled: true,
'default': true
},
'number': {
'name': 'layout2',
'launch_path': '/index.html#layout2',
'description': 'layout2',
'types': ['number']
}
}
}
},
{
manifestURL: 'app://app2.gaiamobile.org/manifest.webapp',
manifest: {
name: 'app2',
description: 'app2',
permissions: {
'settings': { 'access': 'readwrite' },
'input': {}
},
role: 'input',
launch_path: '/settings.html',
inputs: {
'layout1': {
'name': 'layout1',
'launch_path': '/index.html#layout1',
'description': 'layout1',
'types': ['url']
}
}
}
},
{
manifestURL: 'app://app3.gaiamobile.org/manifest.webapp',
manifest: {
name: 'app3',
description: 'app3',
permissions: {
'settings': { 'access': 'readwrite' },
'keyboard': {}
},
role: 'input',
launch_path: '/settings.html',
inputs: {
'layout1': {
'name': 'layout1',
'launch_path': '/index.html#layout1',
'description': 'layout1',
'types': ['number'],
enabled: true,
'default': true
}
}
}
}
],
mSetup: function() {
this.watchCallback = null;
// dirty clone
this.keyboards = JSON.parse(JSON.stringify(this.mKeyboards));
this.layouts = this.keyboards.reduce(function(carry, keyboard) {
var inputIds = Object.keys(keyboard.manifest.inputs);
var layouts = inputIds.map(function(layoutId) {
var inputManifest = keyboard.manifest.inputs[layoutId];
return {
app: keyboard,
manifest: keyboard.manifest,
inputManifest: inputManifest,
layoutId: layoutId,
enabled: inputManifest.enabled,
'default': inputManifest['default']
};
});
return carry.concat(layouts);
}, []);
},
stopWatching: function() {
this.watchCallback = null;
},
getLayouts: function(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
callback(this.layouts);
},
watchLayouts: function(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
this.watchCallback = callback;
callback(this.layouts, { apps: true, settings: true });
},
checkDefaults: function() {},
setLayoutEnabled: function(manifestURL, layoutId, enabled) {
this.layouts.some(function eachLayout(layout) {
if (layout.app.manifestURL === manifestURL &&
layout.layoutId === layoutId) {
layout.enabled = enabled;
return true;
}
});
},
saveToSettings: function() {
if (this.watchCallback) {
this.watchCallback(this.layouts, { settings: true });
}
},
isKeyboardType: function() {
return true;
},
changeDefaultLayouts: function() {}
};
MockKeyboardHelper.mSuiteSetup = MockKeyboardHelper.mSetup;

View file

@ -0,0 +1,139 @@
/* globals assert */
(function(exports) {
'use strict';
exports.MockL10n = {
readyState: 'complete',
get: stringify,
_stringify: stringify,
formatValue: function(id, args) {
return Promise.resolve(stringify(id, args || ''));
},
translateFragment: function() {},
// XXX Remove in https://bugzil.la/1020137
localize: function(element, id, args) {
if (id) {
element.setAttribute('data-l10n-id', id);
} else {
element.removeAttribute('data-l10n-id');
element.removeAttribute('data-l10n-args');
element.textContent = '';
}
if (args) {
element.setAttribute('data-l10n-args', JSON.stringify(args));
} else {
element.removeAttribute('data-l10n-args');
}
element.textContent = id + (args ? JSON.stringify(args) : '');
},
setAttributes: function(element, id, args) {
element.setAttribute('data-l10n-id', id);
if (args) {
element.setAttribute('data-l10n-args', JSON.stringify(args));
}
},
getAttributes: function(element) {
return {
id: element.getAttribute('data-l10n-id'),
args: JSON.parse(element.getAttribute('data-l10n-args'))
};
},
ready: function(handler) {
setTimeout(handler);
},
once: function(handler) {
setTimeout(handler);
},
language: {
code: 'en-US',
direction: 'ltr'
},
qps: {
'fr-x-psaccent': {
id: 'fr-x-psaccent',
name: 'Pseudo English',
translate: stringify
}
},
DateTimeFormat: function() {
// Support navigator.mozL10n.DateTimeFormat() without new the object.
if (!this.localeFormat) {
var localeFormat = function mockLocaleFormat(time, strFormat) {
return '' + time;
};
return {
localeFormat: localeFormat
};
}
}
};
// Defining methods on the prototype allows to spy on them in tests
exports.MockL10n.DateTimeFormat.prototype = {
localeFormat: stringify,
fromNow: stringify,
relativeParts: stringify
};
/**
* Helper used to check that an element has been properly localized. This
* supports all the patterns described on MDN for localization best
* practices:
* - If 'expectedL10n' is a string it will check that the node's
* 'data-l10n-id' attribute is set and matches that id.
* - If 'expectedL10n' is an object then if it as an id and an args field it
* will check if the node's 'data-l10n-id' and 'data-l10n-args' fields
* match them.
* - If 'expectedL10n' is an object with a 'raw' field it will check that the
* node doesn't have a 'data-l10n-id' attribute and its 'textContent' field
* matches the'raw' field.
* - If 'expectedL10n' is an object with am 'html' field it will check that
* the node doesn't have a 'data-l10n-id' attribute and its 'innerHTML'
* field matches the'html' field.
*/
exports.l10nAssert = function(node, expectedL10n) {
if (typeof expectedL10n === 'string') {
assert.isTrue(node.hasAttribute('data-l10n-id'));
assert.equal(node.getAttribute('data-l10n-id'), expectedL10n);
} else if (expectedL10n.raw) {
assert.isFalse(node.hasAttribute('data-l10n-id'));
assert.equal(node.textContent, expectedL10n.raw);
} else if (expectedL10n.html) {
assert.isFalse(node.hasAttribute('data-l10n-id'));
assert.equal(node.innerHTML, expectedL10n.html);
} else if (expectedL10n.id) {
assert.isTrue(node.hasAttribute('data-l10n-id'));
assert.equal(node.getAttribute('data-l10n-id'), expectedL10n.id);
if (expectedL10n.args) {
assert.isTrue(node.hasAttribute('data-l10n-args'));
assert.deepEqual(
JSON.parse(node.getAttribute('data-l10n-args')),
expectedL10n.args
);
}
}
};
function stringify() {
return Array.prototype.reduce.call(arguments, function(prev, cur) {
if (typeof cur === 'object') {
cur = JSON.stringify(cur);
}
return prev + cur;
}, '');
}
}(this));

View file

@ -0,0 +1,91 @@
/* globals assert */
(function(exports) {
'use strict';
exports.MockL10n = {
_stringify: stringify,
formatValue: function(id, args) {
return Promise.resolve(
args ? stringify(id, args) : stringify(id));
},
formatValues: function(...keys) {
return Promise.resolve(
keys.map(key => stringify(key)));
},
translateFragment: function() {
return Promise.resolve();
},
setAttributes: function(element, id, args) {
element.setAttribute('data-l10n-id', id);
if (args) {
element.setAttribute('data-l10n-args', JSON.stringify(args));
}
},
getAttributes: function(element) {
return {
id: element.getAttribute('data-l10n-id'),
args: JSON.parse(element.getAttribute('data-l10n-args'))
};
},
ready: Promise.resolve(),
requestLanguages: function() {
return Promise.resolve();
},
};
/**
* Helper used to check that an element has been properly localized. This
* supports all the patterns described on MDN for localization best
* practices:
* - If 'expectedL10n' is a string it will check that the node's
* 'data-l10n-id' attribute is set and matches that id.
* - If 'expectedL10n' is an object then if it as an id and an args field it
* will check if the node's 'data-l10n-id' and 'data-l10n-args' fields
* match them.
* - If 'expectedL10n' is an object with a 'raw' field it will check that the
* node doesn't have a 'data-l10n-id' attribute and its 'textContent' field
* matches the'raw' field.
* - If 'expectedL10n' is an object with am 'html' field it will check that
* the node doesn't have a 'data-l10n-id' attribute and its 'innerHTML'
* field matches the'html' field.
*/
exports.l10nAssert = function(node, expectedL10n) {
if (typeof expectedL10n === 'string') {
assert.isTrue(node.hasAttribute('data-l10n-id'));
assert.equal(node.getAttribute('data-l10n-id'), expectedL10n);
} else if (expectedL10n.raw) {
assert.isFalse(node.hasAttribute('data-l10n-id'));
assert.equal(node.textContent, expectedL10n.raw);
} else if (expectedL10n.html) {
assert.isFalse(node.hasAttribute('data-l10n-id'));
assert.equal(node.innerHTML, expectedL10n.html);
} else if (expectedL10n.id) {
assert.isTrue(node.hasAttribute('data-l10n-id'));
assert.equal(node.getAttribute('data-l10n-id'), expectedL10n.id);
if (expectedL10n.args) {
assert.isTrue(node.hasAttribute('data-l10n-args'));
assert.deepEqual(
JSON.parse(node.getAttribute('data-l10n-args')),
expectedL10n.args
);
}
}
};
function stringify(...args) {
return args.reduce(
(prev, cur) =>
prev + (typeof cur === 'object' ? JSON.stringify(cur) : cur), '');
}
}(this));

View file

@ -0,0 +1,22 @@
(function(exports) {
'use strict';
var langs = {
'en-US': 'English (US)'
};
exports.MockLanguageList = {
_languages: {
then: function(callback) {
callback(langs);
}
},
get: function(callback) {
callback(langs, 'en-US');
},
wrapBidi: function(lang, name) {
return name;
}
};
}(this));

View file

@ -0,0 +1,16 @@
'use strict';
/* exported MockLazyLoader */
var MockLazyLoader = {
load: function(fileArray, callback) {
if (callback) {
callback();
} else {
return Promise.resolve();
}
},
getJSON: function(file) {
return Promise.resolve({});
}
};

View file

@ -0,0 +1,14 @@
'use strict';
/* exported MockManifestHelper */
function MockManifestHelper(manifest) {
for (var prop in manifest) {
this[prop] = manifest[prop];
}
}
Object.defineProperty(MockManifestHelper.prototype, 'displayName', {
get: function displayName() {
return this.short_name || this.name;
}
});

View file

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

View file

@ -0,0 +1,20 @@
'use strict';
/* exported MockMobileOperator */
var MockMobileOperator = {
userFacingInfo: function mmo_userFacingInfo(mobileConnection) {
return {
'operator': this.mOperator,
'carrier': this.mCarrier,
'region': this.mRegion
};
},
mOperator: '',
mCarrier: '',
mRegion: '',
mTeardown: function() {
this.mOperator = this.mCarrier = this.mRegion = '';
}
};

View file

@ -0,0 +1,157 @@
'use strict';
var MockMozContacts;
function MockMozContactsObj(contacts) {
this.contacts = contacts;
}
var listeners = {};
MockMozContactsObj.prototype = {
limit: 20,
addEventListener: function(evt, handler) {
listeners[evt] = handler;
},
removeEventListener: function(evt, handler) {
delete listeners[evt];
},
dispatchEvent: function(evt) {
listeners[evt.type] && listeners[evt.type](evt);
},
_getRequest: function(options) {
var that = this;
function Req(options) {
var self = this;
Object.defineProperty(self, 'onsuccess', { set: function(cb) {
if (options.type === 'find') {
if (options.data.filterBy &&
options.data.filterBy.indexOf('id') !== -1) {
for (var i = 0; i < that.contacts.length; i++) {
if (options.data.filterValue === that.contacts[i].id) {
self.result = [that.contacts[i]];
}
}
} else {
self.result = that.contacts;
}
} else {
self.result = options.data;
}
cb.call(self, { target: self });
}});
}
return new Req(options);
},
find: function find(filter) {
return this._getRequest({
type: 'find',
data: filter
});
},
total: 0,
set number(n) {
this.total = n;
},
getCount: function() {
return this._getRequest({
type: 'count',
data: MockMozContacts.total
});
},
getAll: function getAll() {
return {
set onsuccess(callback) {
var count = 0;
this.continue = function() {
count++;
var evt = {
target: {}
};
if (count <= MockMozContacts.limit) {
evt.target.result = {
id: count,
givenName: ['givenName ' + count],
lastName: ['lastName ' + count]
};
}
callback.call(this, evt);
};
var evt = {
target: {
result: {
id: count,
givenName: ['givenName ' + count],
lastName: ['lastName ' + count]
}
}
};
callback.call(this, evt);
},
set onerror(callback) {
}
};
},
save: function save(ct) {
ct.id = this.contacts.length;
this.contacts.push(ct);
return {
set onsuccess(callback) {
var self = this;
setTimeout(function() {
callback.call(self);
}, 10);
},
set onerror(callback) {
}
};
},
getRevision: function() {
var req = {
result: 'fakeRevision',
set onsuccess(callback) {
setTimeout(() => {
callback.call(this, { target: req });
});
},
set onerror(callback) {
}
};
return req;
},
setContacts: function(contacts) {
this.contacts = contacts;
},
remove: function remove(ct) {
var contactsIndex = this.contacts.indexOf(ct);
if (contactsIndex > -1) {
this.contacts.splice(contactsIndex, 1);
}
return {
set onsuccess(callback) {
var self = this;
setTimeout(function() {
callback.call(self);
}, 0);
},
set onerror(callback) {
}
};
},
clear: function clear() {
this.contacts.splice(0, this.contacts.length);
}
};
MockMozContacts = new MockMozContactsObj([]);

View file

@ -0,0 +1,49 @@
'use strict';
/* exported MockMozActivity */
var MockMozActivity = function(info) {
MockMozActivity.calls.push(info);
MockMozActivity.instances.push(this);
if (!info) {
return;
}
var name = info.name;
var data = info.data;
return {
set onsuccess(cb) {
MockMozActivity.timeouts.push(setTimeout(cb, 50));
},
set onerror(cb) {
MockMozActivity.errorCallback = cb;
},
name: name,
data: data,
get result() {
return MockMozActivity.successResult;
}
};
};
// create an array to hold any calls to this function to test them
MockMozActivity.mSetup = function() {
MockMozActivity.calls = [];
MockMozActivity.instances = [];
MockMozActivity.successResult = null;
MockMozActivity.timeouts = [];
};
MockMozActivity.mTriggerOnError = function() {
MockMozActivity.errorCallback();
};
// destroy the array so it resets every time
MockMozActivity.mTeardown = function() {
delete MockMozActivity.calls;
delete MockMozActivity.instances;
MockMozActivity.timeouts.forEach(clearTimeout);
MockMozActivity.timeouts = [];
};

View file

@ -0,0 +1,96 @@
// modified from the clock app's mock_moz_alarm.js for bug 1034570
(function(exports) {
'use strict';
var mozAlarmsId = 0;
function MockOSAlarm(date, respectTimezone, data, callback) {
this.date = date || new Date();
this.data = data || {};
this.callback = callback;
this.id = mozAlarmsId++;
this.respectTimezone = respectTimezone;
this.timeout = setTimeout(function() {
if ((typeof this.callback) === 'function') {
this.callback({
id: this.id,
respectTimezone: this.respectTimezone,
date: this.date,
data: this.data
});
}
}.bind(this), this.date.getTime() - Date.now());
}
function MockMozAlarmRequest(config) {
this.init(config || {});
}
MockMozAlarmRequest.prototype = {
init: function mar_init(config) {
setTimeout(function() {
this.fire(config.error, config.result);
}.bind(this), 0);
},
fire: function mar_call(error, result) {
if (!error) {
this.result = result;
this.onsuccess && this.onsuccess(this);
} else {
this.error = error;
this.onerror && this.onerror(this);
}
}
};
var messageHandlers = [];
navigator.mozSetMessageHandler = function(type, callback) {
if (type === 'alarm') {
messageHandlers.push(callback);
}
};
exports.MockMozAlarms = {
alarms: [],
getAll: function() {
return new MockMozAlarmRequest({
result: this.alarms
});
},
add: function(date, timezone, data) {
if (!(timezone === 'ignoreTimezone' ||
timezone === 'honorTimezone')) {
throw new Error('invalid timezone argument');
}
var alarm = new MockOSAlarm(date, timezone, data, function(x) {
messageHandlers.forEach(function(callback) {
callback(x);
});
this.remove(x.id);
}.bind(this));
this.alarms.push(alarm);
return new MockMozAlarmRequest({
result: alarm.id
});
},
remove: function(id) {
for (var i = 0; i < this.alarms.length; i++) {
var alarm = this.alarms[i];
if (alarm && (alarm.id === id)) {
clearTimeout(alarm.timeout);
this.alarms.splice(i, 1);
return true;
}
}
return false;
}
};
}(this));

View file

@ -0,0 +1,13 @@
/* exported MockmozContact */
'use strict';
var MockmozContact = function(obj) {
if (!obj) {
return;
}
var self = this;
Object.keys(obj).forEach(function(k) {
self[k] = obj[k];
});
};

View file

@ -0,0 +1,61 @@
(function(global) {
'use strict';
global.MockMozIntl = {
formatList: function(list) {
return Promise.resolve(list.join(', '));
},
DateTimeFormat: function(locales, options) {
var formatter = Intl.DateTimeFormat(locales, options);
return {
// we need this because mozIntl options extend Intl options
resolvedOptions() { return options; },
format: formatter.format.bind(formatter)
};
},
calendarInfo: function(token) {
return Promise.resolve(0);
},
DurationFormat: function(locales, options) {
return Promise.resolve({
format: function(input) {
return JSON.stringify({ value: input, options: options });
}
});
},
RelativeTimeFormat: function(locales, options) {
return {
format: function(value) {
return Promise.resolve();
}
};
},
UnitFormat: function(locales, options) {
return {
format: function(value) {
return Promise.resolve();
}
};
},
_gaia: {
relativePart: function(ms) {
return {
unit: 'minutes',
value: Math.abs(parseInt(ms / 1000 / 60))
};
},
RelativeDate: function(locales, options) {
return {
format: function(value) {
return Promise.resolve('pretty date');
},
formatElement: function(element, time, maxDiff) {}
};
},
getFormattedUnit(type, style, v) {
return Promise.resolve();
},
}
};
}(this));

View file

@ -0,0 +1,13 @@
'use strict';
var MockMozNDEFRecord = function(dict) {
this.tnf = dict ? dict.tnf : 'empty';
this.type = dict && dict.type;
this.id = dict && dict.id;
this.payload = dict && dict.payload;
};
MockMozNDEFRecord.tnf = 'empty';
MockMozNDEFRecord.type = null;
MockMozNDEFRecord.id = null;
MockMozNDEFRecord.payload = null;

View file

@ -0,0 +1,20 @@
/* exported MockMozNfc */
/* global MockPromise */
'use strict';
(function(exports) {
require('/shared/test/unit/mocks/mock_promise.js');
var MockMozNfc = {
onpeerready: null,
notifySendFileStatus: function() {
},
MockNFCPeer: {
isLost: false,
sendNDEF: function(records) {
return new MockPromise();
}
}
};
exports.MockMozNfc = MockMozNfc;
})(window);

View file

@ -0,0 +1,9 @@
'use strict';
/* global SimplePhoneMatcher */
/* exported MockPhoneNumberService */
var MockPhoneNumberService = {
normalize: function(number) {
return SimplePhoneMatcher.sanitizedNumber(number);
}
};

View file

@ -0,0 +1,15 @@
'use strict';
/* globals Promise */
var MockMultiContact = {
_data: Object.create(null),
getData: function(entry) {
return new Promise(function(resolve, reject) {
var id = entry.entryData[0].uid;
resolve(MockMultiContact._data[id]);
});
}
};

View file

@ -0,0 +1,22 @@
/* exported MockMultiSimActionButton */
'use strict';
var MockMultiSimActionButtonSingleton = {
mIsInitialized: false,
performAction: function() {
},
_phoneNumberGetter: null,
_click: function() {}
};
var MockMultiSimActionButton =
function(button, callCallback, settings, phoneNumberGetter) {
MockMultiSimActionButtonSingleton.mIsInitialized = true;
MockMultiSimActionButtonSingleton._phoneNumberGetter = phoneNumberGetter;
button.addEventListener('click', MockMultiSimActionButtonSingleton._click);
return MockMultiSimActionButtonSingleton;
};

View file

@ -0,0 +1,27 @@
'use strict';
(function(exports) {
var id = 0;
var MockMutationObserver = function MockMutationObserver(callback) {
var o = {
id: id++,
type: 'MockMutationObserver',
observe: function(element, config) {},
disconnect: function() {},
mTriggerCallback: function(mutations) {
callback(mutations);
}
};
MockMutationObserver.mLastObserver = o;
return o;
};
MockMutationObserver.mTeardown = function() {
this.mLastObserver = null;
};
exports.MockMutationObserver = MockMutationObserver;
})(window);

View file

@ -0,0 +1,221 @@
'use strict';
/* exports MockDatastore, MockDatastoreObj */
function MockDatastoreObj(name, owner, records) {
this.name = name || 'Mock_Datastore';
this.owner = owner;
this._records = records || Object.create(null);
}
MockDatastoreObj.prototype = {
readOnly: false,
revisionId: '123456',
_nextId: 1,
_inError: false,
_raceCondition: null,
_cb: null,
_clone: function(obj) {
var out = null;
if (obj) {
out = JSON.parse(JSON.stringify(obj));
}
return out;
},
_reject: function(errorName, errorMessage) {
return new window.Promise(function(resolve, reject) {
reject({
name: errorName || 'UnknownError',
message: errorMessage
});
});
},
get: function() {
if (this._inError === true) {
return this._reject();
}
var self = this;
var funcArgs = arguments;
return new window.Promise(function(resolve, reject) {
var dsIds;
var args = [];
for (var j = 0; j < funcArgs.length; j++) {
args.push(funcArgs[j]);
}
if (args.length === 1) {
dsIds = Array.isArray(args[0]) ? args[0] : [args[0]];
} else {
dsIds = args;
}
var results = [];
dsIds.forEach(function(aId) {
var record = self._clone(self._records[aId]);
results.push(record);
});
var out = args.length > 1 ? results : results[0];
resolve(out);
});
},
put: function(obj, dsId, revisionId) {
if (this._inError === true) {
return this._reject();
}
if (revisionId && this._raceCondition) {
return this._reject('ConstraintError', 'RevisionId is not up-to-date');
}
if (dsId === this._nextId) {
this._nextId++;
}
this._records[dsId] = this._clone(obj);
var self = this;
return new window.Promise(function(resolve, reject) {
resolve();
self._cb && self._cb({
operation: 'updated',
id: dsId
});
});
},
add: function(obj, dsId, revisionId) {
if (this._inError === true) {
return this._reject();
}
if (revisionId && this._raceCondition) {
return this._reject('ConstraintError', 'RevisionId is not up-to-date');
}
var newId = dsId || this._nextId;
this._nextId++;
if (typeof this._records[newId] !== 'undefined') {
return this._reject('ConstraintError');
}
this._records[newId] = this._clone(obj);
var self = this;
return new window.Promise(function(resolve, reject) {
resolve(newId);
self._cb && self._cb({
operation: 'added',
id: newId
});
});
},
remove: function(dsId, revisionId) {
if (this._inError === true) {
return this._reject();
}
if (revisionId && this._raceCondition) {
return this._reject('ConstraintError', 'RevisionId is not up-to-date');
}
delete this._records[dsId];
return new window.Promise(function(resolve, reject) {
resolve(true);
});
},
sync: function(revisionId) {
return {
next: () => {
if (!this._tasks[this._taskCounter]) {
this._taskCounter = 0;
}
return Promise.resolve(this._tasks[this._taskCounter++]);
}
};
},
_taskCounter: 0,
// Please make sure there is always a 'done' task at the end.
_tasks: [
{
operation: 'update',
id: 0,
data: {}
},
{
operation: 'done',
id: 0,
data: null
}
],
getLength: function() {
if (this._inError === true) {
return this._reject();
}
var total = Object.keys(this._records).length;
return new window.Promise(function(resolve, reject) {
resolve(total);
});
},
clear: function(revisionId) {
if (this._inError === true) {
return this._reject();
}
if (revisionId && this._raceCondition) {
return this._reject('ConstraintError', 'RevisionId is not up-to-date');
}
this._records = {};
this._nextId = 1;
return new window.Promise(function(resolve, reject) {
resolve();
});
},
addEventListener: function(type, cb) {
if (type === 'change') {
this._cb = cb;
}
},
removeEventListener: function() {
this._cb = null;
}
};
var MockDatastore = new MockDatastoreObj();
var MockNavigatorDatastore = {
_datastores: null,
getDataStores: function() {
if (MockNavigatorDatastore._notFound === true) {
return new window.Promise(function(resolve, reject) {
resolve([]);
});
}
return new window.Promise(function(resolve, reject) {
if (!MockNavigatorDatastore._datastores) {
resolve([MockDatastore]);
} else {
resolve(MockNavigatorDatastore._datastores);
}
});
}
};

View file

@ -0,0 +1,9 @@
'use strict';
/* global Promise */
window.MockNavigatorGetFeature = function(key) {
return new Promise((resolve, reject) => {
resolve(window.MockNavigatorGetFeature._values[key]);
});
};
window.MockNavigatorGetFeature._values = {};

Some files were not shown because too many files have changed in this diff Show more