'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 = [ '
', '

', '', '', '', '', '', '', '', '', '', '
Size
Author', '', '
', '
', '', '', '', '', '
' ].join(''); fakeInstallCancelDialog = document.createElement('form'); fakeInstallCancelDialog.id = 'app-install-cancel-dialog'; fakeInstallCancelDialog.innerHTML = [ '
', '

Cancel Install

', '

', 'Cancelling ' + 'will not refund a purchase. Refunds for paid content are ' + 'provided by the original seller.', 'Apps can be ' + 'installed later from the original installation source.', '

', '

' + 'Are you sure you want to cancel this install?

', '', '', '', '', '
' ].join(''); fakeDownloadCancelDialog = document.createElement('form'); fakeDownloadCancelDialog.id = 'app-download-cancel-dialog'; fakeDownloadCancelDialog.innerHTML = [ '
', '

', '

' + 'The download can be restarted later.

', '', '', '', '', '
' ].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)); }); }); });