Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/test
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/test')
-rw-r--r--apps/system/test/integration/atoms/notification.js15
-rw-r--r--apps/system/test/integration/notification_test.js41
-rw-r--r--apps/system/test/integration/system_integration.js49
-rw-r--r--apps/system/test/unit/_proxy.html49
-rw-r--r--apps/system/test/unit/_sandbox.html28
-rw-r--r--apps/system/test/unit/app_install_manager_test.js1036
-rw-r--r--apps/system/test/unit/battery_manager_test.js204
-rw-r--r--apps/system/test/unit/date_picker_test.js483
-rw-r--r--apps/system/test/unit/identity_test.js92
-rw-r--r--apps/system/test/unit/lockscreen_test.js122
-rw-r--r--apps/system/test/unit/mobile_operator_test.js96
-rw-r--r--apps/system/test/unit/mock_app.js93
-rw-r--r--apps/system/test/unit/mock_applications.js26
-rw-r--r--apps/system/test/unit/mock_apps_mgmt.js64
-rw-r--r--apps/system/test/unit/mock_asyncStorage.js36
-rw-r--r--apps/system/test/unit/mock_chrome_event.js4
-rw-r--r--apps/system/test/unit/mock_custom_dialog.js26
-rw-r--r--apps/system/test/unit/mock_gesture_detector.js9
-rw-r--r--apps/system/test/unit/mock_l10n.js17
-rw-r--r--apps/system/test/unit/mock_manifest_helper.js5
-rw-r--r--apps/system/test/unit/mock_mobile_operator.js15
-rw-r--r--apps/system/test/unit/mock_modal_dialog.js34
-rw-r--r--apps/system/test/unit/mock_navigator_battery.js55
-rw-r--r--apps/system/test/unit/mock_navigator_moz_mobile_connection.js21
-rw-r--r--apps/system/test/unit/mock_navigator_moz_telephony.js55
-rw-r--r--apps/system/test/unit/mock_navigator_settings.js64
-rw-r--r--apps/system/test/unit/mock_navigator_wake_lock.js41
-rw-r--r--apps/system/test/unit/mock_notification_helper.js22
-rw-r--r--apps/system/test/unit/mock_notification_screen.js38
-rw-r--r--apps/system/test/unit/mock_settings_listener.js16
-rw-r--r--apps/system/test/unit/mock_sleep_menu.js5
-rw-r--r--apps/system/test/unit/mock_statusbar.js36
-rw-r--r--apps/system/test/unit/mock_system_banner.js13
-rw-r--r--apps/system/test/unit/mock_trusted_ui_manager.js25
-rw-r--r--apps/system/test/unit/mock_updatable.js72
-rw-r--r--apps/system/test/unit/mock_update_manager.js60
-rw-r--r--apps/system/test/unit/mock_utility_tray.js14
-rw-r--r--apps/system/test/unit/mock_window_manager.js16
-rw-r--r--apps/system/test/unit/mocks_helper.js40
-rw-r--r--apps/system/test/unit/notifications_test.js132
-rw-r--r--apps/system/test/unit/statusbar_test.js421
-rw-r--r--apps/system/test/unit/style/lockscreen/images/mask.pngbin0 -> 18182 bytes
-rw-r--r--apps/system/test/unit/updatable_test.js630
-rw-r--r--apps/system/test/unit/update_manager_test.js1449
44 files changed, 5769 insertions, 0 deletions
diff --git a/apps/system/test/integration/atoms/notification.js b/apps/system/test/integration/atoms/notification.js
new file mode 100644
index 0000000..cf3e44f
--- /dev/null
+++ b/apps/system/test/integration/atoms/notification.js
@@ -0,0 +1,15 @@
+(function notification(text, desc) {
+ window.addEventListener('mozChromeEvent', function(e) {
+ var detail = e.detail;
+ if (detail.type === 'desktop-notification') {
+ marionetteScriptFinished(JSON.stringify(detail));
+ }
+ });
+
+ var notify = window.navigator.mozNotification;
+ var notification = notify.createNotification(
+ text, desc
+ );
+
+ notification.show();
+}.apply(this, arguments));
diff --git a/apps/system/test/integration/notification_test.js b/apps/system/test/integration/notification_test.js
new file mode 100644
index 0000000..aa532e4
--- /dev/null
+++ b/apps/system/test/integration/notification_test.js
@@ -0,0 +1,41 @@
+require('/apps/system/test/integration/system_integration.js');
+
+suite('notifications', function() {
+
+ var device;
+ var app;
+
+ MarionetteHelper.start(function(client) {
+ app = new SystemIntegration(client);
+ device = app.device;
+ });
+
+ setup(function() {
+ yield app.launch();
+ });
+
+ test('text/description notification', function() {
+
+ var title = 'uniq--integration--uniq';
+ var description = 'q--desc--q';
+
+ yield device.setContext('chrome');
+
+ yield IntegrationHelper.sendAtom(
+ device,
+ '/apps/system/test/integration/atoms/notification',
+ true,
+ [title, description]
+ );
+
+ yield device.setContext('content');
+ var container = yield app.element('notificationsContainer');
+
+ var text = yield container.getAttribute('innerHTML');
+ assert.ok(text, 'container should have notifications');
+
+ assert.include(text, title, 'should include title');
+ assert.include(text, description, 'should include description');
+ });
+});
+
diff --git a/apps/system/test/integration/system_integration.js b/apps/system/test/integration/system_integration.js
new file mode 100644
index 0000000..1116e17
--- /dev/null
+++ b/apps/system/test/integration/system_integration.js
@@ -0,0 +1,49 @@
+require('/tests/js/app_integration.js');
+require('/tests/js/integration_helper.js');
+
+function SystemIntegration() {
+ AppIntegration.apply(this, arguments);
+}
+
+SystemIntegration.prototype = {
+ __proto__: AppIntegration.prototype,
+
+ appName: 'System',
+
+ selectors: {
+ /** notifications */
+ notificationsContainer: '#notifications-container'
+ },
+
+ /**
+ * Override base launch method.
+ * The system app is launched by directly
+ * going to its url we we determine by getting
+ * all the apps and finding the 'System' apps origin.
+ */
+ launch: function(callback) {
+ var self = this;
+ this.task(function(app, next, done) {
+ var device = app.device;
+ yield device.setScriptTimeout(5000);
+
+ yield IntegrationHelper.importScript(
+ device,
+ '/tests/atoms/gaia_apps.js',
+ MochaTask.nodeNext
+ );
+
+ var result = yield device.executeAsyncScript(
+ 'GaiaApps.locateWithName("' + self.appName + '");'
+ );
+
+ // locate the origin of the system app.
+ // We must append the /index.html because of the app:// protocol.
+ yield device.goUrl(result.origin + '/index.html');
+
+ // complete the task
+ done();
+ }, callback);
+ }
+
+};
diff --git a/apps/system/test/unit/_proxy.html b/apps/system/test/unit/_proxy.html
new file mode 100644
index 0000000..2102451
--- /dev/null
+++ b/apps/system/test/unit/_proxy.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Serve the tests</title>
+
+ <script type="text/javascript" charset="utf-8">
+ (function(window){
+ var Loader = window.CommonResourceLoader = {},
+ host = document.location.host,
+ domain = host.replace(/(^[\w\d-]+\.)?([\w\d]+\.[a-z]+)/, 'test-agent.$2');
+
+ Loader.domain = document.location.protocol + '//' + domain;
+ Loader.url = function(url){
+ return this.domain + url;
+ }
+
+ Loader.script = function(url, doc){
+ doc = doc || document;
+ doc.write('<script type="application/javascript;version=1.8" src="' + this.url(url) + '"><\/script>');
+ return this;
+ };
+ }(this));
+ </script>
+
+ <style type="text/css" media="all">
+ html,body,iframe {
+ height: 100%;
+ width: 100%;
+ }
+ </style>
+
+</head>
+<body>
+
+<!-- Test Agent UI will be loaded in here -->
+<div id="test-agent-ui">
+</div>
+
+<script type="text/javascript" charset="utf-8">
+CommonResourceLoader.
+ script('/common/test/test_url_resolver.js').
+ script('/common/vendor/test-agent/test-agent.js').
+ script('/common/test/agent_proxy.js');
+</script>
+</body>
+</html>
+
+
diff --git a/apps/system/test/unit/_sandbox.html b/apps/system/test/unit/_sandbox.html
new file mode 100644
index 0000000..70d0efa
--- /dev/null
+++ b/apps/system/test/unit/_sandbox.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Tests</title>
+ <link rel="stylesheet" type="text/css" href="/vendor/mocha/mocha.css" />
+ <style type="text/css" media="all">
+ iframe {
+ border: none;
+ padding: 0px;
+ }
+ </style>
+ <script type="text/javascript" charset="utf-8">
+ </script>
+</head>
+
+<body>
+
+<div id="mocha">
+</div>
+
+<div id="test">
+</div>
+
+</body>
+</html>
+
+
diff --git a/apps/system/test/unit/app_install_manager_test.js b/apps/system/test/unit/app_install_manager_test.js
new file mode 100644
index 0000000..7e235e5
--- /dev/null
+++ b/apps/system/test/unit/app_install_manager_test.js
@@ -0,0 +1,1036 @@
+'use strict';
+
+requireApp('system/test/unit/mock_app.js');
+requireApp('system/test/unit/mock_chrome_event.js');
+requireApp('system/test/unit/mock_statusbar.js');
+requireApp('system/test/unit/mock_manifest_helper.js');
+requireApp('system/test/unit/mock_app.js');
+requireApp('system/test/unit/mock_system_banner.js');
+requireApp('system/test/unit/mock_notification_screen.js');
+requireApp('system/test/unit/mock_applications.js');
+requireApp('system/test/unit/mock_utility_tray.js');
+requireApp('system/test/unit/mock_modal_dialog.js');
+requireApp('system/test/unit/mock_navigator_wake_lock.js');
+requireApp('system/test/unit/mocks_helper.js');
+
+requireApp('system/js/app_install_manager.js');
+
+var mocksForAppInstallManager = [
+ 'StatusBar',
+ 'SystemBanner',
+ 'NotificationScreen',
+ 'Applications',
+ 'UtilityTray',
+ 'ModalDialog',
+ 'ManifestHelper'
+];
+
+mocksForAppInstallManager.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+suite('system/AppInstallManager >', function() {
+ var realL10n;
+ var realDispatchResponse;
+ var realRequestWakeLock;
+
+ var fakeDialog, fakeNotif;
+ var fakeInstallCancelDialog, fakeDownloadCancelDialog;
+
+ var lastL10nParams = null;
+ var lastDispatchedResponse = null;
+
+ var mocksHelper;
+
+ suiteSetup(function() {
+ realL10n = navigator.mozL10n;
+ navigator.mozL10n = {
+ get: function get(key, params) {
+ lastL10nParams = params;
+ if (params) {
+ return key + JSON.stringify(params);
+ }
+
+ return key;
+ }
+ };
+
+ realDispatchResponse = AppInstallManager.dispatchResponse;
+ AppInstallManager.dispatchResponse = function fakeDispatch(id, type) {
+ lastDispatchedResponse = {
+ id: id,
+ type: type
+ };
+ };
+
+ realRequestWakeLock = navigator.requestWakeLock;
+ navigator.requestWakeLock = MockNavigatorWakeLock.requestWakeLock;
+
+ mocksHelper = new MocksHelper(mocksForAppInstallManager);
+ mocksHelper.suiteSetup();
+ });
+
+ suiteTeardown(function() {
+ AppInstallManager.dialog = null;
+ AppInstallManager.msg = null;
+ AppInstallManager.size = null;
+ AppInstallManager.authorName = null;
+ AppInstallManager.authorUrl = null;
+ AppInstallManager.installButton = null;
+ AppInstallManager.cancelButton = null;
+ AppInstallManager.installCallback = null;
+ AppInstallManager.cancelCallback = null;
+
+ navigator.mozL10n = realL10n;
+ AppInstallManager.dispatchResponse = realDispatchResponse;
+
+ navigator.requestWakeLock = realRequestWakeLock;
+ realRequestWakeLock = null;
+
+ mocksHelper.suiteTeardown();
+ });
+
+ setup(function() {
+ fakeDialog = document.createElement('form');
+ fakeDialog.id = 'app-install-dialog';
+ fakeDialog.innerHTML = [
+ '<section>',
+ '<h1 id="app-install-message"></h1>',
+ '<table>',
+ '<tr>',
+ '<th data-l10n-id="size">Size</th>',
+ '<td id="app-install-size"></td>',
+ '</tr>',
+ '<tr>',
+ '<th data-l10n-id="author">Author</th>',
+ '<td>',
+ '<span id="app-install-author-name"></span>',
+ '<br /><span id="app-install-author-url"></span>',
+ '</td>',
+ '</tr>',
+ '</table>',
+ '<menu>',
+ '<button id="app-install-cancel-button" type="reset"' +
+ ' data-l10n-id="cancel">Cancel</button>',
+ '<button id="app-install-install-button" type="submit"' +
+ ' data-l10n-id="install">Install</button>',
+ '</menu>',
+ '</section>'
+ ].join('');
+
+ fakeInstallCancelDialog = document.createElement('form');
+ fakeInstallCancelDialog.id = 'app-install-cancel-dialog';
+ fakeInstallCancelDialog.innerHTML = [
+ '<section>',
+ '<h1 data-l10n-id="cancel-install">Cancel Install</h1>',
+ '<p>',
+ '<small data-l10n-id="cancelling-will-not-refund">Cancelling ' +
+ 'will not refund a purchase. Refunds for paid content are ' +
+ 'provided by the original seller.</small>',
+ '<small data-l10n-id="apps-can-be-installed-later">Apps can be ' +
+ 'installed later from the original installation source.</small>',
+ '</p>',
+ '<p data-l10n-id="are-you-sure-you-want-to-cancel">' +
+ 'Are you sure you want to cancel this install?</p>',
+ '<menu>',
+ '<button id="app-install-confirm-cancel-button" type="reset" ' +
+ 'data-l10n-id="cancel-install">Cancel Install</button>',
+ '<button id="app-install-resume-button" type="submit" ' +
+ 'data-l10n-id="resume">Resume</button>',
+ '</menu>',
+ '</section>'
+ ].join('');
+
+ fakeDownloadCancelDialog = document.createElement('form');
+ fakeDownloadCancelDialog.id = 'app-download-cancel-dialog';
+ fakeDownloadCancelDialog.innerHTML = [
+ '<section>',
+ '<h1></h1>',
+ '<p data-l10n-id="app-download-can-be-restarted">' +
+ 'The download can be restarted later.</p>',
+ '<menu>',
+ '<button id="app-download-stop-button" class="danger confirm" ',
+ 'data-l10n-id="app-download-stop-button">Stop Download</button>',
+ '<button id="app-download-continue-button" class="cancel" ',
+ 'type="reset" data-l10n-id="continue">Continue</button>',
+ '</menu>',
+ '</section>'
+ ].join('');
+
+ fakeNotif = document.createElement('div');
+ fakeNotif.id = 'install-manager-notification-container';
+
+ document.body.appendChild(fakeDialog);
+ document.body.appendChild(fakeInstallCancelDialog);
+ document.body.appendChild(fakeDownloadCancelDialog);
+ document.body.appendChild(fakeNotif);
+
+ mocksHelper.setup();
+
+ AppInstallManager.init();
+ });
+
+ teardown(function() {
+ fakeDialog.parentNode.removeChild(fakeDialog);
+ fakeInstallCancelDialog.parentNode.removeChild(fakeInstallCancelDialog);
+ fakeDownloadCancelDialog.parentNode.removeChild(fakeDownloadCancelDialog);
+ fakeNotif.parentNode.removeChild(fakeNotif);
+ lastDispatchedResponse = null;
+ lastL10nParams = null;
+
+ mocksHelper.teardown();
+ MockNavigatorWakeLock.mTeardown();
+ });
+
+ suite('init >', function() {
+ test('should bind dom elements', function() {
+ assert.equal('app-install-dialog', AppInstallManager.dialog.id);
+ assert.equal('app-install-message', AppInstallManager.msg.id);
+ assert.equal('app-install-size', AppInstallManager.size.id);
+ assert.equal('app-install-author-name', AppInstallManager.authorName.id);
+ assert.equal('app-install-author-url', AppInstallManager.authorUrl.id);
+ assert.equal('app-install-install-button',
+ AppInstallManager.installButton.id);
+ assert.equal('app-install-cancel-button',
+ AppInstallManager.cancelButton.id);
+ assert.equal('app-install-cancel-dialog',
+ AppInstallManager.installCancelDialog.id);
+ assert.equal('app-install-confirm-cancel-button',
+ AppInstallManager.confirmCancelButton.id);
+ assert.equal('app-install-resume-button',
+ AppInstallManager.resumeButton.id);
+ });
+
+ test('should bind to the click event', function() {
+ assert.equal(AppInstallManager.handleInstall.name,
+ AppInstallManager.installButton.onclick.name);
+ assert.equal(AppInstallManager.showInstallCancelDialog.name,
+ AppInstallManager.cancelButton.onclick.name);
+ assert.equal(AppInstallManager.handleInstallCancel.name,
+ AppInstallManager.confirmCancelButton.onclick.name);
+ assert.equal(AppInstallManager.hideInstallCancelDialog.name,
+ AppInstallManager.resumeButton.onclick.name);
+ });
+ });
+
+ suite('events >', function() {
+ suite('webapps-ask-install >', function() {
+ setup(function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ manifest: {
+ name: 'Fake app',
+ size: 5245678,
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ });
+
+ test('should display the dialog', function() {
+ assert.equal('visible', AppInstallManager.dialog.className);
+ });
+
+ test('should fill the message with app name', function() {
+ assert.equal(AppInstallManager.msg.textContent,
+ 'install-app{"name":"Fake app"}');
+ });
+
+ test('should use the mini manifest if no manifest', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ updateManifest: {
+ name: 'Fake app',
+ size: 5245678,
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+
+ assert.equal(AppInstallManager.msg.textContent,
+ 'install-app{"name":"Fake app"}');
+ });
+
+ suite('developer infos >', function() {
+ test('should fill the developer infos', function() {
+ assert.equal('Fake dev', AppInstallManager.authorName.textContent);
+ assert.equal('http://fakesoftware.com',
+ AppInstallManager.authorUrl.textContent);
+ });
+
+ test('should tell if the developer is unknown', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ updateManifest: {
+ name: 'Fake app',
+ size: 5245678
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ assert.equal('unknown', AppInstallManager.authorName.textContent);
+ assert.equal('', AppInstallManager.authorUrl.textContent);
+ });
+
+ test('should handle empty developer object properly', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ updateManifest: {
+ name: 'Fake app',
+ size: 5245678,
+ developer: {}
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ assert.equal('unknown', AppInstallManager.authorName.textContent);
+ assert.equal('', AppInstallManager.authorUrl.textContent);
+ });
+
+ test('should tell if the developer name is unknown', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ updateManifest: {
+ name: 'Fake app',
+ size: 5245678,
+ developer: {
+ url: 'http://example.com'
+ }
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ assert.equal('unknown', AppInstallManager.authorName.textContent);
+ assert.equal('http://example.com',
+ AppInstallManager.authorUrl.textContent);
+ });
+
+ test('the developer url should default to blank', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ updateManifest: {
+ name: 'Fake app',
+ size: 5245678,
+ developer: {
+ name: 'Fake dev'
+ }
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ assert.equal('Fake dev', AppInstallManager.authorName.textContent);
+ assert.equal('', AppInstallManager.authorUrl.textContent);
+ });
+ });
+
+ suite('install size >', function() {
+ test('should display the package size', function() {
+ assert.equal('5.00 MB', AppInstallManager.size.textContent);
+ });
+
+ test('should tell if the size is unknown', function() {
+ var evt = new MockChromeEvent({
+ type: 'webapps-ask-install',
+ id: 42,
+ app: {
+ manifest: {
+ name: 'Fake app',
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ }
+ }
+ });
+
+ AppInstallManager.handleAppInstallPrompt(evt.detail);
+ assert.equal('unknown', AppInstallManager.size.textContent);
+ });
+ });
+
+ suite('callbacks >', function() {
+ suite('install >', function() {
+ var defaultPrevented = false;
+ setup(function() {
+ AppInstallManager.handleInstall({preventDefault: function() {
+ defaultPrevented = true;
+ }});
+ });
+
+ test('should dispatch a webapps-install-granted with the right id',
+ function() {
+ assert.equal(42, lastDispatchedResponse.id);
+ assert.equal('webapps-install-granted',
+ lastDispatchedResponse.type);
+ });
+
+ test('should prevent the default to avoid form submission',
+ function() {
+ assert.isTrue(defaultPrevented);
+ });
+
+ test('should hide the dialog', function() {
+ assert.equal('', AppInstallManager.dialog.className);
+ });
+
+ test('should remove the callback', function() {
+ assert.equal(null, AppInstallManager.installCallback);
+ });
+ });
+
+ suite('show cancel dialog >', function() {
+ setup(function() {
+ AppInstallManager.showInstallCancelDialog();
+ });
+
+ test('should show cancel dialog and hide dialog', function() {
+ assert.equal('visible',
+ AppInstallManager.installCancelDialog.className);
+ assert.equal('', AppInstallManager.dialog.className);
+ });
+ });
+
+ suite('hide cancel dialog >', function() {
+ setup(function() {
+ AppInstallManager.hideInstallCancelDialog();
+ });
+
+ test('should hide cancel dialog and show dialog', function() {
+ assert.equal('', AppInstallManager.installCancelDialog.className);
+ assert.equal('visible', AppInstallManager.dialog.className);
+ });
+ });
+
+ suite('confirm cancel >', function() {
+ setup(function() {
+ AppInstallManager.handleInstallCancel();
+ });
+
+ test('should dispatch a webapps-install-denied', function() {
+ assert.equal(42, lastDispatchedResponse.id);
+ assert.equal('webapps-install-denied', lastDispatchedResponse.type);
+ });
+
+ test('should hide the dialog', function() {
+ assert.equal('', AppInstallManager.installCancelDialog.className);
+ });
+
+ test('should remove the callback', function() {
+ assert.equal(null, AppInstallManager.installCancelCallback);
+ });
+ });
+ });
+ });
+ });
+
+ suite('duringInstall >', function() {
+ var mockApp, mockAppName;
+
+ function dispatchEvent() {
+ var e = new CustomEvent('applicationinstall', {
+ detail: { application: mockApp }
+ });
+ window.dispatchEvent(e);
+ }
+
+ suite('hosted app without cache >', function() {
+ setup(function() {
+ mockAppName = 'Fake hosted app';
+ mockApp = new MockApp({
+ manifest: {
+ name: mockAppName,
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ },
+ updateManifest: null,
+ installState: 'installed'
+ });
+ MockSystemBanner.mTeardown();
+ dispatchEvent();
+ });
+
+ test('should not show the icon', function() {
+ assert.isUndefined(MockStatusBar.wasMethodCalled['incSystemDownloads']);
+ });
+
+ test('should not add a notification', function() {
+ assert.equal(fakeNotif.childElementCount, 0);
+ });
+
+ test('should display a confirmation', function() {
+ assert.equal(MockSystemBanner.mMessage,
+ 'app-install-success{"appName":"' + mockAppName + '"}');
+ });
+
+ });
+
+ function beforeFirstProgressSuite() {
+ suite('before first progress >', function() {
+ test('should not show the icon', function() {
+ var method = 'incSystemDownloads';
+ assert.isUndefined(MockStatusBar.wasMethodCalled[method]);
+ });
+
+ test('should not add a notification', function() {
+ assert.equal(fakeNotif.childElementCount, 0);
+ });
+
+ suite('on downloadsuccess >', function() {
+ setup(function() {
+ // reseting these mocks as we want to test only one call
+ MockNotificationScreen.mTeardown();
+ MockStatusBar.mTeardown();
+
+ mockApp.mTriggerDownloadSuccess();
+ });
+
+ test('should not remove a notification', function() {
+ var method = 'decExternalNotifications';
+ assert.isUndefined(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('should not remove the download icon', function() {
+ var method = 'decSystemDownloads';
+ assert.isUndefined(MockStatusBar.wasMethodCalled[method]);
+ });
+
+ test('should display a confirmation', function() {
+ assert.equal(MockSystemBanner.mMessage,
+ 'app-install-success{"appName":"' + mockAppName + '"}');
+ });
+
+ });
+
+ suite('on downloaderror >', function() {
+ setup(function() {
+ // reseting these mocks as we want to test only one call
+ MockNotificationScreen.mTeardown();
+ MockStatusBar.mTeardown();
+
+ mockApp.mTriggerDownloadError();
+ });
+
+ test('should not remove a notification', function() {
+ var method = 'decExternalNotifications';
+ assert.isUndefined(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('should not remove the download icon', function() {
+ var method = 'decSystemDownloads';
+ assert.isUndefined(MockStatusBar.wasMethodCalled[method]);
+ });
+ });
+ });
+ }
+
+ function downloadErrorSuite(downloadEventsSuite) {
+ suite('on downloadError >', function() {
+ setup(function() {
+ // reseting these mocks as we only want to test the
+ // following call
+ MockStatusBar.mTeardown();
+ MockSystemBanner.mTeardown();
+ MockModalDialog.mTeardown();
+ });
+
+ function downloadErrorTests(errorName) {
+ test('should display an error', function() {
+ var expectedErrorMsg = knownErrors[errorName] +
+ '{"appName":"' + mockAppName + '"}';
+
+ assert.equal(MockSystemBanner.mMessage, expectedErrorMsg);
+ });
+
+ test('should not display the error dialog', function() {
+ assert.isFalse(MockModalDialog.alert.mWasCalled);
+ });
+
+ }
+
+ function specificDownloadErrorSuite(errorName) {
+ suite(errorName + ' >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadError(errorName);
+ });
+
+ downloadErrorTests(errorName);
+ });
+ }
+
+ var knownErrors = {
+ 'FALLBACK_ERROR': 'app-install-generic-error',
+ 'NETWORK_ERROR': 'app-install-download-failed',
+ 'DOWNLOAD_ERROR': 'app-install-download-failed',
+ 'MISSING_MANIFEST': 'app-install-install-failed',
+ 'INVALID_MANIFEST': 'app-install-install-failed',
+ 'INSTALL_FROM_DENIED': 'app-install-install-failed',
+ 'INVALID_SECURITY_LEVEL': 'app-install-install-failed',
+ 'INVALID_PACKAGE': 'app-install-install-failed',
+ 'APP_CACHE_DOWNLOAD_ERROR': 'app-install-download-failed'
+ };
+
+ Object.keys(knownErrors).forEach(specificDownloadErrorSuite);
+
+ suite('GENERIC_ERROR >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadError('GENERIC_ERROR');
+ });
+
+ test('should remove the notif', function() {
+ assert.equal(fakeNotif.childElementCount, 0);
+ });
+
+ test('should remove the icon', function() {
+ var method = 'decSystemDownloads';
+ assert.ok(MockStatusBar.wasMethodCalled[method]);
+ });
+
+ beforeFirstProgressSuite();
+ downloadEventsSuite(/*afterError*/ true);
+ });
+
+ });
+ }
+
+ suite('hosted app with cache >', function() {
+ setup(function() {
+ mockAppName = 'Fake hosted app with cache';
+ mockApp = new MockApp({
+ manifest: {
+ name: mockAppName,
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ },
+ updateManifest: null,
+ installState: 'pending'
+ });
+ MockSystemBanner.mTeardown();
+ dispatchEvent();
+ });
+
+ function downloadEventsSuite(afterError) {
+ var suiteName = 'on first progress';
+ if (afterError) {
+ suiteName += ' after error';
+ }
+ suiteName += ' >';
+
+ suite(suiteName, function() {
+ setup(function() {
+ // reseting these mocks as we want to test only the following
+ // calls
+ MockNotificationScreen.mTeardown();
+ MockStatusBar.mTeardown();
+
+ mockApp.mTriggerDownloadProgress(NaN);
+ });
+
+ test('should add a notification', function() {
+ var method = 'incExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 1);
+ assert.ok(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('notification should have a message', function() {
+ assert.equal(fakeNotif.querySelector('.message').textContent,
+ 'downloadingAppMessage{"appName":"Fake hosted app with cache"}');
+ assert.equal(fakeNotif.querySelector('progress').textContent,
+ 'downloadingAppProgressIndeterminate');
+ });
+
+ test('notification progress should be indeterminate', function() {
+ assert.equal(fakeNotif.querySelector('progress').position, -1);
+ });
+
+ test('should request wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isFalse(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+
+ suite('on downloadsuccess >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadSuccess();
+ });
+
+ test('should remove the notif', function() {
+ var method = 'decExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 0);
+ assert.ok(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('should release the wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isTrue(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+ });
+
+ test('on downloadsuccess > should remove only its progress handler',
+ function() {
+
+ var onprogressCalled = false;
+ mockApp.onprogress = function() {
+ onprogressCalled = true;
+ };
+ mockApp.mTriggerDownloadSuccess();
+ mockApp.mTriggerDownloadProgress(10);
+ assert.isTrue(onprogressCalled);
+ });
+
+ test('on downloadsuccess > should display a confirmation',
+ function() {
+ mockApp.mTriggerDownloadSuccess();
+ assert.equal(MockSystemBanner.mMessage,
+ 'app-install-success{"appName":"' + mockAppName + '"}');
+ });
+
+ suite('on indeterminate progress >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadProgress(NaN);
+ });
+
+ test('should keep the progress indeterminate', function() {
+ var progressNode = fakeNotif.querySelector('progress');
+ assert.equal(progressNode.position, -1);
+ assert.equal(progressNode.textContent,
+ 'downloadingAppProgressIndeterminate');
+ });
+ });
+
+ suite('on quantified progress >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadProgress(10);
+ });
+
+ test('should have a quantified progress', function() {
+ var progressNode = fakeNotif.querySelector('progress');
+ assert.equal(progressNode.position, -1);
+ assert.equal(progressNode.textContent,
+ 'downloadingAppProgressNoMax{"progress":"10.00 bytes"}');
+ });
+ });
+
+ if (!afterError) {
+ downloadErrorSuite(downloadEventsSuite);
+ }
+ });
+ }
+
+ beforeFirstProgressSuite();
+ downloadEventsSuite(/*afterError*/ false);
+ });
+
+ suite('packaged app >', function() {
+ setup(function() {
+ mockAppName = 'Fake packaged app';
+ mockApp = new MockApp({
+ manifest: null,
+ updateManifest: {
+ name: mockAppName,
+ size: 5245678,
+ developer: {
+ name: 'Fake dev',
+ url: 'http://fakesoftware.com'
+ }
+ },
+ installState: 'pending'
+ });
+
+ dispatchEvent();
+ });
+
+
+ function downloadEventsSuite(afterError) {
+ var suiteName = 'on first progress';
+ if (afterError) {
+ suiteName += ' after error';
+ }
+ suiteName += ' >';
+
+ suite(suiteName, function() {
+ var newprogress = 5;
+
+ setup(function() {
+ // resetting this mock because we want to test only the
+ // following call
+ MockNotificationScreen.mTeardown();
+ MockSystemBanner.mTeardown();
+ mockApp.mTriggerDownloadProgress(newprogress);
+ });
+
+ test('should add a notification', function() {
+ var method = 'incExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 1);
+ assert.ok(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('notification should have a message', function() {
+ var expectedText = 'downloadingAppMessage{"appName":"' +
+ mockAppName + '"}';
+ assert.equal(fakeNotif.querySelector('.message').textContent,
+ expectedText);
+ });
+
+ test('notification progress should have a max and a value',
+ function() {
+ assert.equal(fakeNotif.querySelector('progress').max,
+ mockApp.updateManifest.size);
+ assert.equal(fakeNotif.querySelector('progress').value,
+ newprogress);
+ });
+
+ test('notification progress should not be indeterminate',
+ function() {
+ assert.notEqual(fakeNotif.querySelector('progress').position, -1);
+ });
+
+ test('should request wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isFalse(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+
+ suite('on downloadsuccess >', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadSuccess();
+ });
+
+ test('should remove the notif', function() {
+ var method = 'decExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 0);
+ assert.ok(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('should release the wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isTrue(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+
+ });
+
+ test('on downloadsuccess > ' +
+ 'should not break if wifi unlock throws an exception',
+ function() {
+ MockNavigatorWakeLock.mThrowAtNextUnlock();
+ mockApp.mTriggerDownloadSuccess();
+ assert.ok(true);
+ });
+
+ test('on downloadsuccess > should display a confirmation',
+ function() {
+ mockApp.mTriggerDownloadSuccess();
+ assert.equal(MockSystemBanner.mMessage,
+ 'app-install-success{"appName":"' + mockAppName + '"}');
+ });
+
+ test('on indeterminate progress > ' +
+ 'should update the progress text content',
+ function() {
+ mockApp.mTriggerDownloadProgress(NaN);
+
+ var progressNode = fakeNotif.querySelector('progress');
+ assert.equal(progressNode.textContent,
+ 'downloadingAppProgressIndeterminate');
+ });
+
+ suite('on progress >', function() {
+ var size, ratio;
+ var newprogress = 10;
+
+ setup(function() {
+ size = mockApp.updateManifest.size;
+ ratio = newprogress / size;
+ mockApp.mTriggerDownloadProgress(newprogress);
+ });
+
+ test('should update the progress notification', function() {
+ var progressNode = fakeNotif.querySelector('progress');
+ assert.equal(progressNode.position, ratio);
+ assert.equal(progressNode.textContent,
+ 'downloadingAppProgress{"progress":"10.00 bytes",' +
+ '"max":"5.00 MB"}');
+ });
+ });
+
+ if (!afterError) {
+ downloadErrorSuite(downloadEventsSuite);
+ }
+ });
+ }
+
+ beforeFirstProgressSuite();
+ downloadEventsSuite(/*afterError*/ false);
+
+ suite('on INSUFFICIENT_STORAGE downloaderror >', function() {
+ test('should display an alert', function() {
+ mockApp.mTriggerDownloadError('INSUFFICIENT_STORAGE');
+ assert.isNull(MockSystemBanner.mMessage);
+ assert.isTrue(MockModalDialog.alert.mWasCalled);
+ var args = MockModalDialog.alert.mArgs;
+ assert.equal(args[0], 'not-enough-space');
+ assert.equal(args[1], 'not-enough-space-message');
+ assert.deepEqual(args[2], { title: 'ok' });
+ });
+
+ beforeFirstProgressSuite();
+ downloadEventsSuite(/*afterError*/ true);
+ });
+
+
+ });
+
+ suite('cancelling a download >', function() {
+ setup(function() {
+ mockApp = new MockApp({ installState: 'pending' });
+ MockApplications.mRegisterMockApp(mockApp);
+ dispatchEvent();
+ mockApp.mTriggerDownloadProgress(10);
+ });
+
+ test('tapping the notification should display the dialog', function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ assert.isTrue(fakeDownloadCancelDialog.classList.contains('visible'));
+ });
+
+ test('tapping the container should not display the dialog', function() {
+ fakeNotif.click();
+ assert.isFalse(fakeDownloadCancelDialog.classList.contains('visible'));
+ });
+
+ test('should set the title with the app name', function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ var title = fakeDownloadCancelDialog.querySelector('h1');
+ assert.equal(title.textContent, 'stopDownloading{"app":"Mock app"}');
+ });
+
+ test('should add the manifest url in data set', function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ assert.equal(fakeDownloadCancelDialog.dataset.manifest,
+ mockApp.manifestURL);
+ });
+
+ test('should hide the notification tray', function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ assert.isFalse(MockUtilityTray.mShown);
+ });
+
+ test('cancelling should hide the dialog only', function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ fakeDownloadCancelDialog.querySelector('.cancel').click();
+ assert.isFalse(fakeDownloadCancelDialog.classList.contains('visible'));
+ assert.isFalse(mockApp.mCancelCalled);
+ });
+
+ test('accepting should hide the dialog and call cancelDownload on app',
+ function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ fakeDownloadCancelDialog.querySelector('.confirm').click();
+ assert.isFalse(fakeDownloadCancelDialog.classList.contains('visible'));
+ assert.ok(mockApp.mCancelCalled);
+ });
+
+ test('accepting should hide the dialog but not call cancelDownload ' +
+ 'if app is uninstalled',
+ function() {
+ fakeNotif.querySelector('.fake-notification').click();
+ MockApplications.mUnregisterMockApp(mockApp);
+ fakeDownloadCancelDialog.querySelector('.confirm').click();
+ assert.isFalse(fakeDownloadCancelDialog.classList.contains('visible'));
+ assert.isFalse(mockApp.mCancelCalled);
+ });
+ });
+
+ });
+
+ suite('restarting after reboot >', function() {
+ var mockApp, installedMockApp;
+
+ setup(function() {
+ mockApp = new MockApp({
+ updateManifest: null,
+ installState: 'pending'
+ });
+
+ installedMockApp = new MockApp({
+ updateManifest: null,
+ installState: 'installed'
+ });
+
+ var e = new CustomEvent('applicationready', {
+ detail: { applications: {} }
+ });
+ e.detail.applications[mockApp.manifestURL] = mockApp;
+ e.detail.applications[installedMockApp.manifestURL] = installedMockApp;
+ window.dispatchEvent(e);
+
+ });
+
+ test('should add a notification for the pending app', function() {
+ mockApp.mTriggerDownloadProgress(50);
+
+ var method = 'incExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 1);
+ assert.ok(MockNotificationScreen.wasMethodCalled[method]);
+ });
+
+ test('should not add a notification for the installed app', function() {
+ installedMockApp.mTriggerDownloadProgress(50);
+
+ var method = 'incExternalNotifications';
+ assert.equal(fakeNotif.childElementCount, 0);
+ assert.isUndefined(MockNotificationScreen.wasMethodCalled[method]);
+ });
+ });
+
+ suite('humanizeSize >', function() {
+ test('should handle bytes size', function() {
+ assert.equal('42.00 bytes', AppInstallManager.humanizeSize(42));
+ });
+
+ test('should handle kilobytes size', function() {
+ assert.equal('1.00 kB', AppInstallManager.humanizeSize(1024));
+ });
+
+ test('should handle megabytes size', function() {
+ assert.equal('4.67 MB', AppInstallManager.humanizeSize(4901024));
+ });
+
+ test('should handle gigabytes size', function() {
+ assert.equal('3.73 GB', AppInstallManager.humanizeSize(4000901024));
+ });
+
+ test('should handle 0', function() {
+ assert.equal('0.00 bytes', AppInstallManager.humanizeSize(0));
+ });
+ });
+});
diff --git a/apps/system/test/unit/battery_manager_test.js b/apps/system/test/unit/battery_manager_test.js
new file mode 100644
index 0000000..ee85f86
--- /dev/null
+++ b/apps/system/test/unit/battery_manager_test.js
@@ -0,0 +1,204 @@
+'use strict';
+
+requireApp('system/test/unit/mock_navigator_battery.js');
+requireApp('system/test/unit/mock_settings_listener.js');
+requireApp('system/test/unit/mock_sleep_menu.js');
+requireApp('system/test/unit/mock_gesture_detector.js');
+requireApp('system/test/unit/mocks_helper.js');
+requireApp('system/js/battery_manager.js');
+
+var mocksForBatteryManager = [
+ 'SettingsListener',
+ 'SleepMenu',
+ 'GestureDetector'
+];
+
+mocksForBatteryManager.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+
+suite('battery manager >', function() {
+ var realBattery;
+ var screenNode, notifNode, overlayNode;
+ var mocksHelper;
+ var tinyTimeout = 10;
+
+ suiteSetup(function() {
+ mocksHelper = new MocksHelper(mocksForBatteryManager);
+ mocksHelper.suiteSetup();
+
+ realBattery = BatteryManager._battery;
+ BatteryManager._battery = MockNavigatorBattery;
+
+ // must be big enough, otherwise the BatteryManager timeout occurs
+ // before the different suites execute.
+ BatteryManager.TOASTER_TIMEOUT = tinyTimeout;
+ });
+
+ suiteTeardown(function() {
+ mocksHelper.suiteTeardown();
+
+ BatteryManager._battery = realBattery;
+ realBattery = null;
+ });
+
+ setup(function() {
+ mocksHelper.setup();
+
+ var batteryNotificationMarkup =
+ '<div id="system-overlay" data-z-index-level="system-overlay">' +
+ '<div id="battery">' +
+ '<span class="icon-battery"></span>' +
+ '<span class="battery-notification" '+
+ 'data-l10n-id="battery-almost-empty">Battery almost empty' +
+ '</span>' +
+ '</div>' +
+ '</div>';
+
+ screenNode = document.createElement('div');
+ screenNode.id = 'screen';
+ screenNode.innerHTML = batteryNotificationMarkup;
+ document.body.appendChild(screenNode);
+
+ overlayNode = document.getElementById('system-overlay');
+ notifNode = document.getElementById('battery');
+
+ MockNavigatorBattery.level = 1;
+ PowerSaveHandler.init();
+ BatteryManager.init();
+ });
+
+ teardown(function() {
+ mocksHelper.teardown();
+
+ screenNode.parentNode.removeChild(screenNode);
+ });
+
+ function sendScreenChange(val) {
+ var detail = { screenEnabled: val};
+ var evt = new CustomEvent('screenchange', { detail: detail });
+ window.dispatchEvent(evt);
+ }
+
+ function sendLevelChange(level) {
+ MockNavigatorBattery.level = level;
+
+ var evt = new CustomEvent('levelchange');
+ MockNavigatorBattery.mTriggerEvent(evt);
+ }
+
+ function sendChargingChange(val) {
+ MockNavigatorBattery.charging = val;
+
+ var evt = new CustomEvent('chargingchange');
+ MockNavigatorBattery.mTriggerEvent(evt);
+ }
+
+ suite('"level is near empty" notification >', function() {
+ function assertDisplayed() {
+ assert.ok(overlayNode.classList.contains('battery'));
+ }
+
+ function assertNotDisplayed() {
+ assert.isFalse(overlayNode.classList.contains('battery'));
+ }
+
+ teardown(function(done) {
+ // wait for the notification timeout
+ setTimeout(done, tinyTimeout * 2);
+ });
+
+ suite('init >', function() {
+ setup(function() {
+ MockNavigatorBattery.level = 0.02;
+ BatteryManager.init();
+ });
+
+ test('display notification', function() {
+ assertDisplayed();
+ });
+ });
+
+ suite('battery goes empty >', function() {
+ setup(function() {
+ sendLevelChange(0.05);
+ });
+
+ test('display notification', function() {
+ assertDisplayed();
+ });
+
+ test('do not display twice', function(done) {
+ setTimeout(function() {
+ sendLevelChange(0.02);
+
+ assertNotDisplayed();
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ suite('charging >', function() {
+ setup(function() {
+ sendChargingChange(true);
+ });
+
+ test('hide notification', function() {
+ assertNotDisplayed();
+ });
+
+ test('not charging > show notification', function() {
+ sendChargingChange(false);
+ assertDisplayed();
+ });
+
+ suite('goes up >', function() {
+ setup(function() {
+ sendLevelChange(0.2);
+ });
+
+ test('hide notification', function() {
+ assertNotDisplayed();
+ });
+
+ suite('not charging >', function() {
+ setup(function() {
+ sendChargingChange(false);
+ });
+
+ test('should not display', function() {
+ assertNotDisplayed();
+ });
+
+ test('goes empty again > display notification', function() {
+ sendLevelChange(0.02);
+
+ assertDisplayed();
+ });
+ });
+
+ });
+
+ });
+ });
+
+ suite('screen goes off > battery goes empty >', function() {
+ setup(function() {
+ sendScreenChange(false);
+ sendLevelChange(0.05);
+ });
+
+ test('no notification', function() {
+ assertNotDisplayed();
+ });
+
+ test('screen goes on > display notification', function() {
+ sendScreenChange(true);
+
+ assertDisplayed();
+ });
+ });
+ });
+});
diff --git a/apps/system/test/unit/date_picker_test.js b/apps/system/test/unit/date_picker_test.js
new file mode 100644
index 0000000..51d8867
--- /dev/null
+++ b/apps/system/test/unit/date_picker_test.js
@@ -0,0 +1,483 @@
+requireApp('system/js/value_selector/date_picker.js');
+
+suite('date picker', function() {
+ var subject;
+ var Calc;
+ var triggerEvent;
+
+ teardown(function() {
+ var el = document.getElementById('test');
+ el.parentNode.removeChild(el);
+ });
+
+ setup(function() {
+ var div = document.createElement('div');
+ div.id = 'test';
+ document.body.appendChild(div);
+ subject = new DatePicker(div);
+ Calc = DatePicker.Calc;
+ subject._position = new Date(2012, 1, 1);
+ });
+
+ suite('Calc', function() {
+ var mocked = {};
+
+ function mock(fn, value) {
+ if (!(fn in mocked)) {
+ mocked[fn] = subject[fn];
+ }
+ subject[fn] = function() {
+ return value;
+ };
+ }
+
+ teardown(function() {
+ var key;
+ for (key in mocked) {
+ if (mocked.hasOwnProperty(key)) {
+ subject[key] = mocked[key];
+ }
+ }
+ });
+
+ setup(function() {
+ subject = DatePicker.Calc;
+ });
+
+ suite('#isSameDate', function() {
+
+ test('same day', function() {
+ assert.isTrue(subject.isSameDate(
+ new Date(2012, 1, 1, 8),
+ new Date(2012, 1, 1, 23)
+ ));
+ });
+
+ test('same day different month', function() {
+ assert.isFalse(subject.isSameDate(
+ new Date(2012, 2, 1, 8),
+ new Date(2012, 1, 1, 8)
+ ));
+ });
+ });
+
+ suite('#isToday', function() {
+ test('when given is today', function() {
+ var result = subject.isToday(new Date());
+
+ assert.isTrue(result, 'should be true when given today');
+ });
+
+ test('when given is not today', function() {
+ var now = new Date();
+ now.setDate(now.getDate() - 1);
+ var result = subject.isToday(now);
+
+ assert.isFalse(result, 'should be false when given is not today');
+ });
+ });
+
+ suite('#isPast', function() {
+ test('when date is passed', function() {
+ var date = new Date();
+ date.setTime(Date.now() - 1000);
+ var result = subject.isPast(date);
+
+ assert.isTrue(result, 'should be true when given is in the past');
+ });
+
+ test('when given is in the future', function() {
+ var date = new Date();
+ date.setTime(Date.now() + 100);
+ var result = subject.isPast(date);
+
+ assert.isFalse(result,
+ 'should return false when date is in the future');
+ });
+
+ });
+
+ suite('#isFuture', function() {
+ test('when date is passed', function() {
+ var date = new Date();
+ date.setTime(Date.now() - 100);
+ var result = subject.isFuture(date);
+
+ assert.isFalse(result);
+ });
+
+ test('when given is in the future', function() {
+ var date = new Date(Date.now() + 100);
+ var result = subject.isFuture(date);
+
+ assert.isTrue(result);
+ });
+
+ });
+
+ suite('#dateFromId', function() {
+ var id;
+ var result;
+ var date = new Date(2012, 7, 3);
+
+ setup(function() {
+ id = subject.getDayId(date);
+ });
+
+ test('id to date', function() {
+ assert.deepEqual(
+ subject.dateFromId(id),
+ date
+ );
+ });
+
+ });
+
+ test('#getDayId', function() {
+ var result = subject.getDayId(
+ new Date(2012, 3, 7)
+ );
+
+ assert.equal(result, '2012-3-7');
+ });
+
+ suite('#relativeState', function() {
+
+ setup(function() {
+ mock('isToday', false);
+ });
+
+ test('when in the past', function() {
+ mock('isPast', true);
+ var state = subject.relativeState(
+ new Date(1991, 1, 1),
+ new Date(1991, 1, 1)
+ );
+
+ assert.equal(state, subject.PAST);
+ });
+
+ test('when in the future', function() {
+ mock('isPast', false);
+ var state = subject.relativeState(
+ new Date(1991, subject.today.getMonth(), 1),
+ new Date(1991, subject.today.getMonth(), 1)
+ );
+ assert.equal(state, subject.FUTURE);
+ });
+
+ test('when is in a different month in the past', function() {
+ mock('isPast', true);
+
+ var state = subject.relativeState(
+ new Date(1991, subject.today.getMonth() - 1, 1),
+ new Date(1991, subject.today.getMonth(), 1)
+ );
+
+ assert.include(state, subject.PAST);
+ assert.include(state, subject.OTHER_MONTH);
+ });
+
+ test('when is in a different month in the future', function() {
+ mock('isPast', false);
+
+ var state = subject.relativeState(
+ new Date(1991, subject.today.getMonth() + 1, 1),
+ new Date(1991, subject.today.getMonth(), 1)
+ );
+
+ assert.include(state, subject.FUTURE);
+ assert.include(state, subject.OTHER_MONTH);
+ });
+
+
+ test('when is today', function() {
+ mock('isToday', true);
+ var state = subject.relativeState(new Date(1991, 1, 1));
+
+ assert.equal(state, subject.PRESENT);
+ });
+
+ });
+
+ });
+
+ suite('#_daysIn', function() {
+ test('leap year', function() {
+ var result = subject._daysInMonth(2012, 1);
+ assert.equal(result, 29);
+ });
+
+ test('normal', function() {
+ var result = subject._daysInMonth(2012, 0);
+ assert.equal(result, 31);
+ });
+ });
+
+ suite('#_renderDay', function() {
+
+ test('simple', function() {
+ var date = new Date(2012, 1, 27);
+ var result = subject._renderDay(date).firstChild;
+ var html = result.outerHTML;
+
+ assert.ok(html);
+ assert.equal(result.dataset.date, Calc.getDayId(date));
+ assert.include(html, '27');
+ });
+
+ test('today', function() {
+ var date = new Date();
+ var result = subject._renderDay(date);
+ assert.ok(result.classList.contains('present'));
+ });
+
+ test('past', function() {
+ var date = new Date(2009, 1, 1);
+ var result = subject._renderDay(date);
+ assert.ok(result.classList.contains('past'));
+ });
+
+ test('future', function() {
+ var date = new Date();
+ date.setDate(date.getDate() + 2);
+
+ var result = subject._renderDay(date);
+ assert.ok(result.classList.contains('future'));
+ });
+ });
+
+ suite('#_renderWeek', function() {
+ var days = [
+ new Date(2012, 0, 29),
+ new Date(2012, 0, 30),
+ new Date(2012, 0, 31),
+ new Date(2012, 1, 1),
+ new Date(2012, 1, 2),
+ new Date(2012, 1, 3),
+ new Date(2012, 1, 4)
+ ];
+
+ var result;
+
+ setup(function() {
+ result = subject._renderWeek(days);
+ });
+
+ test('container', function() {
+ assert.equal(result.tagName.toLowerCase(), 'ol');
+ assert.ok(result.outerHTML);
+ });
+
+ days.forEach(function(day) {
+ test('week day ' + day, function() {
+ var expected = subject._renderDay(day);
+ assert.include(result.outerHTML, expected.outerHTML);
+ });
+ });
+
+ });
+
+ suite('#_renderMonth', function() {
+
+ function weekHtml(start, end) {
+ var range = Calc.daysBetween(start, end);
+ return subject._renderWeek(range).outerHTML;
+ }
+
+ test('Feb 2012', function() {
+ var month = 1;
+ var year = 2012;
+
+ var result = subject._renderMonth(year, month);
+ var html = result.outerHTML;
+
+ assert.ok(html, 'has contents');
+
+ assert.include(
+ html,
+ weekHtml(new Date(2012, 0, 29), new Date(2012, 1, 4)),
+ 'has first week'
+ );
+
+ assert.include(
+ html,
+ weekHtml(new Date(2012, 1, 5), new Date(2012, 1, 11)),
+ 'has second week'
+ );
+
+ assert.include(
+ html,
+ weekHtml(new Date(2012, 1, 12), new Date(2012, 1, 18)),
+ 'has third week'
+ );
+
+ assert.include(
+ html,
+ weekHtml(new Date(2012, 1, 19), new Date(2012, 1, 25)),
+ 'has fourth week'
+ );
+
+
+ assert.include(
+ html,
+ weekHtml(new Date(2012, 1, 26), new Date(2012, 2, 3)),
+ 'has fifth week'
+ );
+ });
+ });
+
+ suite('prev/next', function() {
+ var calledWith;
+
+ setup(function() {
+ subject.display(2012, 0, 1);
+ });
+
+ test('#next', function() {
+ subject.next();
+ assert.equal(subject.year, 2012);
+ assert.equal(subject.month, 1);
+ });
+
+ test('#previous', function() {
+ subject.previous();
+ assert.equal(subject.year, 2011);
+ assert.equal(subject.month, 11);
+ });
+
+ });
+
+ suite('prev/next with last day of a month', function() {
+ var calledWith;
+
+ setup(function() {
+ // init as 2012/3/31
+ subject.display(2012, 2, 31);
+ });
+
+ test('#next', function() {
+ subject.next();
+
+ // should be 2012/4/30
+ assert.equal(subject.year, 2012);
+ assert.equal(subject.month, 3);
+ assert.equal(subject.date, 30);
+ });
+
+ test('#previous', function() {
+ subject.previous();
+
+ // should be 2012/2/29
+ assert.equal(subject.year, 2012);
+ assert.equal(subject.month, 1);
+ assert.equal(subject.date, 29);
+ });
+
+ });
+
+ suite('#display', function() {
+ var year = 2012;
+ var month = 11;
+ var date = 1;
+ var calledWith;
+
+ setup(function() {
+ calledWith = null;
+ subject.onmonthchange = function() {
+ calledWith = arguments;
+ };
+
+ subject.display(year, month, date);
+ });
+
+ test('initial render', function() {
+ assert.deepEqual(
+ calledWith[0],
+ new Date(year, month),
+ 'should fire onmonthchange'
+ );
+
+ assert.equal(subject.year, 2012);
+ assert.equal(subject.month, 11);
+ assert.equal(subject.date, 1);
+ assert.ok(subject.monthDisplay);
+ });
+
+ test('second rendering', function() {
+ subject.display(2011, 2, 1);
+
+ assert.deepEqual(
+ calledWith[0],
+ new Date(2011, 2),
+ 'should fire onmonthchange again'
+ );
+
+ assert.equal(subject.year, 2011);
+ assert.equal(subject.month, 2);
+ assert.equal(subject.date, 1);
+ assert.ok(subject.monthDisplay);
+ });
+ });
+
+ suite('setters', function() {
+ test('#value', function() {
+ var calledWith;
+ var date;
+
+ subject.onvaluechange = function() {
+ calledWith = arguments;
+ }
+
+ subject.value = date = new Date(2012, 1, 1);
+
+ assert.deepEqual(subject.value, date);
+ assert.deepEqual(calledWith, [date, null]);
+ });
+ });
+
+ suite('dom events', function() {
+ function triggerEvent(element, eventName) {
+ var event = document.createEvent('HTMLEvents');
+ event.initEvent(eventName, true, true);
+ element.dispatchEvent(event);
+ }
+
+ setup(function() {
+ subject.display(2012, 1, 1);
+ });
+
+ test('select', function() {
+ var calledWith;
+ subject.onvaluechange = function() {
+ calledWith = arguments;
+ }
+
+ var target = subject.element.querySelector('[data-date="2012-1-1"]');
+ triggerEvent(target, 'click');
+
+ assert.ok(target.classList.contains('selected'), 'adds selected class');
+
+ assert.deepEqual(
+ subject.value,
+ new Date(2012, 1, 1),
+ 'changes value'
+ );
+
+ assert.deepEqual(
+ calledWith,
+ [new Date(2012, 1, 1), new Date(2012, 1, 1)],
+ 'calls onvaluechange'
+ );
+
+ triggerEvent(
+ subject.element.querySelector('[data-date="2012-1-2"]'),
+ 'click'
+ );
+
+ assert.ok(!target.classList.contains('selected'), 'clears past selected');
+ });
+ });
+
+});
+
diff --git a/apps/system/test/unit/identity_test.js b/apps/system/test/unit/identity_test.js
new file mode 100644
index 0000000..150944c
--- /dev/null
+++ b/apps/system/test/unit/identity_test.js
@@ -0,0 +1,92 @@
+'use strict';
+
+requireApp('system/js/identity.js');
+requireApp('system/test/unit/mock_chrome_event.js');
+requireApp('system/test/unit/mock_trusted_ui_manager.js');
+requireApp('system/test/unit/mock_l10n.js');
+
+// ensure its defined as a global so mocha will not complain about us
+// leaking new global variables during the test
+if (!window.TrustedUIManager) {
+ window.TrustedUIManager = true;
+}
+
+suite('identity', function() {
+ var subject;
+ var realL10n;
+ var realTrustedUIManager;
+ var realDispatchEvent;
+
+ var lastDispatchedEvent = null;
+
+ suiteSetup(function() {
+ subject = Identity;
+ realTrustedUIManager = window.TrustedUIManager;
+ window.TrustedUIManager = MockTrustedUIManager;
+
+ realL10n = navigator.mozL10n;
+ navigator.mozL10n = MockL10n;
+
+ realDispatchEvent = subject._dispatchEvent;
+ subject._dispatchEvent = function (obj) {
+ lastDispatchedEvent = obj;
+ };
+ });
+
+ suiteTeardown(function() {
+ window.TrustedUIManager = realTrustedUIManager;
+ subject._dispatchEvent = realDispatchEvent;
+
+ navigator.mozL10n = realL10n;
+ });
+
+ setup(function() {});
+
+ teardown(function() {
+ MockTrustedUIManager.mTeardown();
+ });
+
+ suite('open popup', function() {
+ setup(function() {
+ var event = new MockChromeEvent({
+ type: 'open-id-dialog',
+ id: 'test-open-event-id',
+ showUI: true
+ });
+ subject.handleEvent(event);
+ });
+
+ test('popup parameters', function() {
+ assert.equal(MockTrustedUIManager.mOpened, true);
+ assert.equal(MockTrustedUIManager.mName, 'persona-signin');
+ assert.equal(MockTrustedUIManager.mChromeEventId, 'test-open-event-id');
+ });
+
+ test('frame event listener', function() {
+ var frame = MockTrustedUIManager.mFrame;
+ var event = document.createEvent('CustomEvent');
+ event.initCustomEvent('mozbrowserloadstart', true, true, {target: frame});
+ frame.dispatchEvent(event);
+
+ assert.equal(frame, lastDispatchedEvent.frame);
+ assert.equal('test-open-event-id', lastDispatchedEvent.id);
+ });
+ });
+
+ suite('close popup', function() {
+ setup(function() {
+ var event = new MockChromeEvent({
+ type: 'received-id-assertion',
+ id: 'test-close-event-id',
+ showUI: true
+ });
+ subject.handleEvent(event);
+ });
+
+ test('close', function() {
+ assert.equal(false, MockTrustedUIManager.mOpened);
+ assert.equal('test-close-event-id', lastDispatchedEvent.id);
+ });
+ });
+});
+
diff --git a/apps/system/test/unit/lockscreen_test.js b/apps/system/test/unit/lockscreen_test.js
new file mode 100644
index 0000000..7258670
--- /dev/null
+++ b/apps/system/test/unit/lockscreen_test.js
@@ -0,0 +1,122 @@
+'use strict';
+
+requireApp('system/test/unit/mock_settings_listener.js');
+requireApp('system/test/unit/mocks_helper.js');
+requireApp('system/test/unit/mock_l10n.js');
+
+requireApp('system/js/lockscreen.js');
+
+var mocksForStatusBar = ['SettingsListener'];
+
+mocksForStatusBar.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+suite('lockscreen', function() {
+ var mocksHelper;
+
+ var realSettingsListener, realMozL10n;
+
+ var fakeLockscreenPanel;
+
+ var red_png, green_png;
+
+ suiteSetup(function() {
+ mocksHelper = new MocksHelper(mocksForStatusBar);
+ mocksHelper.suiteSetup();
+ realMozL10n = navigator.mozL10n;
+ navigator.mozL10n = MockL10n;
+
+ red_png =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAHgAQMAAADuQiYHAAAAA1BMVEX/AAAZ4gk3AAAAKUlEQVR4Xu3AMQEAAADCIPuntsROWAQAAAAAAAAAAAAAAAAAAAAAAADgTOAAAZXle7kAAAAASUVORK5CYII=';
+ green_png =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAHgAQMAAAAlmcL5AAAAA1BMVEUAgACc+aWRAAAAKUlEQVR4Xu3BMQEAAADCoPVPbQ0PoAAAAAAAAAAAAAAAAAAAAAAAAL4MSSAAAZTTyRkAAAAASUVORK5CYII=';
+ });
+
+ suiteTeardown(function() {
+ mocksHelper.suiteTeardown();
+ navigator.mozL10n = realMozL10n;
+ });
+
+ setup(function() {
+ fakeLockscreenPanel = document.createElement('div');
+ fakeLockscreenPanel.classList.add('lockscreen-panel');
+ fakeLockscreenPanel.setAttribute('data-wallpaper', '');
+ document.body.appendChild(fakeLockscreenPanel);
+
+ mocksHelper.setup();
+ });
+
+ teardown(function() {
+ fakeLockscreenPanel.parentNode.removeChild(fakeLockscreenPanel);
+ mocksHelper.teardown();
+ });
+
+ test('wallpaper has vignette effect', function(done) {
+ LockScreen.updateBackground(red_png);
+
+ (function checkCanvas() {
+ var canvas = fakeLockscreenPanel.getElementsByTagName('canvas')[0];
+ if (!canvas) {
+ setTimeout(checkCanvas, 10);
+ return;
+ }
+ var ctx = canvas.getContext('2d');
+ var top_pixel = ctx.getImageData(0, 0, 1, 1).data;
+
+ assert.equal(top_pixel[0], 77);
+ assert.equal(top_pixel[1], 0);
+ assert.equal(top_pixel[2], 0);
+
+ var center_width = Math.floor(canvas.width / 2);
+ var center_height = Math.floor(canvas.height / 2);
+ var center_pixel = ctx.getImageData(center_width, center_height,
+ 1 , 1).data;
+ assert.equal(center_pixel[0], 251);
+ assert.equal(center_pixel[1], 0);
+ assert.equal(center_pixel[2], 0);
+
+ done();
+ })();
+
+ });
+
+ test('multiple wallpaper updates only keep one canvas', function(done) {
+ function waitFirstUpdate(callback) {
+ var first_canvas = fakeLockscreenPanel.getElementsByTagName('canvas')[0];
+ if (!first_canvas) {
+ setTimeout(waitFirstUpdate, 10, callback);
+ return;
+ }
+
+ setTimeout(callback, 10);
+ }
+
+ function waitSecondUpdate(callback) {
+ var second_canvas = fakeLockscreenPanel.getElementsByTagName('canvas')[0];
+
+ var ctx = second_canvas.getContext('2d');
+ var top_pixel = ctx.getImageData(0, 0, 1, 1).data;
+ // Canvas is not green yet
+ if (top_pixel[1] == 0) {
+ setTimeout(waitSecondUpdate, 10, callback);
+ return;
+ }
+
+ setTimeout(callback, 10);
+ }
+
+ LockScreen.updateBackground(red_png);
+ setTimeout(waitFirstUpdate, 10, function then() {
+ LockScreen.updateBackground(green_png);
+
+ setTimeout(waitSecondUpdate, 10, function then2() {
+ assert.equal(fakeLockscreenPanel.getElementsByTagName('canvas').length, 1);
+ done();
+ });
+ });
+
+ });
+});
diff --git a/apps/system/test/unit/mobile_operator_test.js b/apps/system/test/unit/mobile_operator_test.js
new file mode 100644
index 0000000..6204217
--- /dev/null
+++ b/apps/system/test/unit/mobile_operator_test.js
@@ -0,0 +1,96 @@
+/* This should live in the shared directory */
+
+'use strict';
+
+requireApp('system/shared/js/mobile_operator.js');
+
+suite('shared/MobileOperator', function() {
+ var MockMobileConnection;
+ var BRAZIL_MCC = 724;
+
+
+ setup(function() {
+ MockMobileConnection = {
+ voice: {
+ network: {
+ shortName: 'Fake short',
+ longName: 'Fake long',
+ mnc: '6'
+ },
+ cell: { gsmLocationAreaCode: 71 }
+ },
+ iccInfo: { spn: 'Fake SPN' }
+ };
+ });
+
+ suite('Worldwide connection', function() {
+ test('Connection with short name', function() {
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ test('Connection with long name', function() {
+ MockMobileConnection.voice.network.shortName = '';
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake long');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ test('Connection with SPN display', function() {
+ MockMobileConnection.iccInfo.isDisplaySpnRequired = true;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake SPN');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ test('Connection with SPN display and network display', function() {
+ MockMobileConnection.iccInfo.isDisplaySpnRequired = true;
+ MockMobileConnection.iccInfo.isDisplayNetworkNameRequired = true;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short Fake SPN');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ test('Connection with roaming', function() {
+ MockMobileConnection.voice.roaming = true;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ test('Connection with roaming and SPN display', function() {
+ MockMobileConnection.voice.roaming = true;
+ MockMobileConnection.iccInfo.isDisplaySpnRequired = true;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.isUndefined(infos.carrier);
+ assert.isUndefined(infos.region);
+ });
+ });
+ suite('Brazilian connection', function() {
+ test('Connection ', function() {
+ MockMobileConnection.voice.network.mcc = BRAZIL_MCC;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.equal(infos.carrier, 'VIVO');
+ assert.equal(infos.region, 'BA 71');
+ });
+ test('Connection with unknown mnc', function() {
+ MockMobileConnection.voice.network.mcc = BRAZIL_MCC;
+ MockMobileConnection.voice.network.mnc = 42;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.equal(infos.carrier, '72442');
+ assert.equal(infos.region, 'BA 71');
+ });
+ test('Connection with unknown gsmLocationAreaCode', function() {
+ MockMobileConnection.voice.network.mcc = BRAZIL_MCC;
+ MockMobileConnection.voice.cell.gsmLocationAreaCode = 2;
+ var infos = MobileOperator.userFacingInfo(MockMobileConnection);
+ assert.equal(infos.operator, 'Fake short');
+ assert.equal(infos.carrier, 'VIVO');
+ assert.equal(infos.region, '');
+ });
+ });
+});
diff --git a/apps/system/test/unit/mock_app.js b/apps/system/test/unit/mock_app.js
new file mode 100644
index 0000000..2d34051
--- /dev/null
+++ b/apps/system/test/unit/mock_app.js
@@ -0,0 +1,93 @@
+'use strict';
+
+var idGen = 0;
+
+function MockApp(opts) {
+ /* default values */
+ this.origin = 'https://testapp.gaiamobile.org';
+ this.manifestURL = 'https://testapp.gaiamobile.org/manifest' +
+ idGen + '.webapp';
+ this.manifest = {
+ name: 'Mock app'
+ };
+
+ this.removable = true;
+ this.installState = 'installed';
+ this.downloadAvailable = false;
+ this.downloadError = null;
+ this.downloadSize = null;
+
+ this.mId = idGen++;
+ this.mDownloadCalled = false;
+ this.mCancelCalled = false;
+
+ /* overwrite default values with whatever comes in "opts" from the test */
+ if (opts) {
+ for (var key in opts) {
+ this[key] = opts[key];
+ }
+ }
+}
+
+MockApp.prototype.download = function() {
+ this.mDownloadCalled = true;
+};
+
+MockApp.prototype.cancelDownload = function() {
+ this.mCancelCalled = true;
+};
+
+MockApp.prototype.mTriggerDownloadAvailable = function(size) {
+ this.downloadAvailable = true;
+ this.downloadSize = size;
+ if (this.ondownloadavailable) {
+ this.ondownloadavailable({
+ application: this
+ });
+ }
+};
+
+MockApp.prototype.mTriggerDownloadSuccess = function() {
+ this.downloadAvailable = false;
+ this.downloadSize = null;
+ if (this.ondownloadsuccess) {
+ this.ondownloadsuccess({
+ application: this
+ });
+ }
+};
+
+MockApp.prototype.mTriggerDownloadError = function(error) {
+ this.downloadAvailable = true;
+ this.downloadSize = null;
+
+ this.downloadError = {
+ name: error || 'UNKNOWN_ERROR'
+ };
+
+ if (this.ondownloaderror) {
+ this.ondownloaderror({
+ application: this
+ });
+ }
+};
+
+MockApp.prototype.mTriggerDownloadProgress = function(progress) {
+ this.progress = progress;
+
+ if (this.onprogress) {
+ this.onprogress({
+ application: this
+ });
+ }
+};
+
+MockApp.prototype.mTriggerDownloadApplied = function() {
+ this.downloadAvailable = false;
+ this.downloadSize = null;
+ if (this.ondownloadapplied) {
+ this.ondownloadapplied({
+ application: this
+ });
+ }
+};
diff --git a/apps/system/test/unit/mock_applications.js b/apps/system/test/unit/mock_applications.js
new file mode 100644
index 0000000..8b3e765
--- /dev/null
+++ b/apps/system/test/unit/mock_applications.js
@@ -0,0 +1,26 @@
+var MockApplications = (function() {
+ var mockApps = {};
+
+ function getByManifestURL(url) {
+ return mockApps[url];
+ }
+
+ function mRegisterMockApp(mockApp) {
+ mockApps[mockApp.manifestURL] = mockApp;
+ }
+
+ function mUnregisterMockApp(mockApp) {
+ mockApps[mockApp.manifestURL] = null;
+ }
+
+ function mTeardown() {
+ mockApps = {};
+ }
+
+ return {
+ getByManifestURL: getByManifestURL,
+ mRegisterMockApp: mRegisterMockApp,
+ mUnregisterMockApp: mUnregisterMockApp,
+ mTeardown: mTeardown
+ };
+})();
diff --git a/apps/system/test/unit/mock_apps_mgmt.js b/apps/system/test/unit/mock_apps_mgmt.js
new file mode 100644
index 0000000..948ef02
--- /dev/null
+++ b/apps/system/test/unit/mock_apps_mgmt.js
@@ -0,0 +1,64 @@
+var MockAppsMgmt = {
+ getAll: function mam_getAll() {
+ var request = {};
+
+ setTimeout((function nextTick() {
+ if (request.onsuccess) {
+ var evt = {
+ target: {
+ result: this.mApps
+ }
+ };
+ request.onsuccess(evt);
+ if (this.mNext) {
+ this.mNext();
+ }
+ }
+ }).bind(this));
+
+ return request;
+ },
+
+ applyDownload: function mam_applyDownload(app) {
+ this.mLastAppApplied = app;
+ },
+
+ mApps: [],
+ mLastAppApplied: null,
+ mNext: null,
+ mTeardown: function mam_mTeardown() {
+ this.mLastAppApplied = null;
+ this.mApps = [];
+ this.mNext = null;
+ },
+
+ mTriggerOninstall: function mam_mTriggerOninstall(app) {
+ if (this.oninstall) {
+ var evt = {
+ application: app
+ };
+ this.oninstall(evt);
+ }
+
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent('applicationinstall',
+ true, false,
+ { application: app });
+ window.dispatchEvent(evt);
+ },
+
+ mTriggerOnuninstall: function mam_mTriggerOnuninstall(app) {
+ if (this.onuninstall) {
+ var evt = {
+ application: app
+ };
+ this.onuninstall(evt);
+ }
+
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent('applicationuninstall',
+ true, false,
+ { application: app });
+ window.dispatchEvent(evt);
+ }
+};
diff --git a/apps/system/test/unit/mock_asyncStorage.js b/apps/system/test/unit/mock_asyncStorage.js
new file mode 100644
index 0000000..a657e8b
--- /dev/null
+++ b/apps/system/test/unit/mock_asyncStorage.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var MockasyncStorage = {
+ mItems: {},
+
+ setItem: function(key, value, callback) {
+ this.mItems[key] = value;
+ if (typeof callback === 'function') {
+ callback();
+ }
+ },
+
+ getItem: function(key, callback) {
+ var value = this.mItems[key];
+ // use '|| null' will turn a 'false' to null
+ if (value === undefined)
+ value = null;
+ if (typeof callback === 'function') {
+ callback(value);
+ }
+ },
+
+ removeItem: function(key, callback) {
+ if (key in this.mItems) {
+ delete this.mItems[key];
+ }
+
+ if (typeof callback === 'function') {
+ callback();
+ }
+ },
+
+ mTeardown: function() {
+ this.mItems = {};
+ }
+};
diff --git a/apps/system/test/unit/mock_chrome_event.js b/apps/system/test/unit/mock_chrome_event.js
new file mode 100644
index 0000000..40555f9
--- /dev/null
+++ b/apps/system/test/unit/mock_chrome_event.js
@@ -0,0 +1,4 @@
+function MockChromeEvent(detail) {
+ this.type = 'mozChromeEvent';
+ this.detail = detail;
+}
diff --git a/apps/system/test/unit/mock_custom_dialog.js b/apps/system/test/unit/mock_custom_dialog.js
new file mode 100644
index 0000000..ed4b927
--- /dev/null
+++ b/apps/system/test/unit/mock_custom_dialog.js
@@ -0,0 +1,26 @@
+var MockCustomDialog = {
+ show: function(title, msg, cancel, confirm) {
+ this.mShown = true;
+ this.mShowedTitle = title;
+ this.mShowedMsg = msg;
+ this.mShowedCancel = cancel;
+ this.mShowedConfirm = confirm;
+ },
+
+ hide: function() {
+ this.mShown = false;
+ },
+
+ mShown: false,
+ mShowedTitle: null,
+ mShowedMsg: null,
+ mShowedCancel: null,
+ mShowedConfirm: null,
+ mTeardown: function teardown() {
+ this.mShown = false;
+ this.mShowedTitle = null;
+ this.mShowedMsg = null;
+ this.mShowedCancel = null;
+ this.mShowedConfirm = null;
+ }
+};
diff --git a/apps/system/test/unit/mock_gesture_detector.js b/apps/system/test/unit/mock_gesture_detector.js
new file mode 100644
index 0000000..b66e51c
--- /dev/null
+++ b/apps/system/test/unit/mock_gesture_detector.js
@@ -0,0 +1,9 @@
+'use strict';
+
+var MockGestureDetector = function() {};
+
+MockGestureDetector.prototype = {
+ startDetecting: function() {},
+ stopDetecting: function() {}
+};
+
diff --git a/apps/system/test/unit/mock_l10n.js b/apps/system/test/unit/mock_l10n.js
new file mode 100644
index 0000000..35b01d8
--- /dev/null
+++ b/apps/system/test/unit/mock_l10n.js
@@ -0,0 +1,17 @@
+'use strict';
+
+var MockL10n = {
+ get: function get(key, params) {
+ if (params) {
+ return key + JSON.stringify(params);
+ }
+ return key;
+ },
+ DateTimeFormat: function() {}
+};
+
+MockL10n.DateTimeFormat.prototype = {
+ localeFormat: function mockLocaleFormat(time, strFormat) {
+ return '' + time;
+ }
+};
diff --git a/apps/system/test/unit/mock_manifest_helper.js b/apps/system/test/unit/mock_manifest_helper.js
new file mode 100644
index 0000000..34f9a13
--- /dev/null
+++ b/apps/system/test/unit/mock_manifest_helper.js
@@ -0,0 +1,5 @@
+MockManifestHelper = function(manifest) {
+ for (var prop in manifest) {
+ this[prop] = manifest[prop];
+ }
+};
diff --git a/apps/system/test/unit/mock_mobile_operator.js b/apps/system/test/unit/mock_mobile_operator.js
new file mode 100644
index 0000000..430a613
--- /dev/null
+++ b/apps/system/test/unit/mock_mobile_operator.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var MockMobileOperator = {
+ userFacingInfo: function mmo_userFacingInfo(mobileConnection) {
+ return {
+ 'operator': this.mOperator,
+ 'carrier': this.mCarrier,
+ 'region': this.mRegion
+ }
+ },
+
+ mOperator: '',
+ mCarrier: '',
+ mRegion: ''
+};
diff --git a/apps/system/test/unit/mock_modal_dialog.js b/apps/system/test/unit/mock_modal_dialog.js
new file mode 100644
index 0000000..beb452e
--- /dev/null
+++ b/apps/system/test/unit/mock_modal_dialog.js
@@ -0,0 +1,34 @@
+var MockModalDialog = {
+
+ mMethods: [
+ 'alert'
+ ],
+
+ mPopulate: function mmd_mPopulate() {
+ this.mMethods.forEach(function(methodName) {
+ this[methodName] = function mmd_method() {
+ this.mMethodCalled(methodName, Array.slice(arguments));
+ };
+ }, this);
+ },
+
+ init: function mmd_init() {
+ this.mMethods.forEach(function(methodName) {
+ this[methodName].mWasCalled = false;
+ this[methodName].mArgs = null;
+ }, this);
+ },
+
+ mMethodCalled: function mmd_mMethodCalled(name, args) {
+ this[name].mWasCalled = true;
+ this[name].mArgs = args;
+ },
+
+ mTeardown: function mmd_mTeardown() {
+ this.init();
+ }
+};
+
+MockModalDialog.mPopulate();
+
+
diff --git a/apps/system/test/unit/mock_navigator_battery.js b/apps/system/test/unit/mock_navigator_battery.js
new file mode 100644
index 0000000..5145de1
--- /dev/null
+++ b/apps/system/test/unit/mock_navigator_battery.js
@@ -0,0 +1,55 @@
+'use strict';
+
+(function() {
+
+ var props = ['level', 'charging'];
+
+ var listeners;
+
+ function mnb_init() {
+ props.forEach(function(prop) {
+ Mock[prop] = null;
+ });
+
+ listeners = {};
+ }
+
+ function mnb_addEventListener(evtName, func) {
+ listeners[evtName] = listeners[evtName] || [];
+ listeners[evtName].push(func);
+ }
+
+ function mnb_removeEventListener(evtName, func) {
+ if (listeners[evtName]) {
+ var listenerArray = listeners[evtName];
+ var index = listenerArray.indexOf(func);
+ if (index !== -1) {
+ listenerArray.splice(index, 1);
+ }
+ }
+ }
+
+ function mnb_mTriggerEvent(evt) {
+ var evtName = evt.type;
+ if (listeners[evtName]) {
+ listeners[evtName].forEach(function(listener) {
+ if (listener.handleEvent) {
+ listener.handleEvent(evt);
+ } else {
+ listener.call(Mock, evt);
+ }
+ });
+ }
+ }
+
+ var Mock = {
+ addEventListener: mnb_addEventListener,
+ removeEventListener: mnb_removeEventListener,
+ mTeardown: mnb_init,
+ mTriggerEvent: mnb_mTriggerEvent
+ };
+
+ mnb_init();
+
+ window.MockNavigatorBattery = Mock;
+})();
diff --git a/apps/system/test/unit/mock_navigator_moz_mobile_connection.js b/apps/system/test/unit/mock_navigator_moz_mobile_connection.js
new file mode 100644
index 0000000..fb05aaa
--- /dev/null
+++ b/apps/system/test/unit/mock_navigator_moz_mobile_connection.js
@@ -0,0 +1,21 @@
+'use strict';
+
+(function() {
+
+ var props = ['voice', 'cardState', 'iccInfo', 'data'];
+
+ function mnmmc_init() {
+ props.forEach(function(prop) {
+ Mock[prop] = null;
+ });
+ }
+
+ var Mock = {
+ addEventListener: function() {},
+ mTeardown: mnmmc_init
+ };
+
+ mnmmc_init();
+
+ window.MockNavigatorMozMobileConnection = Mock;
+})();
diff --git a/apps/system/test/unit/mock_navigator_moz_telephony.js b/apps/system/test/unit/mock_navigator_moz_telephony.js
new file mode 100644
index 0000000..21f461c
--- /dev/null
+++ b/apps/system/test/unit/mock_navigator_moz_telephony.js
@@ -0,0 +1,55 @@
+'use strict';
+
+(function() {
+
+ var props = ['active', 'calls'];
+
+ var listeners;
+
+ function mnmt_init() {
+ props.forEach(function(prop) {
+ Mock[prop] = null;
+ });
+
+ listeners = {};
+ }
+
+ function mnmt_addEventListener(evtName, func) {
+ listeners[evtName] = listeners[evtName] || [];
+ listeners[evtName].push(func);
+ }
+
+ function mnmt_removeEventListener(evtName, func) {
+ if (listeners[evtName]) {
+ var listenerArray = listeners[evtName];
+ var index = listenerArray.indexOf(func);
+ if (index !== -1) {
+ listenerArray.splice(index, 1);
+ }
+ }
+ }
+
+ function mnmt_mTriggerEvent(evt) {
+ var evtName = evt.type;
+ if (listeners[evtName]) {
+ listeners[evtName].forEach(function(listener) {
+ if (listener.handleEvent) {
+ listener.handleEvent(evt);
+ } else {
+ listener.call(Mock, evt);
+ }
+ });
+ }
+ }
+
+ var Mock= {
+ addEventListener: mnmt_addEventListener,
+ removeEventListener: mnmt_removeEventListener,
+ mTeardown: mnmt_init,
+ mTriggerEvent: mnmt_mTriggerEvent
+ };
+
+ mnmt_init();
+
+ window.MockNavigatorMozTelephony = Mock;
+})();
diff --git a/apps/system/test/unit/mock_navigator_settings.js b/apps/system/test/unit/mock_navigator_settings.js
new file mode 100644
index 0000000..8b0de8a
--- /dev/null
+++ b/apps/system/test/unit/mock_navigator_settings.js
@@ -0,0 +1,64 @@
+(function(window) {
+ var observers = {},
+ settings = {},
+ removedObservers = {};
+
+ function mns_mLockSet(obj) {
+ for (var key in obj) {
+ settings[key] = obj[key];
+ }
+ }
+
+ function mns_addObserver(name, cb) {
+ observers[name] = observers[name] || [];
+ observers[name].push(cb);
+ }
+
+ function mns_removeObserver(name, cb) {
+ removedObservers[name] = removedObservers[name] || [];
+ removedObservers[name].push(cb);
+ }
+
+ function mns_createLock() {
+ return {
+ set: mns_mLockSet
+ };
+ }
+
+ function mns_mTriggerObservers(name, args) {
+ var theseObservers = observers[name];
+
+ if (! theseObservers) {
+ return;
+ }
+
+ theseObservers.forEach(function(func) {
+ func(args);
+ });
+ }
+
+ function mns_teardown() {
+ observers = {};
+ settings = {};
+ removedObservers = {};
+ }
+
+ window.MockNavigatorSettings = {
+ addObserver: mns_addObserver,
+ removeObserver: mns_removeObserver,
+ createLock: mns_createLock,
+
+ mTriggerObservers: mns_mTriggerObservers,
+ mTeardown: mns_teardown,
+ get mObservers() {
+ return observers;
+ },
+ get mSettings() {
+ return settings;
+ },
+ get mRemovedObservers() {
+ return removedObservers;
+ }
+ };
+
+})(this);
diff --git a/apps/system/test/unit/mock_navigator_wake_lock.js b/apps/system/test/unit/mock_navigator_wake_lock.js
new file mode 100644
index 0000000..c9ff845
--- /dev/null
+++ b/apps/system/test/unit/mock_navigator_wake_lock.js
@@ -0,0 +1,41 @@
+'use strict';
+
+(function() {
+ var lastWakeLock,
+ throwAtNextUnlock;
+
+ function mnwl_requestWakeLock(lock) {
+ lastWakeLock = {
+ released: false,
+ topic: lock,
+ unlock: function() {
+ if (throwAtNextUnlock) {
+ throwAtNextUnlock = false;
+ throw "NS_ERROR_DOM_INVALID_STATE_ERR";
+ }
+
+ this.released = true;
+ }
+ };
+ return lastWakeLock;
+ }
+
+ function mnwl_teardown() {
+ lastWakeLock = undefined;
+ throwAtNextUnlock = undefined;
+ }
+
+ function mnwl_throwAtNextUnlock() {
+ throwAtNextUnlock = true;
+ }
+
+ window.MockNavigatorWakeLock = {
+ requestWakeLock: mnwl_requestWakeLock,
+ mTeardown: mnwl_teardown,
+ mThrowAtNextUnlock: mnwl_throwAtNextUnlock,
+ get mLastWakeLock() {
+ return lastWakeLock;
+ }
+ };
+
+})();
diff --git a/apps/system/test/unit/mock_notification_helper.js b/apps/system/test/unit/mock_notification_helper.js
new file mode 100644
index 0000000..adb80f9
--- /dev/null
+++ b/apps/system/test/unit/mock_notification_helper.js
@@ -0,0 +1,22 @@
+var MockNotificationHelper = {
+ send: function(title, body, icon, clickCB, closeCB) {
+ this.mTitle = title;
+ this.mBody = body;
+ this.mIcon = icon;
+ this.mClickCB = clickCB;
+ this.mCloseCB = closeCB;
+ },
+
+ mTitle: null,
+ mBody: null,
+ mIcon: null,
+ mClickCB: null,
+ mCloseCB: null,
+ mTeardown: function teardown() {
+ this.mTitle = null;
+ this.mBody = null;
+ this.mIcon = null;
+ this.mClickCB = null;
+ this.mCloseCB = null;
+ }
+};
diff --git a/apps/system/test/unit/mock_notification_screen.js b/apps/system/test/unit/mock_notification_screen.js
new file mode 100644
index 0000000..2c88a90
--- /dev/null
+++ b/apps/system/test/unit/mock_notification_screen.js
@@ -0,0 +1,38 @@
+var MockNotificationScreen = {
+ wasMethodCalled: {},
+
+ mockMethods: [
+ 'incExternalNotifications',
+ 'decExternalNotifications',
+ 'updateStatusBarIcon'
+ ],
+
+ mockPopulate: function mockPopulate() {
+ this.mockMethods.forEach(function(methodName) {
+ // we could probably put this method outside if we had a closure
+ this[methodName] = function mns_method() {
+ this.methodCalled(methodName);
+ };
+ }, this);
+ },
+
+ init: function mns_init() {
+ this.wasMethodCalled = {};
+ this.mockMethods.forEach(function(methodName) {
+ this[methodName].wasCalled = false;
+ }, this);
+ },
+
+ methodCalled: function mns_methodCalled(name) {
+ this.wasMethodCalled[name] =
+ this.wasMethodCalled[name] ? this.wasMethodCalled[name]++ : 1;
+ this[name].wasCalled = true;
+ },
+
+ mTeardown: function mns_mTeardown() {
+ this.init();
+ }
+};
+
+MockNotificationScreen.mockPopulate();
+
diff --git a/apps/system/test/unit/mock_settings_listener.js b/apps/system/test/unit/mock_settings_listener.js
new file mode 100644
index 0000000..6a4da7f
--- /dev/null
+++ b/apps/system/test/unit/mock_settings_listener.js
@@ -0,0 +1,16 @@
+var MockSettingsListener = {
+ observe: function msl_observe(name, defaultValue, cb) {
+ this.mName = name;
+ this.mDefaultValue = defaultValue;
+ this.mCallback = cb;
+ },
+
+ mName: null,
+ mDefaultValue: null,
+ mCallback: null,
+ mTeardown: function teardown() {
+ this.mName = null;
+ this.mDefaultValue = null;
+ this.mDefaultCallback = null;
+ }
+};
diff --git a/apps/system/test/unit/mock_sleep_menu.js b/apps/system/test/unit/mock_sleep_menu.js
new file mode 100644
index 0000000..1b883dc
--- /dev/null
+++ b/apps/system/test/unit/mock_sleep_menu.js
@@ -0,0 +1,5 @@
+'use strict';
+
+var MockSleepMenu = {
+ startPowerOff: function(){}
+};
diff --git a/apps/system/test/unit/mock_statusbar.js b/apps/system/test/unit/mock_statusbar.js
new file mode 100644
index 0000000..8813a3b
--- /dev/null
+++ b/apps/system/test/unit/mock_statusbar.js
@@ -0,0 +1,36 @@
+var MockStatusBar = {
+ notificationsCount: null,
+
+ wasMethodCalled: {},
+
+ methodCalled: function msb_methodCalled(name) {
+ this.wasMethodCalled[name] =
+ this.wasMethodCalled[name] ? this.wasMethodCalled[name]++ : 1;
+ },
+
+ updateNotification: function(count) {
+ var number = new Number(count);
+ this.notificationsCount = number.toString();
+ this.methodCalled('updateNotification');
+ },
+
+ updateNotificationUnread: function(unread) {
+ this.mNotificationUnread = unread;
+ },
+
+ mNotificationUnread: false,
+ mTeardown: function teardown() {
+ this.notificationsCount = null;
+ this.mNotificationsUpdated = false;
+ this.mNotificationUnread = false;
+ this.wasMethodCalled = {};
+ },
+
+ incSystemDownloads: function msb_incSystemDownloads() {
+ this.methodCalled('incSystemDownloads');
+ },
+
+ decSystemDownloads: function msb_decSystemDownloads() {
+ this.methodCalled('decSystemDownloads');
+ }
+};
diff --git a/apps/system/test/unit/mock_system_banner.js b/apps/system/test/unit/mock_system_banner.js
new file mode 100644
index 0000000..e40d26a
--- /dev/null
+++ b/apps/system/test/unit/mock_system_banner.js
@@ -0,0 +1,13 @@
+var MockSystemBanner = {
+ show: function(message) {
+ this.mShowCount++;
+ this.mMessage = message;
+ },
+
+ mShowCount: 0,
+ mMessage: null,
+ mTeardown: function teardown() {
+ this.mShowCount = 0;
+ this.mMessage = null;
+ }
+};
diff --git a/apps/system/test/unit/mock_trusted_ui_manager.js b/apps/system/test/unit/mock_trusted_ui_manager.js
new file mode 100644
index 0000000..672ed47
--- /dev/null
+++ b/apps/system/test/unit/mock_trusted_ui_manager.js
@@ -0,0 +1,25 @@
+'use strict';
+
+var MockTrustedUIManager = {
+ open: function(name, frame, chromeEventId) {
+ this.mOpened = true;
+ this.mName = name;
+ this.mFrame = frame;
+ this.mChromeEventId = chromeEventId;
+ },
+
+ close: function() {
+ this.mOpened = false;
+ },
+
+ mOpened: false,
+ mName: null,
+ mFrame: null,
+ mChromeEventId: null,
+ mTeardown: function teardown() {
+ this.mOpened = false;
+ this.mName = null;
+ this.mFrame = null;
+ this.mChromeEventId = null;
+ }
+};
diff --git a/apps/system/test/unit/mock_updatable.js b/apps/system/test/unit/mock_updatable.js
new file mode 100644
index 0000000..33f5fb8
--- /dev/null
+++ b/apps/system/test/unit/mock_updatable.js
@@ -0,0 +1,72 @@
+'use strict';
+
+function MockAppUpdatable(aApp) {
+ this.app = aApp;
+
+ this.mDownloadCalled = false;
+ this.mCancelCalled = false;
+ this.mUninitCalled = false;
+ MockAppUpdatable.mCount++;
+}
+
+MockAppUpdatable.mTeardown = function() {
+ MockAppUpdatable.mCount = 0;
+};
+
+MockAppUpdatable.mCount = 0;
+
+MockAppUpdatable.prototype.uninit = function() {
+ this.mUninitCalled = true;
+};
+
+MockAppUpdatable.prototype.download = function() {
+ this.mDownloadCalled = true;
+};
+
+MockAppUpdatable.prototype.cancelDownload = function() {
+ this.mCancelCalled = true;
+};
+
+function MockSystemUpdatable() {
+ this.size = null;
+ this.name = 'systemUpdate';
+
+ this.mDownloadCalled = false;
+ this.mCancelCalled = false;
+ this.mUninitCalled = false;
+
+ MockSystemUpdatable.mInstancesCount++;
+}
+
+MockSystemUpdatable.mInstancesCount = 0;
+MockSystemUpdatable.mTeardown = function() {
+ MockSystemUpdatable.mInstancesCount = 0;
+ delete MockSystemUpdatable.mKnownUpdate;
+};
+
+
+MockSystemUpdatable.prototype.uninit = function() {
+ this.mUninitCalled = true;
+};
+
+MockSystemUpdatable.prototype.download = function() {
+ this.mDownloadCalled = true;
+};
+
+MockSystemUpdatable.prototype.cancelDownload = function() {
+ this.mCancelCalled = true;
+};
+
+MockSystemUpdatable.prototype.rememberKnownUpdate = function() {
+ this.mKnownUpdate = true;
+};
+
+MockSystemUpdatable.prototype.forgetKnownUpdate = function() {
+ delete this.mKnownUpdate;
+};
+
+MockSystemUpdatable.prototype.checkKnownUpdate = function(callback) {
+ if (this.mKnownUpdate && typeof callback === 'function') {
+ callback();
+ }
+};
diff --git a/apps/system/test/unit/mock_update_manager.js b/apps/system/test/unit/mock_update_manager.js
new file mode 100644
index 0000000..86cb4b9
--- /dev/null
+++ b/apps/system/test/unit/mock_update_manager.js
@@ -0,0 +1,60 @@
+'use strict';
+
+var MockUpdateManager = {
+ addToUpdatesQueue: function mum_addtoUpdateQueue(updatable) {
+ this.mLastUpdatesAdd = updatable;
+ },
+ addToUpdatableApps: function mum_addToUpdatableApps(updatable) {
+ this.mLastUpdatableAdd = updatable;
+ },
+
+ removeFromUpdatesQueue: function mum_removeFromUpdateQueue(updatable) {
+ this.mLastUpdatesRemoval = updatable;
+ },
+
+ addToDownloadsQueue: function mum_addtoActiveDownloads(updatable) {
+ this.mLastDownloadsAdd = updatable;
+ },
+ removeFromDownloadsQueue:
+ function mum_removeFromActiveDownloads(updatable) {
+
+ this.mLastDownloadsRemoval = updatable;
+ },
+
+ downloadProgressed: function mum_downloadProgressed(bytes) {
+ this.mProgressCalledWith = bytes;
+ },
+
+ startedUncompressing: function mum_startedUncompressing() {
+ this.mStartedUncompressingCalled = true;
+ },
+
+ requestErrorBanner: function mum_requestErrorBanner() {
+ this.mErrorBannerRequested = true;
+ },
+
+ checkForUpdates: function mum_checkForUpdate(forced) {
+ this.mCheckForUpdatesCalledWith = forced;
+ },
+
+ mErrorBannerRequested: false,
+ mLastUpdatesAdd: null,
+ mLastUpdatableAdd: null,
+ mLastUpdatesRemoval: null,
+ mLastDownloadsAdd: null,
+ mLastDownloadsRemoval: null,
+ mProgressCalledWith: null,
+ mCheckForUpdatesCalledWith: null,
+ mStartedUncompressingCalled: false,
+ mTeardown: function mum_mTeardown() {
+ this.mErrorBannerRequested = false;
+ this.mLastUpdatesAdd = null;
+ this.mLastUpdatableAdd = null;
+ this.mLastUpdatesRemoval = null;
+ this.mLastDownloadsAdd = null;
+ this.mLastDownloadsRemoval = null;
+ this.mProgressCalledWith = null;
+ this.mCheckForUpdatesCalledWith = null;
+ this.mStartedUncompressingCalled = false;
+ }
+};
diff --git a/apps/system/test/unit/mock_utility_tray.js b/apps/system/test/unit/mock_utility_tray.js
new file mode 100644
index 0000000..8bf8a8b
--- /dev/null
+++ b/apps/system/test/unit/mock_utility_tray.js
@@ -0,0 +1,14 @@
+var MockUtilityTray = {
+ show: function() {
+ this.mShown = true;
+ },
+
+ hide: function() {
+ this.mShown = false;
+ },
+
+ mShown: false,
+ mTeardown: function teardown() {
+ this.mShown = false;
+ }
+};
diff --git a/apps/system/test/unit/mock_window_manager.js b/apps/system/test/unit/mock_window_manager.js
new file mode 100644
index 0000000..331e254
--- /dev/null
+++ b/apps/system/test/unit/mock_window_manager.js
@@ -0,0 +1,16 @@
+var MockWindowManager = {
+ getDisplayedApp: function mwm_getDisplayedApp() {
+ return this.mDisplayedApp;
+ },
+
+ kill: function mwm_kill(origin) {
+ this.mLastKilledOrigin = origin;
+ },
+
+ mDisplayedApp: '',
+ mLastKilledOrigin: '',
+ mTeardown: function() {
+ this.mDisplayedApp = '';
+ this.mLastKilledOrigin = '';
+ }
+};
diff --git a/apps/system/test/unit/mocks_helper.js b/apps/system/test/unit/mocks_helper.js
new file mode 100644
index 0000000..40c8689
--- /dev/null
+++ b/apps/system/test/unit/mocks_helper.js
@@ -0,0 +1,40 @@
+var MocksHelper = function(mocks) {
+ this.mocks = mocks;
+ this.realWindowObjects = {};
+};
+
+MocksHelper.prototype = {
+
+ setup: function mh_setup() {
+ },
+
+ suiteSetup: function mh_suiteSetup() {
+ this.mocks.forEach(function(objName) {
+ var mockName = 'Mock' + objName;
+ if (!window[mockName]) {
+ throw 'Mock ' + mockName + ' has not been loaded into the test';
+ }
+
+ this.realWindowObjects[objName] = window[objName];
+ window[objName] = window[mockName];
+ }, this);
+ },
+
+ suiteTeardown: function mh_suiteTeardown() {
+ this.mocks.forEach(function(objName) {
+ window[objName] = this.realWindowObjects[objName];
+ }, this);
+ },
+
+ teardown: function mh_teardown() {
+ this.mocks.forEach(function(objName) {
+ var mockName = 'Mock' + objName;
+ var mock = window[mockName];
+
+ if (mock.mTeardown) {
+ mock.mTeardown();
+ }
+ });
+ }
+};
+
diff --git a/apps/system/test/unit/notifications_test.js b/apps/system/test/unit/notifications_test.js
new file mode 100644
index 0000000..e323f07
--- /dev/null
+++ b/apps/system/test/unit/notifications_test.js
@@ -0,0 +1,132 @@
+'use strict';
+
+requireApp('system/test/unit/mock_statusbar.js');
+requireApp('system/test/unit/mock_gesture_detector.js');
+requireApp('system/test/unit/mock_settings_listener.js');
+requireApp('system/test/unit/mocks_helper.js');
+
+requireApp('system/js/notifications.js');
+
+var mocksForNotificationScreen = ['StatusBar', 'GestureDetector',
+ 'SettingsListener'];
+
+mocksForNotificationScreen.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+
+suite('system/NotificationScreen >', function() {
+ var fakeNotifContainer, fakeLockScreenContainer, fakeToaster,
+ fakeButton, fakeToasterIcon, fakeToasterTitle, fakeToasterDetail;
+
+ var mocksHelper;
+
+ suiteSetup(function() {
+ mocksHelper = new MocksHelper(mocksForNotificationScreen);
+ mocksHelper.suiteSetup();
+ });
+
+ suiteTeardown(function() {
+ mocksHelper.suiteTeardown();
+ });
+
+ setup(function() {
+ fakeNotifContainer = document.createElement('div');
+ fakeNotifContainer.id = 'desktop-notifications-container';
+ // add some children, we don't care what they are
+ fakeNotifContainer.appendChild(document.createElement('div'));
+ fakeNotifContainer.appendChild(document.createElement('div'));
+
+ function createFakeElement(tag, id) {
+ var obj = document.createElement(tag);
+ obj.id = id;
+ return obj;
+ };
+
+ fakeLockScreenContainer = createFakeElement('div',
+ 'notifications-lockscreen-container');
+ fakeToaster = createFakeElement('div', 'notification-toaster');
+ fakeButton = createFakeElement('button', 'notification-clear');
+ fakeToasterIcon = createFakeElement('img', 'toaster-icon');
+ fakeToasterTitle = createFakeElement('div', 'toaster-title');
+ fakeToasterDetail = createFakeElement('div', 'toaster-detail');
+
+ document.body.appendChild(fakeNotifContainer);
+
+ document.body.appendChild(fakeLockScreenContainer);
+ document.body.appendChild(fakeToaster);
+ document.body.appendChild(fakeButton);
+ document.body.appendChild(fakeToasterIcon);
+ document.body.appendChild(fakeToasterTitle);
+ document.body.appendChild(fakeToasterDetail);
+
+ mocksHelper.setup();
+
+ NotificationScreen.init();
+ });
+
+ teardown(function() {
+ fakeNotifContainer.parentNode.removeChild(fakeNotifContainer);
+ fakeLockScreenContainer.parentNode.removeChild(fakeLockScreenContainer);
+ fakeToaster.parentNode.removeChild(fakeToaster);
+ fakeButton.parentNode.removeChild(fakeButton);
+
+ mocksHelper.teardown();
+ });
+
+ suite('updateStatusBarIcon >', function() {
+ setup(function() {
+ NotificationScreen.updateStatusBarIcon();
+ });
+
+ test('should update the icon in the status bar', function() {
+ assert.ok(MockStatusBar.wasMethodCalled['updateNotification']);
+ assert.equal(2, MockStatusBar.notificationsCount);
+ });
+
+ test('external notif should not be able to decrease the global count',
+ function() {
+
+ NotificationScreen.decExternalNotifications();
+ assert.equal(2, MockStatusBar.notificationsCount);
+ });
+
+ test('external notif should increase the global count',
+ function() {
+
+ NotificationScreen.incExternalNotifications();
+ assert.isTrue(MockStatusBar.mNotificationUnread);
+ assert.equal(3, MockStatusBar.notificationsCount);
+ });
+
+ test('external notif should decrease the global count',
+ function() {
+
+ NotificationScreen.incExternalNotifications();
+ MockStatusBar.mNotificationUnread = false;
+ NotificationScreen.decExternalNotifications();
+ assert.isFalse(MockStatusBar.mNotificationUnread);
+ assert.equal(2, MockStatusBar.notificationsCount);
+ });
+
+ test('should change the read status', function() {
+ NotificationScreen.updateStatusBarIcon(true);
+ assert.isTrue(MockStatusBar.mNotificationUnread);
+ });
+
+ test('calling addNotification without icon', function() {
+ var toasterIcon = NotificationScreen.toasterIcon;
+ var imgpath = 'http://example.com/test.png';
+ var detail = {icon: imgpath, title: 'title', detail: 'detail'};
+ NotificationScreen.addNotification(detail);
+ assert.equal(imgpath, toasterIcon.src);
+ assert.isFalse(toasterIcon.hidden);
+ delete detail.icon;
+ NotificationScreen.addNotification(detail);
+ assert.isTrue(toasterIcon.hidden);
+ });
+ });
+
+});
diff --git a/apps/system/test/unit/statusbar_test.js b/apps/system/test/unit/statusbar_test.js
new file mode 100644
index 0000000..fba5c54
--- /dev/null
+++ b/apps/system/test/unit/statusbar_test.js
@@ -0,0 +1,421 @@
+'use strict';
+
+requireApp('system/test/unit/mock_settings_listener.js');
+requireApp('system/test/unit/mock_l10n.js');
+requireApp('system/test/unit/mock_navigator_moz_mobile_connection.js');
+requireApp('system/test/unit/mock_navigator_moz_telephony.js');
+requireApp('system/test/unit/mock_mobile_operator.js');
+requireApp('system/test/unit/mocks_helper.js');
+
+requireApp('system/js/statusbar.js');
+
+var mocksForStatusBar = ['SettingsListener', 'MobileOperator'];
+
+mocksForStatusBar.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+suite('system/Statusbar', function() {
+ var fakeStatusBarNode;
+ var mocksHelper;
+
+ var realSettingsListener, realMozL10n, realMozMobileConnection,
+ realMozTelephony,
+ fakeIcons = [];
+
+ suiteSetup(function() {
+ mocksHelper = new MocksHelper(mocksForStatusBar);
+ mocksHelper.suiteSetup();
+ realMozL10n = navigator.mozL10n;
+ navigator.mozL10n = MockL10n;
+ realMozMobileConnection = navigator.mozMobileConnection;
+ navigator.mozMobileConnection = MockNavigatorMozMobileConnection;
+ realMozTelephony = navigator.mozTelephony;
+ navigator.mozTelephony = MockNavigatorMozTelephony;
+ });
+
+ suiteTeardown(function() {
+ mocksHelper.suiteTeardown();
+ navigator.mozL10n = realMozL10n;
+ navigator.mozMobileConnection = realMozMobileConnection;
+ navigator.mozTelephony = realMozTelephony;
+ window.SettingsListener = realSettingsListener;
+ });
+
+ setup(function() {
+ mocksHelper.setup();
+ fakeStatusBarNode = document.createElement('div');
+ fakeStatusBarNode.id = 'statusbar';
+ document.body.appendChild(fakeStatusBarNode);
+
+ StatusBar.ELEMENTS.forEach(function testAddElement(elementName) {
+ var elt = document.createElement('div');
+ elt.id = 'statusbar-' + elementName;
+ elt.hidden = true;
+ fakeStatusBarNode.appendChild(elt);
+ fakeIcons[elementName] = elt;
+ });
+
+ // executing init again
+ StatusBar.init();
+ });
+ teardown(function() {
+ mocksHelper.teardown();
+ fakeStatusBarNode.parentNode.removeChild(fakeStatusBarNode);
+ MockNavigatorMozTelephony.mTeardown();
+ MockNavigatorMozMobileConnection.mTeardown();
+ });
+
+ suite('system-downloads', function() {
+ test('incrementing should display the icon', function() {
+ StatusBar.incSystemDownloads();
+ assert.isFalse(fakeIcons['system-downloads'].hidden);
+ });
+ test('incrementing then decrementing should not display the icon',
+ function() {
+ StatusBar.incSystemDownloads();
+ StatusBar.decSystemDownloads();
+ assert.isTrue(fakeIcons['system-downloads'].hidden);
+ });
+ test('incrementing twice then decrementing once should display the icon',
+ function() {
+ StatusBar.incSystemDownloads();
+ StatusBar.incSystemDownloads();
+ StatusBar.decSystemDownloads();
+ assert.isFalse(fakeIcons['system-downloads'].hidden);
+ });
+ test('incrementing then decrementing twice should not display the icon',
+ function() {
+ StatusBar.incSystemDownloads();
+ StatusBar.decSystemDownloads();
+ StatusBar.decSystemDownloads();
+ assert.isTrue(fakeIcons['system-downloads'].hidden);
+ });
+
+ /* JW: testing that we can't have a negative counter */
+ test('incrementing then decrementing twice then incrementing should ' +
+ 'display the icon', function() {
+ StatusBar.incSystemDownloads();
+ StatusBar.decSystemDownloads();
+ StatusBar.decSystemDownloads();
+ StatusBar.incSystemDownloads();
+ assert.isFalse(fakeIcons['system-downloads'].hidden);
+ });
+ });
+
+ suite('signal icon', function() {
+ var dataset;
+ setup(function() {
+ dataset = fakeIcons.signal.dataset;
+ });
+
+ test('no network without sim, not searching', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: null,
+ emergencyCallsOnly: false,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'absent';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.notEqual(dataset.emergency, 'true');
+ assert.isUndefined(dataset.level);
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('no network without sim, searching', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: null,
+ emergencyCallsOnly: false,
+ state: 'searching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'absent';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.notEqual(dataset.emergency, 'true');
+ assert.isUndefined(dataset.level);
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('no network with sim', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: null,
+ emergencyCallsOnly: false,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'pinRequired';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.notEqual(dataset.emergency, 'true');
+ assert.equal(dataset.level, -1);
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('searching', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: null,
+ emergencyCallsOnly: false,
+ state: 'searching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'ready';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.notEqual(dataset.emergency, 'true');
+ assert.equal(dataset.level, -1);
+ assert.equal(dataset.searching, 'true');
+ });
+
+ test('emergency calls only, no sim', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'absent';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.notEqual(dataset.emergency, 'true');
+ assert.isUndefined(dataset.level);
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('emergency calls only, with sim', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'pinRequired';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.emergency, 'true');
+ assert.equal(dataset.level, '-1');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('emergency calls only, in call', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'pinRequired';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ MockNavigatorMozTelephony.active = {
+ state: 'connected'
+ };
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.level, 4);
+ assert.notEqual(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('emergency calls only, dialing', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'pinRequired';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ MockNavigatorMozTelephony.active = {
+ state: 'dialing'
+ };
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.level, 4);
+ assert.notEqual(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('emergency calls, passing a call', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'pinRequired';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ var activeCall = {
+ state: 'dialing'
+ };
+
+ MockNavigatorMozTelephony.active = activeCall;
+ MockNavigatorMozTelephony.calls = [activeCall];
+
+ var evt = new CustomEvent('callschanged');
+ MockNavigatorMozTelephony.mTriggerEvent(evt);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.level, 4);
+ assert.notEqual(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('normal carrier', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: true,
+ relSignalStrength: 80,
+ emergencyCallsOnly: false,
+ state: 'notSearching',
+ roaming: false,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'ready';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.level, 4);
+ assert.notEqual(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('roaming', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: true,
+ relSignalStrength: 80,
+ emergencyCallsOnly: false,
+ state: 'notSearching',
+ roaming: true,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'ready';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.equal(dataset.roaming, 'true');
+ assert.equal(dataset.level, 4);
+ assert.notEqual(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+
+ test('emergency calls, roaming', function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: false,
+ relSignalStrength: 80,
+ emergencyCallsOnly: true,
+ state: 'notSearching',
+ roaming: true,
+ network: {}
+ };
+
+ MockNavigatorMozMobileConnection.cardState = 'ready';
+ MockNavigatorMozMobileConnection.iccInfo = {};
+
+ StatusBar.update.signal.call(StatusBar);
+
+ assert.notEqual(dataset.roaming, 'true');
+ assert.equal(dataset.level, -1);
+ assert.equal(dataset.emergency, 'true');
+ assert.notEqual(dataset.searching, 'true');
+ });
+ }),
+
+ suite('operator name', function() {
+ setup(function() {
+ MockNavigatorMozMobileConnection.voice = {
+ connected: true,
+ network: {
+ shortName: 'Fake short',
+ longName: 'Fake long',
+ mnc: 10 // VIVO
+ },
+ cell: {
+ gsmLocationAreaCode: 71 // BA
+ }
+ }
+
+ MockNavigatorMozMobileConnection.iccInfo = {
+ isDisplaySpnRequired: false,
+ spn: 'Fake SPN'
+ }
+ });
+
+ test('Connection without region', function() {
+ MobileOperator.mOperator = 'Orange';
+ var evt = new CustomEvent('iccinfochange');
+ StatusBar.handleEvent(evt);
+ assert.include(fakeIcons.label.textContent, 'Orange');
+ });
+ test('Connection with region', function() {
+ MobileOperator.mOperator = 'Orange';
+ MobileOperator.mRegion = 'PR';
+ var evt = new CustomEvent('iccinfochange');
+ StatusBar.handleEvent(evt);
+ var label_content = fakeIcons.label.textContent;
+ assert.include(label_content, 'Orange');
+ assert.include(label_content, 'PR');
+ });
+ });
+});
diff --git a/apps/system/test/unit/style/lockscreen/images/mask.png b/apps/system/test/unit/style/lockscreen/images/mask.png
new file mode 100644
index 0000000..e1b8cf5
--- /dev/null
+++ b/apps/system/test/unit/style/lockscreen/images/mask.png
Binary files differ
diff --git a/apps/system/test/unit/updatable_test.js b/apps/system/test/unit/updatable_test.js
new file mode 100644
index 0000000..7af2abf
--- /dev/null
+++ b/apps/system/test/unit/updatable_test.js
@@ -0,0 +1,630 @@
+'use strict';
+
+requireApp('system/js/updatable.js');
+
+requireApp('system/test/unit/mock_app.js');
+requireApp('system/test/unit/mock_asyncStorage.js');
+requireApp('system/test/unit/mock_update_manager.js');
+requireApp('system/test/unit/mock_window_manager.js');
+requireApp('system/test/unit/mock_apps_mgmt.js');
+requireApp('system/test/unit/mock_chrome_event.js');
+requireApp('system/test/unit/mock_custom_dialog.js');
+requireApp('system/test/unit/mock_utility_tray.js');
+requireApp('system/test/unit/mock_manifest_helper.js');
+requireApp('system/test/unit/mocks_helper.js');
+
+
+var mocksForUpdatable = [
+ 'CustomDialog',
+ 'UpdateManager',
+ 'WindowManager',
+ 'UtilityTray',
+ 'ManifestHelper',
+ 'asyncStorage'
+];
+
+mocksForUpdatable.forEach(function(mockName) {
+ if (!window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+suite('system/Updatable', function() {
+ var subject;
+ var mockApp;
+
+ var realDispatchEvent;
+ var realL10n;
+
+ var mocksHelper;
+
+ var lastDispatchedEvent = null;
+ var fakeDispatchEvent;
+
+ suiteSetup(function() {
+ realL10n = navigator.mozL10n;
+ navigator.mozL10n = {
+ get: function get(key) {
+ return key;
+ }
+ };
+
+ mocksHelper = new MocksHelper(mocksForUpdatable);
+ mocksHelper.suiteSetup();
+ });
+
+ suiteTeardown(function() {
+ navigator.mozL10n = realL10n;
+ mocksHelper.suiteTeardown();
+ });
+
+ setup(function() {
+ mockApp = new MockApp();
+ subject = new AppUpdatable(mockApp);
+ subject._mgmt = MockAppsMgmt;
+
+ fakeDispatchEvent = function(type, value) {
+ lastDispatchedEvent = {
+ type: type,
+ value: value
+ };
+ };
+ subject._dispatchEvent = fakeDispatchEvent;
+
+ mocksHelper.setup();
+ });
+
+ teardown(function() {
+ MockAppsMgmt.mTeardown();
+ mocksHelper.teardown();
+
+ subject._dispatchEvent = realDispatchEvent;
+ lastDispatchedEvent = null;
+ });
+
+ function downloadAvailableSuite(name, setupFunc) {
+ suite(name, function() {
+ setup(setupFunc);
+
+ test('should add self to the available downloads', function() {
+ assert.isNotNull(MockUpdateManager.mLastUpdatesAdd);
+ assert.equal(MockUpdateManager.mLastUpdatesAdd.app.mId,
+ mockApp.mId);
+ });
+
+ suite('first progress', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadProgress(42);
+ });
+
+ test('should add self to active downloads', function() {
+ assert.isNotNull(MockUpdateManager.mLastDownloadsAdd);
+ assert.equal(MockUpdateManager.mLastDownloadsAdd.app.mId,
+ mockApp.mId);
+ });
+
+ test('should start with first progress value', function() {
+ assert.equal(42, subject.progress);
+ });
+ });
+ });
+ }
+
+ suite('init', function() {
+ test('should keep a reference to the app', function() {
+ assert.equal(mockApp, subject.app);
+ });
+
+ test('should handle fresh app with just an updateManifest', function() {
+ var freshApp = new MockApp();
+ freshApp.manifest = undefined;
+ subject = new AppUpdatable(freshApp);
+ assert.equal(freshApp, subject.app);
+ });
+
+ test('should add itself to updatable apps', function() {
+ assert.equal(MockUpdateManager.mLastUpdatableAdd, subject);
+ });
+
+ test('should remember about the update on startup', function() {
+ asyncStorage.mItems[SystemUpdatable.KNOWN_UPDATE_FLAG] = true;
+ var systemUpdatable = new SystemUpdatable();
+ assert.equal(MockUpdateManager.mCheckForUpdatesCalledWith, true);
+ });
+
+ downloadAvailableSuite('app has a download available', function() {
+ mockApp.downloadAvailable = true;
+ subject = new AppUpdatable(mockApp);
+ });
+
+ test('should apply update if downloaded', function() {
+ mockApp.readyToApplyDownload = true;
+ subject = new AppUpdatable(mockApp);
+ // We cannot test for this._mgmt methods because it's created in
+ // a constructor, so we check if the window is killed because
+ // WindowManager.kill() is also called in applyUpdate() method
+ assert.equal(MockWindowManager.mLastKilledOrigin, subject.app.origin);
+ });
+ });
+
+ suite('infos', function() {
+ suite('name', function() {
+ test('should give a name for system updates', function() {
+ subject = new SystemUpdatable(42);
+ assert.equal('systemUpdate', subject.name);
+ });
+
+ test('should give a name for app updates', function() {
+ assert.equal('Mock app', subject.name);
+ });
+ });
+
+ suite('size', function() {
+ test('should give packaged app update size', function() {
+ assert.equal(null, subject.size);
+ });
+
+ test('should return null for hosted apps', function() {
+ mockApp.updateManifest = null;
+ subject = new AppUpdatable(mockApp);
+ assert.isNull(subject.size);
+ });
+
+ test('should update size on download available', function() {
+ mockApp.updateManifest = null;
+ subject = new AppUpdatable(mockApp);
+ assert.isNull(subject.size);
+
+ mockApp.mTriggerDownloadAvailable(45678);
+ assert.equal(45678, subject.size);
+ });
+ });
+ });
+
+ suite('actions', function() {
+ suite('ask for download', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadAvailable();
+ subject.download();
+ });
+
+ test('should call download on the app', function() {
+ assert.isTrue(mockApp.mDownloadCalled);
+ });
+ });
+
+ suite('download system update', function() {
+ setup(function() {
+ subject = new SystemUpdatable(42);
+ subject._dispatchEvent = fakeDispatchEvent;
+ subject.progress = 42;
+ subject.download();
+ });
+
+ test('should send download message for system updates', function() {
+ assert.equal('update-available-result', lastDispatchedEvent.type);
+ assert.equal('download', lastDispatchedEvent.value);
+ });
+
+ test('should add system updates to active downloads too', function() {
+ assert.isNotNull(MockUpdateManager.mLastDownloadsAdd);
+ assert.equal(subject, MockUpdateManager.mLastDownloadsAdd);
+ });
+
+ test('should start system updates with progress 0 too', function() {
+ assert.equal(subject.progress, 0);
+ });
+
+ test('should do nothing if already downloading', function() {
+ lastDispatchedEvent = null;
+ subject.progress = 42;
+ subject.download();
+
+ assert.equal(subject.progress, 42);
+ assert.isNull(lastDispatchedEvent);
+ });
+ });
+
+ suite('cancel app update download', function() {
+ setup(function() {
+ subject.cancelDownload();
+ });
+
+ test('should call cancelDownload on the app', function() {
+ assert.isTrue(mockApp.mCancelCalled);
+ });
+ });
+
+ suite('cancel system update download', function() {
+ setup(function() {
+ asyncStorage.setItem(SystemUpdatable.KNOWN_UPDATE_FLAG, true);
+ subject = new SystemUpdatable(42);
+ subject.download();
+ subject._dispatchEvent = fakeDispatchEvent;
+ subject.cancelDownload();
+ });
+
+ test('should send cancel message', function() {
+ assert.equal('update-download-cancel', lastDispatchedEvent.type);
+ });
+
+ test('should remove the downloading flag', function() {
+ assert.isFalse(subject.downloading);
+ });
+ });
+ });
+
+ suite('events', function() {
+ suite('apps events', function() {
+ // This function checks that we release the callbacks properly
+ // at the end of a download. Assumes subject.download() was called.
+ function testCleanup() {
+ test('should stop responding to progress', function() {
+ mockApp.mTriggerDownloadProgress(42);
+ assert.notEqual(subject.progress, 42);
+ });
+
+ test('should stop responding to error', function() {
+ MockUpdateManager.mErrorBannerRequested = false;
+ mockApp.mTriggerDownloadError();
+ assert.isFalse(MockUpdateManager.mErrorBannerRequested);
+ });
+
+ test('progress should be reset', function() {
+ assert.isNull(subject.progress);
+ });
+ }
+
+ downloadAvailableSuite('ondownloadavailable', function() {
+ mockApp.mTriggerDownloadAvailable();
+ });
+
+ suite('ondownloadavailable when not installed', function() {
+ setup(function() {
+ mockApp.installState = 'pending';
+ mockApp.mTriggerDownloadAvailable();
+ });
+
+ test('should not add self to the available downloads', function() {
+ assert.isNull(MockUpdateManager.mLastUpdatesAdd);
+ });
+
+ test('should not answer to progress', function() {
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNull(MockUpdateManager.mLastDownloadsRemoval);
+ });
+ });
+
+ suite('downloadavailable at init when not installed', function() {
+ setup(function() {
+ mockApp.installState = 'pending';
+ subject = new AppUpdatable(mockApp);
+ mockApp.mTriggerDownloadAvailable();
+ });
+
+ test('should not add self to the available downloads', function() {
+ assert.isNull(MockUpdateManager.mLastUpdatesAdd);
+ });
+
+ test('should not answer to progress', function() {
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNull(MockUpdateManager.mLastDownloadsRemoval);
+ });
+ });
+
+ suite('ondownloadsuccess', function() {
+ test('should remove self from active downloads', function() {
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadProgress(42);
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNotNull(MockUpdateManager.mLastDownloadsRemoval);
+ assert.equal(MockUpdateManager.mLastDownloadsRemoval.app.mId,
+ mockApp.mId);
+ });
+
+ test('should not remove self if not downloading', function() {
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNull(MockUpdateManager.mLastDownloadsRemoval);
+ });
+
+ test('should remove self from available downloads', function() {
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadProgress(42);
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNotNull(MockUpdateManager.mLastUpdatesRemoval);
+ assert.equal(MockUpdateManager.mLastUpdatesRemoval.app.mId,
+ mockApp.mId);
+ });
+
+ suite('application of the download', function() {
+ test('should apply if the app is not in foreground', function() {
+ mockApp.mTriggerDownloadAvailable();
+ MockWindowManager.mDisplayedApp =
+ 'http://homescreen.gaiamobile.org';
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNotNull(MockAppsMgmt.mLastAppApplied);
+ assert.equal(MockAppsMgmt.mLastAppApplied.mId, mockApp.mId);
+ });
+
+ test('should wait for appwillclose if it is', function() {
+ var origin = 'http://testapp.gaiamobile.org';
+ mockApp.origin = origin;
+ MockWindowManager.mDisplayedApp = origin;
+
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadSuccess();
+ assert.isNull(MockAppsMgmt.mLastAppApplied);
+
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent('appwillclose', true, false,
+ { origin: origin });
+ window.dispatchEvent(evt);
+
+ assert.isNotNull(MockAppsMgmt.mLastAppApplied);
+ assert.equal(MockAppsMgmt.mLastAppApplied.mId, mockApp.mId);
+ });
+
+ test('should kill the app before applying the update', function() {
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadSuccess();
+ assert.equal('https://testapp.gaiamobile.org',
+ MockWindowManager.mLastKilledOrigin);
+ });
+ });
+ });
+
+ suite('ondownloaderror', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadError();
+ });
+
+ test('should request error banner', function() {
+ assert.isTrue(MockUpdateManager.mErrorBannerRequested);
+ });
+
+ test('should remove self from active downloads', function() {
+ assert.isNotNull(MockUpdateManager.mLastDownloadsRemoval);
+ assert.equal(MockUpdateManager.mLastDownloadsRemoval.app.mId,
+ mockApp.mId);
+ });
+
+ test('progress should be reset', function() {
+ assert.isNull(subject.progress);
+ });
+
+ test('should still answer to progress events', function() {
+ mockApp.mTriggerDownloadProgress(42);
+ assert.equal(42, subject.progress);
+ });
+ });
+
+ suite('onprogress', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadAvailable();
+ });
+
+ test('should send progress to update manager', function() {
+ mockApp.mTriggerDownloadProgress(1234);
+ assert.equal(1234, MockUpdateManager.mProgressCalledWith);
+ });
+
+ test('should send progress delta to update manager', function() {
+ mockApp.mTriggerDownloadProgress(1234);
+ mockApp.mTriggerDownloadProgress(2234);
+ assert.equal(1000, MockUpdateManager.mProgressCalledWith);
+ });
+ });
+
+ suite('ondownloadapplied', function() {
+ setup(function() {
+ mockApp.mTriggerDownloadAvailable();
+ mockApp.mTriggerDownloadApplied();
+ });
+
+ testCleanup();
+ });
+ });
+
+ suite('system update events', function() {
+ setup(function() {
+ subject = new SystemUpdatable(42);
+ subject._dispatchEvent = fakeDispatchEvent;
+ subject.download();
+ });
+
+ suite('update-downloaded', function() {
+ setup(function() {
+ asyncStorage.setItem(SystemUpdatable.KNOWN_UPDATE_FLAG, true);
+ var event = new MockChromeEvent({
+ type: 'update-downloaded'
+ });
+ subject.handleEvent(event);
+ });
+
+ test('should reset the downloading flag', function() {
+ assert.isFalse(subject.downloading);
+ });
+
+ test('should reset SystemUpdatable.KNOWN_UPDATE_FLAG', function() {
+ assert.isUndefined(asyncStorage.mItems[SystemUpdatable.KNOWN_UPDATE_FLAG]);
+ });
+
+ testSystemApplyPrompt();
+ });
+
+ suite('update-prompt-apply', function() {
+ setup(function() {
+ asyncStorage.setItem(SystemUpdatable.KNOWN_UPDATE_FLAG, true);
+ MockUtilityTray.show();
+ var event = new MockChromeEvent({
+ type: 'update-prompt-apply'
+ });
+ subject.handleEvent(event);
+ });
+
+ test('should reset SystemUpdatable.KNOWN_UPDATE_FLAG', function() {
+ assert.isUndefined(asyncStorage.mItems[SystemUpdatable.KNOWN_UPDATE_FLAG]);
+ });
+
+ testSystemApplyPrompt();
+ });
+
+ suite('update-error', function() {
+ setup(function() {
+ subject = new SystemUpdatable(42);
+ var event = new MockChromeEvent({
+ type: 'update-error'
+ });
+ subject.handleEvent(event);
+ });
+
+ test('should request error banner', function() {
+ assert.isTrue(MockUpdateManager.mErrorBannerRequested);
+ });
+
+ test('should remove self from active downloads', function() {
+ assert.isNotNull(MockUpdateManager.mLastDownloadsRemoval);
+ assert.equal(subject, MockUpdateManager.mLastDownloadsRemoval);
+ });
+
+ test('should remove the downloading flag', function() {
+ assert.isFalse(subject.downloading);
+ });
+ });
+
+ suite('update download events', function() {
+ var event;
+ setup(function() {
+ subject = new SystemUpdatable(98734);
+ subject.download();
+ });
+
+ suite('when the download starts', function() {
+ setup(function() {
+ event = new MockChromeEvent({
+ type: 'update-download-started',
+ total: 98734
+ });
+ });
+
+ test('should clear paused flag', function() {
+ subject.paused = true;
+ subject.handleEvent(event);
+ assert.isFalse(subject.paused);
+ });
+ });
+
+ suite('when the download receives progress', function() {
+ setup(function() {
+ event = new MockChromeEvent({
+ type: 'update-download-progress',
+ progress: 1234,
+ total: 98734
+ });
+ });
+
+ test('should send progress to update manager', function() {
+ subject.handleEvent(event);
+ assert.equal(1234, MockUpdateManager.mProgressCalledWith);
+ });
+
+ test('should send progress delta to update manager', function() {
+ subject.handleEvent(event);
+ event.detail.progress = 2234;
+ subject.handleEvent(event);
+ assert.equal(1000, MockUpdateManager.mProgressCalledWith);
+ });
+ });
+
+ suite('when the download is paused', function() {
+ setup(function() {
+ asyncStorage.setItem(SystemUpdatable.KNOWN_UPDATE_FLAG, true);
+ event = new MockChromeEvent({
+ type: 'update-download-stopped',
+ paused: true
+ });
+ subject.handleEvent(event);
+ });
+
+ test('should set the paused flag', function() {
+ assert.isTrue(subject.paused);
+ });
+ test('shouldn\'t signal "started uncompressing"', function() {
+ assert.isFalse(MockUpdateManager.mStartedUncompressingCalled);
+ });
+ test('should not reset SystemUpdatable.KNOWN_UPDATE_FLAG', function() {
+ assert.isTrue(asyncStorage.mItems[SystemUpdatable.KNOWN_UPDATE_FLAG]);
+ });
+ });
+
+ suite('when the download is complete', function() {
+ setup(function() {
+ asyncStorage.setItem(SystemUpdatable.KNOWN_UPDATE_FLAG, true);
+ event = new MockChromeEvent({
+ type: 'update-download-stopped',
+ paused: false
+ });
+ subject.handleEvent(event);
+ });
+
+ test('should clear the paused flag', function() {
+ assert.isFalse(subject.paused);
+ });
+
+ test('should signal the UpdateManager', function() {
+ assert.isTrue(MockUpdateManager.mStartedUncompressingCalled);
+ });
+ test('should not reset SystemUpdatable.KNOWN_UPDATE_FLAG', function() {
+ assert.isTrue(asyncStorage.mItems[SystemUpdatable.KNOWN_UPDATE_FLAG]);
+ });
+ });
+ });
+ });
+ });
+
+
+ function testSystemApplyPrompt() {
+ test('apply prompt shown', function() {
+ assert.isTrue(MockCustomDialog.mShown);
+ assert.equal('systemUpdateReady', MockCustomDialog.mShowedTitle);
+ assert.equal('wantToInstall', MockCustomDialog.mShowedMsg);
+
+ assert.equal('later', MockCustomDialog.mShowedCancel.title);
+ assert.equal('installNow', MockCustomDialog.mShowedConfirm.title);
+ });
+
+ test('utility tray hidden', function() {
+ assert.isFalse(MockUtilityTray.mShown);
+ });
+
+ test('apply prompt cancel callback', function() {
+ assert.equal(subject.declineInstall.name,
+ MockCustomDialog.mShowedCancel.callback.name);
+
+ subject.declineInstall();
+ assert.isFalse(MockCustomDialog.mShown);
+
+ assert.equal('update-prompt-apply-result', lastDispatchedEvent.type);
+ assert.equal('wait', lastDispatchedEvent.value);
+ });
+
+ test('canceling should remove from downloads queue', function() {
+ subject.declineInstall();
+
+ assert.isNotNull(MockUpdateManager.mLastDownloadsRemoval);
+ assert.equal(subject, MockUpdateManager.mLastDownloadsRemoval);
+ });
+
+ test('apply prompt confirm callback', function() {
+ assert.equal(subject.acceptInstall.name,
+ MockCustomDialog.mShowedConfirm.callback.name);
+
+ subject.acceptInstall();
+ assert.isFalse(MockCustomDialog.mShown);
+
+ assert.equal('update-prompt-apply-result', lastDispatchedEvent.type);
+ assert.equal('restart', lastDispatchedEvent.value);
+ });
+ }
+});
diff --git a/apps/system/test/unit/update_manager_test.js b/apps/system/test/unit/update_manager_test.js
new file mode 100644
index 0000000..2b2ef4c
--- /dev/null
+++ b/apps/system/test/unit/update_manager_test.js
@@ -0,0 +1,1449 @@
+'use strict';
+
+requireApp('system/js/update_manager.js');
+
+requireApp('system/test/unit/mock_app.js');
+requireApp('system/test/unit/mock_updatable.js');
+requireApp('system/test/unit/mock_apps_mgmt.js');
+requireApp('system/test/unit/mock_custom_dialog.js');
+requireApp('system/test/unit/mock_utility_tray.js');
+requireApp('system/test/unit/mock_system_banner.js');
+requireApp('system/test/unit/mock_chrome_event.js');
+requireApp('system/test/unit/mock_settings_listener.js');
+requireApp('system/test/unit/mock_statusbar.js');
+requireApp('system/test/unit/mock_notification_screen.js');
+requireApp('system/test/unit/mock_navigator_settings.js');
+requireApp('system/test/unit/mock_navigator_wake_lock.js');
+requireApp('system/test/unit/mock_navigator_moz_mobile_connection.js');
+requireApp('system/test/unit/mock_l10n.js');
+requireApp('system/test/unit/mock_asyncStorage.js');
+
+requireApp('system/test/unit/mocks_helper.js');
+
+var mocksForUpdateManager = [
+ 'StatusBar',
+ 'SystemBanner',
+ 'NotificationScreen',
+ 'UtilityTray',
+ 'CustomDialog',
+ 'SystemUpdatable',
+ 'AppUpdatable',
+ 'SettingsListener',
+ 'asyncStorage'
+];
+
+mocksForUpdateManager.forEach(function(mockName) {
+ if (! window[mockName]) {
+ window[mockName] = null;
+ }
+});
+
+suite('system/UpdateManager', function() {
+ var realL10n;
+ var realWifiManager;
+ var realRequestWakeLock;
+ var realNavigatorSettings;
+ var realDispatchEvent;
+
+ var apps;
+ var updatableApps;
+ var uAppWithDownloadAvailable;
+ var appWithDownloadAvailable;
+ var fakeNode;
+ var fakeToaster;
+ var fakeDialog;
+ var fakeWarning;
+
+ var tinyTimeout = 10;
+ var lastDispatchedEvent = null;
+
+ var mocksHelper;
+
+ suiteSetup(function() {
+ realNavigatorSettings = navigator.mozSettings;
+ navigator.mozSettings = MockNavigatorSettings;
+
+ realL10n = navigator.mozL10n;
+ navigator.mozL10n = MockL10n;
+
+ realWifiManager = navigator.mozWifiManager;
+ navigator.mozWifiManager = {
+ connection: {
+ status: 'connected'
+ }
+ };
+
+ realRequestWakeLock = navigator.requestWakeLock;
+ navigator.requestWakeLock = MockNavigatorWakeLock.requestWakeLock;
+
+ realDispatchEvent = UpdateManager._dispatchEvent;
+ UpdateManager._dispatchEvent = function fakeDispatch(type, value) {
+ lastDispatchedEvent = {
+ type: type,
+ value: value
+ };
+ };
+
+ mocksHelper = new MocksHelper(mocksForUpdateManager);
+ mocksHelper.suiteSetup();
+
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = 0;
+ UpdateManager.TOASTER_TIMEOUT = 0;
+ });
+
+ suiteTeardown(function() {
+ navigator.mozSettings = realNavigatorSettings;
+ realNavigatorSettings = null;
+
+ navigator.mozL10n = realL10n;
+ navigator.mozWifiManager = realWifiManager;
+ navigator.requestWakeLock = realRequestWakeLock;
+ realRequestWakeLock = null;
+
+ UpdateManager._dispatchEvent = realDispatchEvent;
+
+ mocksHelper.suiteTeardown();
+ });
+
+ setup(function() {
+ UpdateManager._mgmt = MockAppsMgmt;
+
+ apps = [new MockApp(), new MockApp(), new MockApp()];
+ updatableApps = apps.map(function(app) {
+ return new AppUpdatable(app);
+ });
+ MockAppsMgmt.mApps = apps;
+
+ uAppWithDownloadAvailable = updatableApps[2];
+ appWithDownloadAvailable = apps[2];
+ appWithDownloadAvailable.downloadAvailable = true;
+
+ fakeNode = document.createElement('div');
+ fakeNode.id = 'update-manager-container';
+ fakeNode.innerHTML = [
+ '<div class="icon">',
+ '</div>',
+ '<div class="activity">',
+ '</div>',
+ '<div class="message">',
+ '</div>'
+ ].join('');
+
+ fakeToaster = document.createElement('div');
+ fakeToaster.id = 'update-manager-toaster';
+ fakeToaster.innerHTML = [
+ '<div class="icon">',
+ '</div>',
+ '<div class="message">',
+ '</div>'
+ ].join('');
+
+ fakeDialog = document.createElement('form');
+ fakeDialog.id = 'updates-download-dialog';
+ fakeDialog.innerHTML = [
+ '<section>',
+ '<h1>',
+ 'Updates',
+ '</h1>',
+ '<ul>',
+ '</ul>',
+ '<menu>',
+ '<button id="updates-later-button" type="reset">',
+ 'Later',
+ '</button>',
+ '<button id="updates-download-button" type="submit">',
+ 'Download',
+ '</button>',
+ '</menu>',
+ '</section>'
+ ].join('');
+
+ fakeWarning = document.createElement('form');
+ fakeWarning.id = 'updates-viaDataConnection-dialog';
+ fakeWarning.innerHTML = [
+ '<section>',
+ '<h1>',
+ 'Updates',
+ '</h1>',
+ '<p>',
+ '</p>',
+ '<menu>',
+ '<button id="updates-viaDataConnection-notnow-button" type="reset">',
+ 'Not Now',
+ '</button>',
+ '<button id="updates-viaDataConnection-download-button" type="submit">',
+ 'Download',
+ '</button>',
+ '</menu>',
+ '</section>'
+ ].join('');
+
+ document.body.appendChild(fakeNode);
+ document.body.appendChild(fakeToaster);
+ document.body.appendChild(fakeDialog);
+ document.body.appendChild(fakeWarning);
+
+ mocksHelper.setup();
+ });
+
+ teardown(function(done) {
+ // We wait for the nextTick in order to let the UpdateManger's
+ // timeouts finish (they are all set to 0)
+ setTimeout(function() {
+ UpdateManager.updatableApps = [];
+ UpdateManager.systemUpdatable = null;
+ UpdateManager.updatesQueue = [];
+ UpdateManager.downloadsQueue = [];
+ UpdateManager._downloading = false;
+ UpdateManager._uncompressing = false;
+ UpdateManager.container = null;
+ UpdateManager.message = null;
+ UpdateManager.toaster = null;
+ UpdateManager.toasterMessage = null;
+ UpdateManager.laterButton = null;
+ UpdateManager.downloadButton = null;
+ UpdateManager.downloadDialog = null;
+ UpdateManager.downloadDialogTitle = null;
+ UpdateManager.downloadDialogList = null;
+ UpdateManager.lastUpdatesAvailable = 0;
+
+ MockAppsMgmt.mTeardown();
+
+ mocksHelper.teardown();
+
+ fakeNode.parentNode.removeChild(fakeNode);
+ fakeToaster.parentNode.removeChild(fakeToaster);
+ fakeDialog.parentNode.removeChild(fakeDialog);
+
+ lastDispatchedEvent = null;
+ MockNavigatorWakeLock.mTeardown();
+ MockNavigatorSettings.mTeardown();
+
+ done();
+ });
+ });
+
+ suite('init', function() {
+ test('should get all applications', function(done) {
+ MockAppsMgmt.mNext = function() {
+ done();
+ };
+ UpdateManager.init();
+ });
+
+ test('should create AppUpdatable on init', function(done) {
+ MockAppUpdatable.mTeardown();
+
+ MockAppsMgmt.mNext = function() {
+ assert.equal(MockAppUpdatable.mCount, apps.length);
+ done();
+ };
+ UpdateManager.init();
+ });
+
+ test('should bind dom elements', function() {
+ UpdateManager.init();
+ assert.equal('update-manager-container', UpdateManager.container.id);
+ assert.equal('message', UpdateManager.message.className);
+
+ assert.equal('update-manager-toaster', UpdateManager.toaster.id);
+ assert.equal('message', UpdateManager.toasterMessage.className);
+
+ assert.equal('updates-later-button', UpdateManager.laterButton.id);
+ assert.equal('updates-download-button', UpdateManager.downloadButton.id);
+ assert.equal('updates-download-dialog', UpdateManager.downloadDialog.id);
+ assert.equal('updates-viaDataConnection-dialog',
+ UpdateManager.downloadViaDataConnectionDialog.id);
+ assert.equal('updates-viaDataConnection-notnow-button',
+ UpdateManager.notnowButton.id);
+ assert.equal('updates-viaDataConnection-download-button',
+ UpdateManager.downloadViaDataConnectionButton.id);
+ assert.equal('H1', UpdateManager.downloadDialogTitle.tagName);
+ assert.equal('UL', UpdateManager.downloadDialogList.tagName);
+ });
+
+ test('should bind to the click event', function() {
+ UpdateManager.init();
+ assert.equal(UpdateManager.containerClicked.name,
+ UpdateManager.container.onclick.name);
+
+ assert.equal(UpdateManager.requestDownloads.name,
+ UpdateManager.downloadButton.onclick.name);
+
+ assert.equal(UpdateManager.cancelPrompt.name,
+ UpdateManager.laterButton.onclick.name);
+
+ assert.equal(UpdateManager.cancelDataConnectionUpdatesPrompt.name,
+ UpdateManager.notnowButton.onclick.name);
+
+ assert.equal(UpdateManager.requestDownloads.name,
+ UpdateManager.downloadViaDataConnectionButton.onclick.name);
+ });
+ });
+
+ suite('events', function() {
+ suite('app install', function() {
+ var installedApp;
+
+ setup(function() {
+ MockAppUpdatable.mTeardown();
+
+ UpdateManager.init();
+
+ installedApp = new MockApp();
+ installedApp.downloadAvailable = true;
+ MockAppsMgmt.mTriggerOninstall(installedApp);
+ });
+
+ test('should instantiate an updatable app', function() {
+ assert.equal(MockAppUpdatable.mCount, 1);
+ });
+ });
+
+ suite('app uninstall', function() {
+ var partialApp;
+
+ setup(function() {
+ UpdateManager.init();
+ UpdateManager.updatableApps = updatableApps;
+ UpdateManager.addToUpdatesQueue(uAppWithDownloadAvailable);
+
+ partialApp = {
+ origin: appWithDownloadAvailable.origin,
+ manifestURL: appWithDownloadAvailable.manifestURL
+ };
+ });
+
+ test('should remove the updatable app', function() {
+ var initialLength = UpdateManager.updatableApps.length;
+ MockAppsMgmt.mTriggerOnuninstall(partialApp);
+ assert.equal(initialLength - 1, UpdateManager.updatableApps.length);
+ });
+
+ test('should remove from the update queue', function() {
+ var initialLength = UpdateManager.updatesQueue.length;
+ MockAppsMgmt.mTriggerOnuninstall(partialApp);
+ assert.equal(initialLength - 1, UpdateManager.updatesQueue.length);
+ });
+
+ test('should remove from the update queue even if no downloadavailable',
+ function() {
+ uAppWithDownloadAvailable.app.downloadAvailable = false;
+ var initialLength = UpdateManager.updatesQueue.length;
+ MockAppsMgmt.mTriggerOnuninstall(partialApp);
+ assert.equal(initialLength - 1, UpdateManager.updatesQueue.length);
+ });
+
+ test('should call uninit on the updatable', function() {
+ var lastIndex = UpdateManager.updatesQueue.length - 1;
+ var updatableApp = UpdateManager.updatesQueue[lastIndex];
+ MockAppsMgmt.mTriggerOnuninstall(partialApp);
+ assert.isTrue(updatableApp.mUninitCalled);
+ });
+ });
+
+ suite('system update available', function() {
+ var event;
+
+ setup(function() {
+ UpdateManager.init();
+ event = new MockChromeEvent({
+ type: 'update-available',
+ size: 42
+ });
+ UpdateManager.handleEvent(event);
+ });
+
+ test('should add a system updatable to the updates', function() {
+ var lastIndex = UpdateManager.updatesQueue.length - 1;
+ assert.equal(undefined, UpdateManager.updatesQueue[lastIndex].app);
+ });
+
+ test('should init the updatable with the download size', function() {
+ var lastIndex = UpdateManager.updatesQueue.length - 1;
+ assert.equal(42, UpdateManager.updatesQueue[lastIndex].size);
+ });
+
+ test('should not add or instanciate a system updatable if there is one',
+ function() {
+ var initialLength = UpdateManager.updatesQueue.length;
+
+ UpdateManager.handleEvent(event);
+
+ assert.equal(UpdateManager.updatesQueue.length, initialLength);
+ assert.equal(MockSystemUpdatable.mInstancesCount, 1);
+ });
+
+ test('should remember that update is available', function() {
+ assert.isTrue(UpdateManager.systemUpdatable.mKnownUpdate);
+ });
+ });
+
+ suite('no system update available', function() {
+ setup(function() {
+ UpdateManager.init();
+ });
+
+ test('should not remember about the update', function() {
+ assert.isUndefined(UpdateManager.systemUpdatable.mKnownUpdate);
+ });
+ });
+
+ });
+
+ suite('UI', function() {
+ setup(function() {
+ MockAppsMgmt.mApps = [];
+ UpdateManager.init();
+ UpdateManager.updatableApps = updatableApps;
+ });
+
+ suite('downloading state', function() {
+ test('should add the css class if downloading', function() {
+ UpdateManager._downloading = true;
+ UpdateManager.render();
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('downloading'));
+ });
+
+ test('should remove the css class if not downloading', function() {
+ UpdateManager._downloading = true;
+ UpdateManager.render();
+
+ UpdateManager._downloading = false;
+ UpdateManager.render();
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('downloading'));
+ });
+
+ test('should show the downloading progress if downloading', function() {
+ UpdateManager._downloading = true;
+ UpdateManager.render();
+ assert.equal('downloadingUpdateMessage{"progress":"0.00 bytes"}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should not show the toaster if downloading', function(done) {
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = tinyTimeout;
+ UpdateManager.TOASTER_TIMEOUT = tinyTimeout;
+ UpdateManager._downloading = true;
+ UpdateManager.addToUpdatesQueue(uAppWithDownloadAvailable);
+
+ setTimeout(function() {
+ var css = UpdateManager.toaster.classList;
+ assert.isFalse(css.contains('displayed'));
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = 0;
+ UpdateManager.TOASTER_TIMEOUT = 0;
+ done();
+ }, tinyTimeout * 1.5);
+ });
+
+ test('should show the available message if not downloading', function() {
+ UpdateManager.updatesQueue = updatableApps;
+ UpdateManager.render();
+ assert.equal('updateAvailableInfo{"n":3}',
+ UpdateManager.message.textContent);
+ });
+ });
+
+ suite('progress display', function() {
+ setup(function() {
+ UpdateManager.updatesQueue = [uAppWithDownloadAvailable];
+
+ var evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+ UpdateManager.startDownloads(evt);
+
+ UpdateManager.addToDownloadsQueue(uAppWithDownloadAvailable);
+
+ UpdateManager.downloadProgressed(1234);
+ });
+
+ test('downloadedBytes should be reset by startDownloads', function() {
+ var evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+ UpdateManager.startDownloads(evt);
+
+ assert.equal('downloadingUpdateMessage{"progress":"0.00 bytes"}',
+ UpdateManager.message.textContent);
+ });
+
+ test('downloadedBytes should be reset when stopping the download',
+ function() {
+
+ UpdateManager.removeFromDownloadsQueue(uAppWithDownloadAvailable);
+ UpdateManager.addToDownloadsQueue(uAppWithDownloadAvailable);
+
+ assert.equal('downloadingUpdateMessage{"progress":"0.00 bytes"}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should increment the downloadedBytes', function() {
+ UpdateManager.downloadProgressed(100);
+ assert.equal('downloadingUpdateMessage{"progress":"1.30 kB"}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should not update if bytes <= 0', function() {
+ UpdateManager.downloadProgressed(-100);
+ assert.equal('downloadingUpdateMessage{"progress":"1.21 kB"}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should display the notification', function() {
+ assert.isTrue(fakeNode.classList.contains('displayed'));
+ });
+
+ });
+
+ suite('uncompress display', function() {
+ var systemUpdatable;
+
+ setup(function() {
+ systemUpdatable = new MockSystemUpdatable();
+ });
+
+ suite('when we only have the system update', function() {
+ setup(function() {
+ UpdateManager.addToUpdatesQueue(systemUpdatable);
+ UpdateManager.addToDownloadsQueue(systemUpdatable);
+ UpdateManager.startedUncompressing();
+ });
+
+ test('should render in uncompressing mode', function() {
+ assert.equal(UpdateManager.message.textContent,
+ 'uncompressingMessage');
+ });
+ });
+
+ suite('when we have various ongoing updates', function() {
+ setup(function() {
+ UpdateManager.addToUpdatableApps(uAppWithDownloadAvailable);
+ UpdateManager.addToUpdatesQueue(uAppWithDownloadAvailable);
+ UpdateManager.addToDownloadsQueue(uAppWithDownloadAvailable);
+
+ UpdateManager.addToUpdatesQueue(systemUpdatable);
+ UpdateManager.addToDownloadsQueue(systemUpdatable);
+
+ UpdateManager.startedUncompressing();
+ });
+
+ test('should stay in downloading mode', function() {
+ assert.include(UpdateManager.message.textContent,
+ 'downloadingUpdateMessage');
+ });
+
+ suite('once the app updates are done', function() {
+ setup(function() {
+ UpdateManager.removeFromDownloadsQueue(uAppWithDownloadAvailable);
+ UpdateManager.removeFromUpdatesQueue(uAppWithDownloadAvailable);
+ });
+
+ test('should render in uncompressing mode', function() {
+ assert.equal(UpdateManager.message.textContent,
+ 'uncompressingMessage');
+ });
+ });
+ });
+ });
+
+ suite('container visibility', function() {
+ suiteSetup(function() {
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = tinyTimeout;
+ UpdateManager.TOASTER_TIMEOUT = tinyTimeout;
+ });
+
+ suiteTeardown(function() {
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = 0;
+ UpdateManager.TOASTER_TIMEOUT = 0;
+ });
+
+ setup(function() {
+ UpdateManager.addToUpdatesQueue(uAppWithDownloadAvailable);
+ });
+
+ teardown(function(done) {
+ // wait for all actions to happen in UpdateManager before reseting
+ setTimeout(function() {
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ suite('notification behavior after addToDownloadsQueue', function() {
+ setup(function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ UpdateManager.addToDownloadsQueue(uAppWithDownloadAvailable);
+ });
+
+ test('should be displayed only once', function() {
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('displayed'));
+ assert.equal(MockNotificationScreen.wasMethodCalled['incExternalNotifications'], 1);
+ });
+
+ test('should not be displayed after timeout', function(done) {
+ setTimeout(function() {
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('displayed'));
+ assert.equal(MockNotificationScreen.wasMethodCalled['incExternalNotifications'], 1);
+ done();
+ }, tinyTimeout * 2);
+
+ });
+ });
+
+ suite('notification behavior after addToDownloadsQueue after timeout', function() {
+ setup(function(done) {
+ setTimeout(function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ UpdateManager.addToDownloadsQueue(uAppWithDownloadAvailable);
+ done();
+ });
+ });
+
+ test('should not increment the counter if already displayed', function() {
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('displayed'));
+ assert.equal(MockNotificationScreen.wasMethodCalled['incExternalNotifications'], 1);
+ });
+ });
+
+ suite('displaying the container after a timeout', function() {
+ setup(function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ });
+
+ test('should display after a timeout', function(done) {
+ setTimeout(function() {
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('displayed'));
+ assert.equal(MockNotificationScreen.wasMethodCalled['incExternalNotifications'], 1);
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should not display if there are no more updates', function(done) {
+ UpdateManager.updatesQueue.forEach(function(uApp) {
+ UpdateManager.removeFromUpdatesQueue(uApp);
+ });
+
+ setTimeout(function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should display an updated count', function(done) {
+ UpdateManager.addToUpdatesQueue(updatableApps[1]);
+ setTimeout(function() {
+ assert.equal('updateAvailableInfo{"n":2}',
+ UpdateManager.message.textContent);
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ suite('update toaster', function() {
+ test('should display after a timeout', function(done) {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ setTimeout(function() {
+ var css = UpdateManager.toaster.classList;
+ assert.isTrue(css.contains('displayed'));
+ assert.equal('updateAvailableInfo{"n":1}',
+ UpdateManager.toasterMessage.textContent);
+ done();
+ }, tinyTimeout * 1.5);
+ });
+
+ test('should reset toaster value when notification was activated', function(done) {
+ setTimeout(function() {
+ UpdateManager.addToUpdatesQueue(updatableApps[1]);
+ assert.equal('updateAvailableInfo{"n":1}',
+ UpdateManager.toasterMessage.textContent);
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should show the right message', function(done) {
+ setTimeout(function() {
+ assert.equal('updateAvailableInfo{"n":1}',
+ UpdateManager.toasterMessage.textContent);
+ done();
+ }, tinyTimeout * 2);
+ });
+
+
+ test('should hide after TOASTER_TIMEOUT', function(done) {
+ UpdateManager.addToUpdatesQueue(updatableApps[1]);
+ setTimeout(function() {
+ setTimeout(function() {
+ var css = UpdateManager.toaster.classList;
+ assert.isFalse(css.contains('displayed'));
+ done();
+ }, tinyTimeout * 2);
+ }, tinyTimeout * 2);
+ });
+
+ });
+
+ test('should add a new statusbar notification', function(done) {
+ var method1 = 'incExternalNotifications';
+ setTimeout(function() {
+ assert.ok(MockNotificationScreen.wasMethodCalled[method1]);
+ done();
+ }, tinyTimeout * 2);
+ });
+ });
+
+ suite('no more updates', function() {
+ setup(function() {
+ UpdateManager.container.classList.add('displayed');
+ UpdateManager.updatesQueue = [uAppWithDownloadAvailable];
+ UpdateManager.removeFromUpdatesQueue(uAppWithDownloadAvailable);
+ });
+
+ test('should hide the container', function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('displayed'));
+ });
+
+ test('should decrease the external notifications count', function() {
+ var method1 = 'decExternalNotifications';
+ assert.ok(MockNotificationScreen.wasMethodCalled[method1]);
+ });
+ });
+ });
+
+ suite('after downloads', function() {
+ test('should check if new updates where found', function() {
+ var uApp = updatableApps[0];
+
+ UpdateManager.updatableApps = updatableApps;
+ UpdateManager.downloadsQueue = [uApp];
+
+ UpdateManager.removeFromDownloadsQueue(uApp);
+ assert.equal(uAppWithDownloadAvailable.app.mId,
+ UpdateManager.updatesQueue[0].app.mId);
+ });
+ });
+
+ suite('error banner requests', function() {
+ suiteSetup(function() {
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = tinyTimeout;
+ UpdateManager.TOASTER_TIMEOUT = tinyTimeout;
+ });
+
+ suiteTeardown(function() {
+ UpdateManager.NOTIFICATION_BUFFERING_TIMEOUT = 0;
+ UpdateManager.TOASTER_TIMEOUT = 0;
+ });
+
+ setup(function() {
+ UpdateManager.init();
+ UpdateManager.requestErrorBanner();
+ });
+
+ teardown(function(done) {
+ // wait for all actions to happen in UpdateManager before reseting
+ setTimeout(function() {
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should wait before showing the system banner', function(done) {
+ assert.equal(0, MockSystemBanner.mShowCount);
+
+ setTimeout(function() {
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should show after NOTIFICATION_BUFFERING_TIMEOUT', function(done) {
+ setTimeout(function() {
+ assert.equal(1, MockSystemBanner.mShowCount);
+ assert.equal('downloadError', MockSystemBanner.mMessage);
+ done();
+ }, tinyTimeout * 2);
+ });
+
+ test('should show only once if called multiple time', function(done) {
+ UpdateManager.requestErrorBanner();
+ setTimeout(function() {
+ assert.equal(1, MockSystemBanner.mShowCount);
+ done();
+ }, tinyTimeout * 2);
+ });
+ });
+
+ suite('humanizeSize', function() {
+ test('should handle 0', function() {
+ assert.equal('0.00 bytes', UpdateManager._humanizeSize(0));
+ });
+
+ test('should handle bytes size', function() {
+ assert.equal('42.00 bytes', UpdateManager._humanizeSize(42));
+ });
+
+ test('should handle kilobytes size', function() {
+ assert.equal('1.00 kB', UpdateManager._humanizeSize(1024));
+ });
+
+ test('should handle megabytes size', function() {
+ assert.equal('4.67 MB', UpdateManager._humanizeSize(4901024));
+ });
+
+ test('should handle gigabytes size', function() {
+ assert.equal('3.73 GB', UpdateManager._humanizeSize(4000901024));
+ });
+ });
+ });
+
+ suite('actions', function() {
+ setup(function() {
+ UpdateManager.init();
+ });
+
+ suite('start downloads', function() {
+ var systemUpdatable, appUpdatable, evt;
+
+ setup(function() {
+ UpdateManager.init();
+
+ systemUpdatable = new MockSystemUpdatable();
+
+ appUpdatable = new MockAppUpdatable(new MockApp());
+ appUpdatable.name = 'Angry birds';
+ appUpdatable.size = '423459';
+
+ UpdateManager.addToUpdatableApps(appUpdatable);
+ UpdateManager.addToUpdatesQueue(appUpdatable);
+ UpdateManager.addToUpdatesQueue(systemUpdatable);
+
+ UpdateManager.container.click();
+
+ evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+ });
+
+ suite('data connection warning', function() {
+ var downloadDialog;
+ setup(function() {
+ downloadDialog = UpdateManager.downloadDialog;
+ });
+
+ test('should switch the online data attribute when online',
+ function() {
+ downloadDialog.dataset.online = false;
+ window.dispatchEvent(new CustomEvent('online'));
+ assert.equal(downloadDialog.dataset.online, 'true');
+ });
+
+ test('should leave the online data attribute true when online',
+ function() {
+ downloadDialog.dataset.online = true;
+ window.dispatchEvent(new CustomEvent('online'));
+ assert.equal(downloadDialog.dataset.online, 'true');
+ });
+
+ test('should switch the nowifi data attribute when connected',
+ function() {
+ downloadDialog.dataset.nowifi = true;
+ window.dispatchEvent(new CustomEvent('wifi-statuschange'));
+ assert.equal(downloadDialog.dataset.nowifi, 'false');
+ });
+
+ test('should switch the nowifi data attribute when disconnected',
+ function() {
+ downloadDialog.dataset.nowifi = false;
+ navigator.mozWifiManager.connection.status = 'disconnected';
+ window.dispatchEvent(new CustomEvent('wifi-statuschange'));
+ assert.equal(downloadDialog.dataset.nowifi, 'true');
+ });
+ });
+
+ test('should enable the download button', function() {
+ var downloadButton = UpdateManager.downloadButton;
+ assert.isFalse(downloadButton.disabled);
+ });
+
+ suite('with all the checkboxes checked', function() {
+ setup(function() {
+ UpdateManager.startDownloads(evt);
+ });
+
+ test('should download system updates', function() {
+ assert.isTrue(systemUpdatable.mDownloadCalled);
+ });
+
+ test('should call download on checked app updatables', function() {
+ assert.isTrue(appUpdatable.mDownloadCalled);
+ });
+ });
+
+ suite('with no checkbox checked', function() {
+ setup(function() {
+ var dialog = UpdateManager.downloadDialogList;
+ var checkboxes = dialog.querySelectorAll('input[type="checkbox"]');
+ for (var i = 0; i < checkboxes.length; i++) {
+ var checkbox = checkboxes[i];
+ if (checkbox.checked) {
+ checkbox.click();
+ }
+ }
+
+ UpdateManager.startDownloads(evt);
+ });
+
+ test('the download button should be enabled', function() {
+ assert.isFalse(UpdateManager.downloadButton.disabled);
+ });
+
+ test('should still download system updates', function() {
+ assert.isTrue(systemUpdatable.mDownloadCalled);
+ });
+
+ test('should not call download on unchecked app updatables',
+ function() {
+ assert.isFalse(appUpdatable.mDownloadCalled);
+ });
+ });
+
+ suite('with only app updates', function() {
+ setup(function() {
+ UpdateManager.removeFromUpdatesQueue(systemUpdatable);
+ UpdateManager.container.click();
+ });
+
+ suite('unchecking all the checkboxes', function() {
+ var dialog, downloadButton;
+
+ setup(function() {
+ dialog = UpdateManager.downloadDialogList;
+ var checkboxes = dialog.querySelectorAll('input[type="checkbox"]');
+ for (var i = 0; i < checkboxes.length; i++) {
+ var checkbox = checkboxes[i];
+ if (checkbox.checked) {
+ checkboxes[i].click();
+ }
+ }
+
+ downloadButton = UpdateManager.downloadButton;
+ });
+
+ test('should disable the download button', function() {
+ assert.isTrue(downloadButton.disabled);
+ });
+
+ suite('then checking one back', function() {
+ setup(function() {
+ var checkbox = dialog.querySelector('input[type="checkbox"]');
+ checkbox.click();
+ });
+
+ test('should enable the download button back', function() {
+ assert.isFalse(downloadButton.disabled);
+ });
+ });
+ });
+ });
+ });
+
+ suite('cancel all downloads', function() {
+ var systemUpdatable;
+
+ setup(function() {
+ systemUpdatable = new MockSystemUpdatable();
+ UpdateManager.updatableApps = updatableApps;
+ [systemUpdatable, uAppWithDownloadAvailable].forEach(function(updatable) {
+ UpdateManager.addToUpdatesQueue(updatable);
+ UpdateManager.addToDownloadsQueue(updatable);
+ });
+
+ UpdateManager.cancelAllDownloads();
+ });
+
+ test('should call cancelDownload on the app updatables', function() {
+ assert.isTrue(uAppWithDownloadAvailable.mCancelCalled);
+ });
+
+ test('should call cancelDownload on the system updatable', function() {
+ assert.isTrue(systemUpdatable.mCancelCalled);
+ });
+
+ test('should empty the downloads queue', function() {
+ assert.equal(UpdateManager.downloadsQueue.length, 0);
+ });
+
+ test('should leave the updates available', function() {
+ assert.equal(UpdateManager.updatesQueue.length, 2);
+ });
+ });
+
+ suite('download prompt', function() {
+ setup(function() {
+ MockUtilityTray.show();
+ var systemUpdatable = new MockSystemUpdatable();
+ systemUpdatable.size = 5296345;
+ var appUpdatable = new MockAppUpdatable(new MockApp());
+ appUpdatable.name = 'Angry birds';
+ appUpdatable.size = '423459';
+ var hostedAppUpdatable = new MockAppUpdatable(new MockApp());
+ hostedAppUpdatable.name = 'Twitter';
+ UpdateManager.updatesQueue = [hostedAppUpdatable, appUpdatable,
+ systemUpdatable];
+ UpdateManager.containerClicked();
+ UpdateManager._isDataConnectionWarningDialogEnabled = true;
+ UpdateManager.downloadDialog.dataset.nowifi = false;
+ });
+
+ suite('download prompt', function() {
+ test('should hide the utility tray', function() {
+ assert.isFalse(MockUtilityTray.mShown);
+ });
+
+ test('should show the download dialog', function() {
+ var css = UpdateManager.downloadDialog.classList;
+ assert.isTrue(css.contains('visible'));
+ });
+
+ test('should set the title', function() {
+ var title = fakeDialog.querySelector('h1');
+ assert.equal('numberOfUpdates{"n":3}', title.textContent);
+ });
+
+ suite('update list rendering', function() {
+ test('should create an item for each update', function() {
+ assert.equal(3, UpdateManager.downloadDialogList.children.length);
+ });
+
+ test('should render system update item first with required',
+ function() {
+ var item = UpdateManager.downloadDialogList.children[0];
+
+ assert.include(item.textContent, 'systemUpdate');
+ assert.include(item.textContent, '5.05 MB');
+ assert.include(item.textContent, 'required');
+ });
+
+ test('should render packaged app items alphabetically with checkbox',
+ function() {
+ var item = UpdateManager.downloadDialogList.children[1];
+
+ assert.include(item.textContent, 'Angry birds');
+ assert.include(item.textContent, '413.53 kB');
+
+ var checkbox = item.querySelector('input');
+ assert.equal(checkbox.type, 'checkbox');
+ assert.isTrue(checkbox.checked);
+ assert.equal(checkbox.dataset.position, '1');
+ });
+
+ test('should render hosted app items alphabetically with checkbox',
+ function() {
+ var item = UpdateManager.downloadDialogList.children[2];
+
+ assert.include(item.textContent, 'Twitter');
+
+ var checkbox = item.querySelector('input');
+ assert.equal(checkbox.type, 'checkbox');
+ assert.isTrue(checkbox.checked);
+ assert.equal(checkbox.dataset.position, '2');
+ });
+ });
+ });
+
+ test('should handle clicking download in the data connection warning dialog', function() {
+ UpdateManager.downloadDialog.dataset.nowifi = true;
+
+ var evt = {
+ preventDefault: function() {},
+ type: 'click',
+ target: UpdateManager.downloadViaDataConnectionButton
+ };
+
+ UpdateManager.requestDownloads(evt);
+ MockasyncStorage.getItem('gaia.system.isDataConnectionWarningDialogEnabled', function(value) {
+ assert.isFalse(value);
+ });
+ assert.isFalse(UpdateManager._isDataConnectionWarningDialogEnabled);
+ assert.equal(UpdateManager.downloadDialog.dataset.dataConnectionInlineWarning, 'true');
+
+ MockasyncStorage.mTeardown();
+ });
+
+ test('should handle clicking download when using data connection in the first time', function() {
+ UpdateManager.downloadDialog.dataset.nowifi = true;
+
+ var evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+
+ UpdateManager.requestDownloads(evt);
+ var css = UpdateManager.downloadViaDataConnectionDialog.classList;
+ assert.isTrue(css.contains('visible'));
+ });
+
+ test('should handle clicking download when using wifi', function() {
+ UpdateManager._isDataConnectionWarningDialogEnabled = false;
+
+ var calledToMockStartDownloads = false;
+ var realStartDownloadsFunc = UpdateManager.startDownloads;
+ UpdateManager.startDownloads = function() {
+ calledToMockStartDownloads = true;
+ };
+
+ var evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+
+ UpdateManager.requestDownloads(evt);
+ assert.isTrue(calledToMockStartDownloads);
+
+ UpdateManager.startDownloads = realStartDownloadsFunc;
+ });
+
+ test('should handle cancellation on the data connection warning dialog', function() {
+ UpdateManager.cancelDataConnectionUpdatesPrompt();
+
+ var css = UpdateManager.downloadViaDataConnectionDialog.classList;
+ assert.isFalse(css.contains('visible'));
+ css = UpdateManager.downloadDialog.classList;
+ assert.isFalse(css.contains('visible'));
+ });
+
+ test('should handle cancellation', function() {
+ UpdateManager.cancelPrompt();
+
+ var css = UpdateManager.downloadDialog.classList;
+ assert.isFalse(css.contains('visible'));
+ });
+
+ test('should handle confirmation', function() {
+ UpdateManager._isDataConnectionWarningDialogEnabled = false;
+
+ var evt = document.createEvent('MouseEvents');
+ evt.initEvent('click', true, true);
+
+ UpdateManager.requestDownloads(evt);
+ var css = UpdateManager.downloadDialog.classList;
+ assert.isFalse(css.contains('visible'));
+ css = UpdateManager.downloadViaDataConnectionDialog.classList;
+ assert.isFalse(css.contains('visible'));
+ assert.isTrue(MockUtilityTray.mShown);
+ assert.isTrue(evt.defaultPrevented);
+ });
+ });
+
+ suite('cancel prompt', function() {
+ setup(function() {
+ UpdateManager._downloading = true;
+ MockUtilityTray.show();
+ UpdateManager.containerClicked();
+ });
+
+ test('should show the cancel', function() {
+ assert.isTrue(MockCustomDialog.mShown);
+ assert.isFalse(MockUtilityTray.mShown);
+
+ assert.equal('cancelAllDownloads', MockCustomDialog.mShowedTitle);
+ assert.equal('wantToCancelAll', MockCustomDialog.mShowedMsg);
+
+ assert.equal('no', MockCustomDialog.mShowedCancel.title);
+ assert.equal('yes', MockCustomDialog.mShowedConfirm.title);
+ });
+
+ test('should handle cancellation', function() {
+ assert.equal('um_cancelPrompt',
+ MockCustomDialog.mShowedCancel.callback.name);
+
+ UpdateManager.cancelPrompt();
+ assert.isFalse(MockCustomDialog.mShown);
+ });
+
+ test('should handle confirmation', function() {
+ assert.equal('um_cancelAllDownloads',
+ MockCustomDialog.mShowedConfirm.callback.name);
+
+ UpdateManager.cancelAllDownloads();
+ assert.isFalse(MockCustomDialog.mShown);
+ });
+ });
+
+ suite('check for updates', function() {
+ setup(function() {
+ UpdateManager.init();
+ });
+
+ test('should observe the setting', function() {
+ assert.equal('gaia.system.checkForUpdates', MockSettingsListener.mName);
+ assert.equal(false, MockSettingsListener.mDefaultValue);
+ assert.equal(UpdateManager.checkForUpdates.name,
+ MockSettingsListener.mCallback.name);
+ });
+
+ suite('when asked to check', function() {
+ setup(function() {
+ UpdateManager.checkForUpdates(true);
+ });
+
+ test('should dispatch force update event if asked for', function() {
+ assert.equal('force-update-check', lastDispatchedEvent.type);
+ });
+
+ test('should set the setting back to false', function() {
+ var setting = 'gaia.system.checkForUpdates';
+ assert.isFalse(MockNavigatorSettings.mSettings[setting]);
+ });
+ });
+
+ test('should not dispatch force update event if not asked', function() {
+ UpdateManager.checkForUpdates(false);
+ assert.isNull(lastDispatchedEvent);
+ });
+ });
+ });
+
+ suite('queues support', function() {
+ suite('updates queue', function() {
+ suite('addToUpdatesQueue', function() {
+ setup(function() {
+ var installedApp = new MockApp();
+ var updatableApp = new MockAppUpdatable(installedApp);
+
+ var pendingApp = new MockApp({ installState: 'pending' }),
+ uPendingApp = new MockAppUpdatable(pendingApp);
+
+ UpdateManager.updatableApps = [updatableApp, uPendingApp];
+ UpdateManager.init();
+ });
+
+ test('should add the updatable app to the array', function() {
+ var updatableApp = UpdateManager.updatableApps[0];
+
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal(initialLength + 1, UpdateManager.updatesQueue.length);
+ });
+
+ test('should render', function() {
+ var updatableApp = UpdateManager.updatableApps[0];
+
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal('updateAvailableInfo{"n":1}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should not add app if not in updatableApps array', function() {
+ var updatableApp = new MockAppUpdatable(new MockApp);
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal(initialLength, UpdateManager.updatesQueue.length);
+ });
+
+ test('should add a system update to the array', function() {
+ var systemUpdate = new MockSystemUpdatable();
+
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(systemUpdate);
+ assert.equal(initialLength + 1, UpdateManager.updatesQueue.length);
+ });
+
+ test('should not add more than one system update', function() {
+ var systemUpdate = new MockSystemUpdatable();
+
+ UpdateManager.updatesQueue.push(new MockSystemUpdatable());
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(systemUpdate);
+ assert.equal(initialLength, UpdateManager.updatesQueue.length);
+ });
+
+ test('should not add if app already in the array', function() {
+ var updatableApp = UpdateManager.updatableApps[0];
+ UpdateManager.addToUpdatesQueue(updatableApp);
+
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal(initialLength, UpdateManager.updatesQueue.length);
+ });
+
+ test('should not add if downloading', function() {
+ UpdateManager._downloading = true;
+ var updatableApp = UpdateManager.updatableApps[0];
+
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal(initialLength, UpdateManager.updatesQueue.length);
+ });
+
+ test('should not add a pending app to the array', function() {
+ var updatableApp = UpdateManager.updatableApps[1];
+
+ var initialLength = UpdateManager.updatesQueue.length;
+
+ UpdateManager.addToUpdatesQueue(updatableApp);
+ assert.equal(UpdateManager.updatesQueue.length, initialLength);
+ });
+
+ });
+
+ suite('removeFromUpdatesQueue', function() {
+ var updatableApp;
+
+ setup(function() {
+ var installedApp = new MockApp();
+ updatableApp = new MockAppUpdatable(installedApp);
+ UpdateManager.updatableApps = [updatableApp];
+ UpdateManager.updatesQueue = [updatableApp];
+ UpdateManager.init();
+ });
+
+ test('should remove if in updatesQueue array', function() {
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.removeFromUpdatesQueue(updatableApp);
+ assert.equal(initialLength - 1, UpdateManager.updatesQueue.length);
+ });
+
+ test('should render', function() {
+ UpdateManager.removeFromUpdatesQueue(updatableApp);
+ assert.equal('updateAvailableInfo{"n":0}',
+ UpdateManager.message.textContent);
+ });
+
+ test('should remove system updates too', function() {
+ var systemUpdate = new MockSystemUpdatable();
+ UpdateManager.updatesQueue.push(systemUpdate);
+
+ var initialLength = UpdateManager.updatesQueue.length;
+ UpdateManager.removeFromUpdatesQueue(systemUpdate);
+ assert.equal(initialLength - 1, UpdateManager.updatesQueue.length);
+ });
+ });
+ });
+
+ suite('downloads queue', function() {
+ suite('addToDownloadsQueue', function() {
+ var updatableApp;
+
+ setup(function() {
+ var installedApp = new MockApp();
+ updatableApp = new MockAppUpdatable(installedApp);
+ UpdateManager.updatableApps = [updatableApp];
+ UpdateManager.init();
+ });
+
+ test('should add the updatable to the array', function() {
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.addToDownloadsQueue(updatableApp);
+ assert.equal(initialLength + 1, UpdateManager.downloadsQueue.length);
+ });
+
+ test('should add system updates too', function() {
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.addToDownloadsQueue(new MockSystemUpdatable());
+ assert.equal(initialLength + 1, UpdateManager.downloadsQueue.length);
+ });
+
+ test('should not add more than one system updates', function() {
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.addToDownloadsQueue(new MockSystemUpdatable());
+ UpdateManager.addToDownloadsQueue(new MockSystemUpdatable());
+ assert.equal(initialLength + 1, UpdateManager.downloadsQueue.length);
+ });
+
+ suite('switching to downloading mode on first add', function() {
+ setup(function() {
+ UpdateManager.addToDownloadsQueue(updatableApp);
+ });
+
+ test('should add css class', function() {
+ var css = UpdateManager.container.classList;
+ assert.isTrue(css.contains('downloading'));
+ });
+
+ test('should ask for statusbar indicator', function() {
+ var incMethod = 'incSystemDownloads';
+ assert.ok(MockStatusBar.wasMethodCalled[incMethod]);
+ });
+
+ test('should request wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isFalse(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+ });
+
+ test('should not add app if not in updatableApps array', function() {
+ var updatableApp = new MockAppUpdatable(new MockApp);
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.addToDownloadsQueue(updatableApp);
+ assert.equal(initialLength, UpdateManager.downloadsQueue.length);
+ });
+
+ test('should not add if already in the array', function() {
+ UpdateManager.addToDownloadsQueue(updatableApp);
+
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.addToDownloadsQueue(updatableApp);
+ assert.equal(initialLength, UpdateManager.downloadsQueue.length);
+ });
+ });
+
+ suite('removeFromDownloadsQueue', function() {
+ var updatableApp;
+
+ setup(function() {
+ var installedApp = new MockApp();
+ updatableApp = new MockAppUpdatable(installedApp);
+ UpdateManager.init();
+
+ UpdateManager.addToUpdatableApps(updatableApp);
+ UpdateManager.addToDownloadsQueue(updatableApp);
+ });
+
+ test('should remove if in downloadsQueue array', function() {
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.removeFromDownloadsQueue(updatableApp);
+ assert.equal(initialLength - 1, UpdateManager.downloadsQueue.length);
+ });
+
+ suite('should switch off downloading mode on last remove', function() {
+ setup(function() {
+ UpdateManager.removeFromDownloadsQueue(updatableApp);
+ });
+
+ test('should remove css class', function() {
+ var css = UpdateManager.container.classList;
+ assert.isFalse(css.contains('downloading'));
+ });
+
+ test('should remove statusbar indicator', function() {
+ var decMethod = 'decSystemDownloads';
+ assert.ok(MockStatusBar.wasMethodCalled[decMethod]);
+ });
+
+ test('should release the wifi wake lock', function() {
+ assert.equal('wifi', MockNavigatorWakeLock.mLastWakeLock.topic);
+ assert.isTrue(MockNavigatorWakeLock.mLastWakeLock.released);
+ });
+ });
+
+ test('should not break if wifi unlock throws an exception',
+ function() {
+ MockNavigatorWakeLock.mThrowAtNextUnlock();
+ UpdateManager.removeFromDownloadsQueue(updatableApp);
+ assert.ok(true);
+ });
+
+ test('should remove system updates too', function() {
+ var systemUpdate = new MockSystemUpdatable();
+ UpdateManager.downloadsQueue.push(systemUpdate);
+
+ var initialLength = UpdateManager.downloadsQueue.length;
+ UpdateManager.removeFromDownloadsQueue(systemUpdate);
+ assert.equal(initialLength - 1, UpdateManager.downloadsQueue.length);
+ });
+ });
+ });
+ });
+});