Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/test/unit/app_install_manager_test.js
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/test/unit/app_install_manager_test.js')
-rw-r--r--apps/system/test/unit/app_install_manager_test.js1036
1 files changed, 1036 insertions, 0 deletions
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));
+ });
+ });
+});