1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/tests/mochitest/browser_downloads.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,389 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* Any copyright is dedicated to the Public Domain. 1.7 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.8 + 1.9 +"use strict"; 1.10 + 1.11 +/** 1.12 + * Provides infrastructure for automated download components tests. 1.13 + * (adapted from browser/component/downloads test's head.js) 1.14 + */ 1.15 + 1.16 +//////////////////////////////////////////////////////////////////////////////// 1.17 +//// Globals 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", 1.20 + "resource://gre/modules/FileUtils.jsm"); 1.21 + 1.22 +const nsIDM = Ci.nsIDownloadManager; 1.23 + 1.24 +//////////////////////////////////////////////////////////////////////////////// 1.25 +// Test Helpers 1.26 + 1.27 +var { spawn } = Task; 1.28 + 1.29 +function equalStrings(){ 1.30 + let ref = ""+arguments[0]; 1.31 + for (let i=1; i<arguments.length; i++){ 1.32 + if (ref !== ""+arguments[i]) { 1.33 + info("equalStrings failure: " + ref + " != " + arguments[i]); 1.34 + return false 1.35 + } 1.36 + } 1.37 + return true; 1.38 +} 1.39 + 1.40 +function equalNumbers(){ 1.41 + let ref = Number(arguments[0]); 1.42 + for (let i=1; i<arguments.length; i++){ 1.43 + if (ref !== Number(arguments[i])) return false; 1.44 + if (ref !== Number(arguments[i])) { 1.45 + info("equalNumbers failure: " + ref + " != " + Number(arguments[i])); 1.46 + return false 1.47 + } 1.48 + } 1.49 + return true; 1.50 +} 1.51 + 1.52 +function getPromisedDbResult(aStatement) { 1.53 + let dbConnection = MetroDownloadsView.manager.DBConnection; 1.54 + let statement = ("string" == typeof aStatement) ? 1.55 + dbConnection.createAsyncStatement( 1.56 + aStatement 1.57 + ) : aStatement; 1.58 + 1.59 + let deferred = Promise.defer(), 1.60 + resultRows = [], 1.61 + err = null; 1.62 + try { 1.63 + statement.executeAsync({ 1.64 + handleResult: function(aResultSet) { 1.65 + let row; 1.66 + if(!aResultSet) { 1.67 + return; 1.68 + } 1.69 + while ((row = aResultSet.getNextRow())){ 1.70 + resultRows.push(row); 1.71 + } 1.72 + }, 1.73 + handleError: function(aError) { 1.74 + Cu.reportError(aError); 1.75 + err = aError; 1.76 + }, 1.77 + handleCompletion: function(){ 1.78 + if (err) { 1.79 + deferred.reject(err); 1.80 + } else { 1.81 + deferred.resolve(resultRows); 1.82 + } 1.83 + } 1.84 + }); 1.85 + } finally { 1.86 + statement.finalize(); 1.87 + } 1.88 + return deferred.promise; 1.89 +} 1.90 + 1.91 +let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]); 1.92 +gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); 1.93 +registerCleanupFunction(function () { 1.94 + gTestTargetFile.remove(false); 1.95 + PanelUI.hide(); 1.96 +}); 1.97 + 1.98 +/** 1.99 + * This object contains a property for each column in the downloads table. 1.100 + */ 1.101 +let gDownloadRowTemplate = { 1.102 + name: "test-download.txt", 1.103 + source: "http://www.example.com/test-download.txt", 1.104 + target: NetUtil.newURI(gTestTargetFile).spec, 1.105 + startTime: 1180493839859230, 1.106 + endTime: 1180493839859234, 1.107 + state: nsIDM.DOWNLOAD_FINISHED, 1.108 + currBytes: 0, 1.109 + maxBytes: -1, 1.110 + preferredAction: 0, 1.111 + autoResume: 0 1.112 +}; 1.113 + 1.114 +//////////////////////////////////////////////////////////////////////////////// 1.115 +// Test Infrastructure 1.116 + 1.117 +function test() { 1.118 + runTests(); 1.119 +} 1.120 + 1.121 +///////////////////////////////////// 1.122 +// shared test setup 1.123 +function resetDownloads(){ 1.124 + // clear out existing and any pending downloads in the db 1.125 + // returns a promise 1.126 + 1.127 + let promisedResult = getPromisedDbResult( 1.128 + "DELETE FROM moz_downloads" 1.129 + ); 1.130 + return promisedResult.then(function(aResult){ 1.131 + // // Reset any prefs that might have been changed. 1.132 + // Services.prefs.clearUserPref("browser.download.panel.shown"); 1.133 + 1.134 + // Ensure that data is unloaded. 1.135 + let dlMgr = MetroDownloadsView.manager; 1.136 + let dlsToRemove = []; 1.137 + // Clear all completed/cancelled downloads 1.138 + dlMgr.cleanUp(); 1.139 + dlMgr.cleanUpPrivate(); 1.140 + 1.141 + // Queue up all active ones as well 1.142 + for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) { 1.143 + while (dlsEnum.hasMoreElements()) { 1.144 + dlsToRemove.push(dlsEnum.next()); 1.145 + } 1.146 + } 1.147 + // Remove any queued up active downloads 1.148 + dlsToRemove.forEach(function (dl) { 1.149 + dl.remove(); 1.150 + }); 1.151 + }); 1.152 +} 1.153 + 1.154 +function addDownloadRow(aDataRow) { 1.155 + let deferredInsert = Promise.defer(); 1.156 + let dataRow = aDataRow; 1.157 + 1.158 + let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); 1.159 + let db = dm.DBConnection; 1.160 + 1.161 + let columnNames = Object.keys(gDownloadRowTemplate).join(", "); 1.162 + let parameterNames = Object.keys(gDownloadRowTemplate) 1.163 + .map(function(n) ":" + n) 1.164 + .join(", "); 1.165 + 1.166 + let statement = db.createAsyncStatement( 1.167 + "INSERT INTO moz_downloads (" + columnNames + 1.168 + ", guid) VALUES(" + parameterNames + ", GENERATE_GUID())"); 1.169 + 1.170 + // Populate insert parameters from the provided data. 1.171 + for (let columnName in gDownloadRowTemplate) { 1.172 + if (!(columnName in dataRow)) { 1.173 + // Update the provided row object with data from the global template, 1.174 + // for columns whose value is not provided explicitly. 1.175 + dataRow[columnName] = gDownloadRowTemplate[columnName]; 1.176 + } 1.177 + statement.params[columnName] = dataRow[columnName]; 1.178 + } 1.179 + 1.180 + // Run the statement asynchronously and wait. 1.181 + let promisedDownloads = getPromisedDbResult( 1.182 + statement 1.183 + ); 1.184 + yield promisedDownloads.then(function(){ 1.185 + let newItemId = db.lastInsertRowID; 1.186 + let download = dm.getDownload(newItemId); 1.187 + deferredInsert.resolve(download); 1.188 + }); 1.189 +} 1.190 + 1.191 +function gen_addDownloadRows(aDataRows){ 1.192 + if (!aDataRows.length) { 1.193 + yield null; 1.194 + } 1.195 + 1.196 + try { 1.197 + // Add each of the provided downloads in reverse. 1.198 + for (let i = aDataRows.length - 1; i >= 0; i--) { 1.199 + let dataRow = aDataRows[i]; 1.200 + let download = yield addDownloadRow(dataRow); 1.201 + 1.202 + // At each iteration, ensure that the start and end time in the global 1.203 + // template is distinct, as these column are used to sort each download 1.204 + // in its category. 1.205 + gDownloadRowTemplate.startTime++; 1.206 + gDownloadRowTemplate.endTime++; 1.207 + } 1.208 + } finally { 1.209 + info("gen_addDownloadRows, finally"); 1.210 + } 1.211 +} 1.212 + 1.213 +///////////////////////////////////// 1.214 +// Test implementations 1.215 + 1.216 +gTests.push({ 1.217 + desc: "zero downloads", 1.218 + run: function () { 1.219 + yield resetDownloads(); 1.220 + todo(false, "Test there are no visible notifications with an empty db."); 1.221 + } 1.222 +}); 1.223 + 1.224 +/** 1.225 + * Make sure the downloads panel can display items in the right order and 1.226 + * contains the expected data. 1.227 + */ 1.228 +gTests.push({ 1.229 + desc: "Show downloads", 1.230 + run: function(){ 1.231 + // Display one of each download state. 1.232 + let DownloadData = [ 1.233 + { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED }, 1.234 + { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING }, 1.235 + { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED }, 1.236 + { endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING }, 1.237 + { endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED }, 1.238 + { endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED }, 1.239 + { endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED }, 1.240 + { endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED }, 1.241 + { endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL }, 1.242 + { endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY }, 1.243 + { endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY } 1.244 + ]; 1.245 + 1.246 + yield resetDownloads(); 1.247 + 1.248 + try { 1.249 + // Populate the downloads database with the data required by this test. 1.250 + // we're going to add stuff to the downloads db. 1.251 + yield spawn( gen_addDownloadRows( DownloadData ) ); 1.252 + 1.253 + todo( false, "Check that MetroDownloadsView._progressNotificationInfo and MetroDownloadsView._downloadCount \ 1.254 + have the correct length (DownloadData.length) \ 1.255 + May also test that the correct notifications show up for various states."); 1.256 + 1.257 + todo(false, "Iterate through download objects in MetroDownloadsView._progressNotificationInfo \ 1.258 + and confirm that the downloads they refer to are the same as those in \ 1.259 + DownloadData."); 1.260 + } catch(e) { 1.261 + info("Show downloads, some error: " + e); 1.262 + } 1.263 + finally { 1.264 + // Clean up when the test finishes. 1.265 + yield resetDownloads(); 1.266 + } 1.267 + } 1.268 +}); 1.269 + 1.270 +/** 1.271 + * Make sure the downloads can be removed with the expected result on the notifications 1.272 + */ 1.273 +gTests.push({ 1.274 + desc: "Remove downloads", 1.275 + run: function(){ 1.276 + // Push a few items into the downloads db. 1.277 + let DownloadData = [ 1.278 + { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_FINISHED }, 1.279 + { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_FINISHED }, 1.280 + { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_FINISHED } 1.281 + ]; 1.282 + 1.283 + yield resetDownloads(); 1.284 + 1.285 + try { 1.286 + // Populate the downloads database with the data required by this test. 1.287 + yield spawn( gen_addDownloadRows( DownloadData ) ); 1.288 + 1.289 + let downloadRows = null, 1.290 + promisedDownloads; 1.291 + // get all the downloads from the db 1.292 + promisedDownloads = getPromisedDbResult( 1.293 + "SELECT guid " 1.294 + + "FROM moz_downloads " 1.295 + + "ORDER BY startTime DESC" 1.296 + ).then(function(aRows){ 1.297 + downloadRows = aRows; 1.298 + }, function(aError){ 1.299 + throw aError; 1.300 + }); 1.301 + yield promisedDownloads; 1.302 + 1.303 + is(downloadRows.length, 3, "Correct number of downloads in the db before removal"); 1.304 + 1.305 + todo(false, "Get some download from MetroDownloadsView._progressNotificationInfo, \ 1.306 + confirm that its file exists, then remove it."); 1.307 + 1.308 + // remove is async(?), wait a bit 1.309 + yield waitForMs(0); 1.310 + 1.311 + // get all the downloads from the db 1.312 + downloadRows = null; 1.313 + promisedDownloads = getPromisedDbResult( 1.314 + "SELECT guid " 1.315 + + "FROM moz_downloads " 1.316 + + "ORDER BY startTime DESC" 1.317 + ).then(function(aRows){ 1.318 + downloadRows = aRows; 1.319 + }, function(aError){ 1.320 + throw aError; 1.321 + }); 1.322 + yield promisedDownloads; 1.323 + 1.324 + todo(false, "confirm that the removed download is no longer in the database \ 1.325 + and its file no longer exists."); 1.326 + 1.327 + } catch(e) { 1.328 + info("Remove downloads, some error: " + e); 1.329 + } 1.330 + finally { 1.331 + // Clean up when the test finishes. 1.332 + yield resetDownloads(); 1.333 + } 1.334 + } 1.335 +}); 1.336 + 1.337 +/** 1.338 + * Make sure the cancelled/aborted downloads are handled correctly. 1.339 + */ 1.340 +gTests.push({ 1.341 + desc: "Cancel/Abort Downloads", 1.342 + run: function(){ 1.343 + todo(false, "Ensure that a cancelled/aborted download is in the correct state \ 1.344 + including correct values for state variables (e.g. _downloadCount, _downloadsInProgress) \ 1.345 + and the existence of the downloaded file."); 1.346 + } 1.347 +}); 1.348 + 1.349 +/** 1.350 + * Make sure download notifications are moved when we close tabs. 1.351 + */ 1.352 +gTests.push({ 1.353 + desc: "Download notifications in closed tabs", 1.354 + setUp: function() { 1.355 + // put up a couple notifications on the initial tab 1.356 + let notificationBox = Browser.getNotificationBox(); 1.357 + notificationBox.appendNotification("not important", "low-priority-thing", "", notificationBox.PRIORITY_INFO_LOW, []); 1.358 + notificationBox.appendNotification("so important", "high-priority-thing", "", notificationBox.PRIORITY_CRITICAL_HIGH, []); 1.359 + 1.360 + // open a new tab where we'll conduct the test 1.361 + yield addTab("about:mozilla"); 1.362 + }, 1.363 + run: function(){ 1.364 + let notificationBox = Browser.getNotificationBox(); 1.365 + let notn = MetroDownloadsView.showNotification("download-progress", "test message", [], 1.366 + notificationBox.PRIORITY_WARNING_LOW); 1.367 + Browser.closeTab(Browser.selectedTab); 1.368 + 1.369 + yield waitForEvent(Elements.tabList, "TabRemove"); 1.370 + 1.371 + // expected behavior when a tab is closed while a download notification is showing: 1.372 + // * the notification remains visible as long as a next tab/browser exists 1.373 + // * normal rules about priority apply 1.374 + // * notifications - including any pre-existing ones - display in expected order 1.375 + let nextBox = Browser.getNotificationBox(); 1.376 + let currentNotification; 1.377 + 1.378 + ok(nextBox.getNotificationWithValue("download-progress"), "notification was moved to next tab"); 1.379 + 1.380 + currentNotification = nextBox.currentNotification; 1.381 + is(currentNotification.value, "high-priority-thing", "high priority notification is current"); 1.382 + currentNotification.close(); 1.383 + 1.384 + currentNotification = nextBox.currentNotification; 1.385 + is(currentNotification.value, "download-progress", "download notification is next"); 1.386 + currentNotification.close(); 1.387 + 1.388 + currentNotification = nextBox.currentNotification; 1.389 + is(currentNotification.value, "low-priority-thing", "low priority notification is next"); 1.390 + currentNotification.close(); 1.391 + } 1.392 +}); 1.393 \ No newline at end of file