| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 /** | |
| 8 * Extension ID of Files.app. | |
| 9 * @type {string} | |
| 10 * @const | |
| 11 */ | |
| 12 var FILE_MANAGER_EXTENSIONS_ID = 'hhaomjibdihmijegdhdafkllkbggdgoj'; | |
| 13 | |
| 14 /** | |
| 15 * Calls a remote test util in Files.app's extension. See: test_util.js. | |
| 16 * | |
| 17 * @param {string} func Function name. | |
| 18 * @param {?string} appId Target window's App ID or null for functions | |
| 19 * not requiring a window. | |
| 20 * @param {Array.<*>} args Array of arguments. | |
| 21 * @param {function(*)=} opt_callback Callback handling the function's result. | |
| 22 * @return {Promise} Promise to be fulfilled with the result of the remote | |
| 23 * utility. | |
| 24 */ | |
| 25 function callRemoteTestUtil(func, appId, args, opt_callback) { | |
| 26 return new Promise(function(onFulfilled) { | |
| 27 chrome.runtime.sendMessage( | |
| 28 FILE_MANAGER_EXTENSIONS_ID, { | |
| 29 func: func, | |
| 30 appId: appId, | |
| 31 args: args | |
| 32 }, | |
| 33 function() { | |
| 34 if (opt_callback) | |
| 35 opt_callback.apply(null, arguments); | |
| 36 onFulfilled(arguments[0]); | |
| 37 }); | |
| 38 }); | |
| 39 } | |
| 40 | |
| 41 /** | |
| 42 * Returns promise to be fulfilled after the given milliseconds. | |
| 43 * @param {number} time Time in milliseconds. | |
| 44 */ | |
| 45 function wait(time) { | |
| 46 return new Promise(function(callback) { | |
| 47 setTimeout(callback, time); | |
| 48 }); | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Interval milliseconds between checks of repeatUntil. | |
| 53 * @type {number} | |
| 54 * @const | |
| 55 */ | |
| 56 var REPEAT_UNTIL_INTERVAL = 200; | |
| 57 | |
| 58 /** | |
| 59 * Interval milliseconds between log output of repeatUntil. | |
| 60 * @type {number} | |
| 61 * @const | |
| 62 */ | |
| 63 var LOG_INTERVAL = 3000; | |
| 64 | |
| 65 /** | |
| 66 * Returns a pending marker. See also the repeatUntil function. | |
| 67 * @param {string} message Pending reason including %s, %d, or %j markers. %j | |
| 68 * format an object as JSON. | |
| 69 * @param {Array.<*>} var_args Values to be assigined to %x markers. | |
| 70 * @return {Object} Object which returns true for the expression: obj instanceof | |
| 71 * pending. | |
| 72 */ | |
| 73 function pending(message, var_args) { | |
| 74 var index = 1; | |
| 75 var args = arguments; | |
| 76 var formattedMessage = message.replace(/%[sdj]/g, function(pattern) { | |
| 77 var arg = args[index++]; | |
| 78 switch(pattern) { | |
| 79 case '%s': return String(arg); | |
| 80 case '%d': return Number(arg); | |
| 81 case '%j': return JSON.stringify(arg); | |
| 82 default: return pattern; | |
| 83 } | |
| 84 }); | |
| 85 var pendingMarker = Object.create(pending.prototype); | |
| 86 pendingMarker.message = formattedMessage; | |
| 87 return pendingMarker; | |
| 88 }; | |
| 89 | |
| 90 /** | |
| 91 * Waits until the checkFunction returns a value but a pending marker. | |
| 92 * @param {function():*} checkFunction Function to check a condition. It can | |
| 93 * return a pending marker created by a pending function. | |
| 94 * @return {Promise} Promise to be fulfilled with the return value of | |
| 95 * checkFunction when the checkFunction reutrns a value but a pending | |
| 96 * marker. | |
| 97 */ | |
| 98 function repeatUntil(checkFunction) { | |
| 99 var logTime = Date.now() + LOG_INTERVAL; | |
| 100 var step = function() { | |
| 101 return checkFunction().then(function(result) { | |
| 102 if (result instanceof pending) { | |
| 103 if (Date.now() > logTime) { | |
| 104 console.log(result.message); | |
| 105 logTime += LOG_INTERVAL; | |
| 106 } | |
| 107 return wait(REPEAT_UNTIL_INTERVAL).then(step); | |
| 108 } else { | |
| 109 return result; | |
| 110 } | |
| 111 }); | |
| 112 }; | |
| 113 return step(); | |
| 114 }; | |
| 115 | |
| 116 /** | |
| 117 * Waits until a window having the given ID prefix appears. | |
| 118 * @param {string} windowIdPrefix ID prefix of the requested window. | |
| 119 * @return {Promise} promise Promise to be fulfilled with a found window's ID. | |
| 120 */ | |
| 121 function waitForWindow(windowIdPrefix) { | |
| 122 return repeatUntil(function() { | |
| 123 return callRemoteTestUtil('getWindows', null, []).then(function(windows) { | |
| 124 for (var id in windows) { | |
| 125 if (id.indexOf(windowIdPrefix) === 0) | |
| 126 return id; | |
| 127 } | |
| 128 return pending('Window with the prefix %s is not found.', windowIdPrefix); | |
| 129 }); | |
| 130 }); | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * Closes a window and waits until the window is closed. | |
| 135 * | |
| 136 * @param {string} windowId ID of the window to close. | |
| 137 * @return {Promise} promise Promise to be fulfilled with the result (true: | |
| 138 * success, false: failed). | |
| 139 */ | |
| 140 function closeWindowAndWait(windowId) { | |
| 141 // Closes the window. | |
| 142 return callRemoteTestUtil('closeWindow', null, [windowId]).then( | |
| 143 function(result) { | |
| 144 // Returns false when the closing is failed. | |
| 145 if (!result) | |
| 146 return false; | |
| 147 | |
| 148 return repeatUntil(function() { | |
| 149 return callRemoteTestUtil('getWindows', null, []).then( | |
| 150 function(windows) { | |
| 151 for (var id in windows) { | |
| 152 if (id === windowId) { | |
| 153 // Window is still available. Continues waiting. | |
| 154 return pending('Window with the prefix %s is not found.', | |
| 155 windowId); | |
| 156 } | |
| 157 } | |
| 158 // Window is not available. Closing is done successfully. | |
| 159 return true; | |
| 160 } | |
| 161 ); | |
| 162 }); | |
| 163 } | |
| 164 ); | |
| 165 } | |
| 166 | |
| 167 /** | |
| 168 * Waits until the window turns to the given size. | |
| 169 * @param {string} windowId Target window ID. | |
| 170 * @param {number} width Requested width in pixels. | |
| 171 * @param {number} height Requested height in pixels. | |
| 172 */ | |
| 173 function waitForWindowGeometry(windowId, width, height) { | |
| 174 return repeatUntil(function() { | |
| 175 return callRemoteTestUtil('getWindows', null, []).then(function(windows) { | |
| 176 if (!windows[windowId]) | |
| 177 return pending('Window %s is not found.', windowId); | |
| 178 if (windows[windowId].innerWidth !== width || | |
| 179 windows[windowId].innerHeight !== height) { | |
| 180 return pending('Expected window size is %j, but it is %j', | |
| 181 {width: width, height: height}, | |
| 182 windows[windowId]); | |
| 183 } | |
| 184 }); | |
| 185 }); | |
| 186 } | |
| 187 | |
| 188 /** | |
| 189 * Waits for the specified element appearing in the DOM. | |
| 190 * @param {string} windowId Target window ID. | |
| 191 * @param {string} query Query string for the element. | |
| 192 * @param {string=} opt_iframeQuery Query string for the iframe containing the | |
| 193 * element. | |
| 194 * @return {Promise} Promise to be fulfilled when the element appears. | |
| 195 */ | |
| 196 function waitForElement(windowId, query, opt_iframeQuery) { | |
| 197 return repeatUntil(function() { | |
| 198 return callRemoteTestUtil( | |
| 199 'queryAllElements', | |
| 200 windowId, | |
| 201 [query, opt_iframeQuery] | |
| 202 ).then(function(elements) { | |
| 203 if (elements.length > 0) | |
| 204 return elements[0]; | |
| 205 else | |
| 206 return pending( | |
| 207 'Element %s (maybe in iframe %s) is not found.', | |
| 208 query, | |
| 209 opt_iframeQuery); | |
| 210 }); | |
| 211 }); | |
| 212 } | |
| 213 | |
| 214 /** | |
| 215 * Waits for the specified element leaving from the DOM. | |
| 216 * @param {string} windowId Target window ID. | |
| 217 * @param {string} query Query string for the element. | |
| 218 * @param {string=} opt_iframeQuery Query string for the iframe containing the | |
| 219 * element. | |
| 220 * @return {Promise} Promise to be fulfilled when the element is lost. | |
| 221 */ | |
| 222 function waitForElementLost(windowId, query, opt_iframeQuery) { | |
| 223 return repeatUntil(function() { | |
| 224 return callRemoteTestUtil( | |
| 225 'queryAllElements', | |
| 226 windowId, | |
| 227 [query, opt_iframeQuery] | |
| 228 ).then(function(elements) { | |
| 229 if (elements.length > 0) | |
| 230 return pending('Elements %j is still exists.', elements); | |
| 231 return true; | |
| 232 }); | |
| 233 }); | |
| 234 } | |
| 235 | |
| 236 /** | |
| 237 /** | |
| 238 * Waits for the file list turns to the given contents. | |
| 239 * @param {string} windowId Target window ID. | |
| 240 * @param {Array.<Array.<string>>} expected Expected contents of file list. | |
| 241 * @param {{orderCheck:boolean=, ignoreLastModifiedTime:boolean=}=} opt_options | |
| 242 * Options of the comparison. If orderCheck is true, it also compares the | |
| 243 * order of files. If ignoreLastModifiedTime is true, it compares the file | |
| 244 * without its last modified time. | |
| 245 * @return {Promise} Promise to be fulfilled when the file list turns to the | |
| 246 * given contents. | |
| 247 */ | |
| 248 function waitForFiles(windowId, expected, opt_options) { | |
| 249 var options = opt_options || {}; | |
| 250 return repeatUntil(function() { | |
| 251 return callRemoteTestUtil( | |
| 252 'getFileList', windowId, []).then(function(files) { | |
| 253 if (!options.orderCheck) { | |
| 254 files.sort(); | |
| 255 expected.sort(); | |
| 256 } | |
| 257 for (var i = 0; i < Math.min(files.length, expected.length); i++) { | |
| 258 if (options.ignoreFileSize) { | |
| 259 files[i][1] = ''; | |
| 260 expected[i][1] = ''; | |
| 261 } | |
| 262 if (options.ignoreLastModifiedTime) { | |
| 263 files[i][3] = ''; | |
| 264 expected[i][3] = ''; | |
| 265 } | |
| 266 } | |
| 267 if (!chrome.test.checkDeepEq(expected, files)) { | |
| 268 return pending('waitForFiles: expected: %j actual %j.', | |
| 269 expected, | |
| 270 files); | |
| 271 } | |
| 272 }); | |
| 273 }); | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Waits until the number of files in the file list is changed from the given | |
| 278 * number. | |
| 279 * TODO(hirono): Remove the function. | |
| 280 * | |
| 281 * @param {string} windowId Target window ID. | |
| 282 * @param {number} lengthBefore Number of items visible before. | |
| 283 * @return {Promise} Promise to be fulfilled with the contents of files. | |
| 284 */ | |
| 285 function waitForFileListChange(windowId, lengthBefore) { | |
| 286 return repeatUntil(function() { | |
| 287 return callRemoteTestUtil( | |
| 288 'getFileList', windowId, []).then(function(files) { | |
| 289 files.sort(); | |
| 290 var notReadyRows = files.filter(function(row) { | |
| 291 return row.filter(function(cell) { return cell == '...'; }).length; | |
| 292 }); | |
| 293 if (notReadyRows.length === 0 && | |
| 294 files.length !== lengthBefore && | |
| 295 files.length !== 0) { | |
| 296 return files; | |
| 297 } else { | |
| 298 return pending('The number of file is %d. Not changed.', lengthBefore); | |
| 299 } | |
| 300 }); | |
| 301 }); | |
| 302 }; | |
| 303 | |
| 304 /** | |
| 305 * Waits until the given taskId appears in the executed task list. | |
| 306 * @param {string} windowId Target window ID. | |
| 307 * @param {string} taskId Task ID to watch. | |
| 308 * @return {Promise} Promise to be fulfilled when the task appears in the | |
| 309 * executed task list. | |
| 310 */ | |
| 311 function waitUntilTaskExecutes(windowId, taskId) { | |
| 312 return repeatUntil(function() { | |
| 313 return callRemoteTestUtil('getExecutedTasks', windowId, []). | |
| 314 then(function(executedTasks) { | |
| 315 if (executedTasks.indexOf(taskId) === -1) | |
| 316 return pending('Executed task is %j', executedTasks); | |
| 317 }); | |
| 318 }); | |
| 319 } | |
| 320 | |
| 321 /** | |
| 322 * Adds check of chrome.test to the end of the given promise. | |
| 323 * @param {Promise} promise Promise. | |
| 324 */ | |
| 325 function testPromise(promise) { | |
| 326 promise.then(function() { | |
| 327 return new Promise(checkIfNoErrorsOccured); | |
| 328 }).then(chrome.test.callbackPass(function() { | |
| 329 // The callbacPass is necessary to avoid prematurely finishing tests. | |
| 330 // Don't put chrome.test.succeed() here to avoid doubled success log. | |
| 331 }), function(error) { | |
| 332 chrome.test.fail(error.stack || error); | |
| 333 }); | |
| 334 }; | |
| 335 | |
| 336 /** | |
| 337 * Sends a fake key down event. | |
| 338 * @param {string} windowId Window ID. | |
| 339 * @param {string} query Query for the target element. | |
| 340 * @param {string} keyIdentifer Key identifier. | |
| 341 * @param {boolean} ctrlKey Control key flag. | |
| 342 * @return {Promise} Promise to be fulfilled or rejected depending on the | |
| 343 * result. | |
| 344 */ | |
| 345 function fakeKeyDown(windowId, query, keyIdentifer, ctrlKey) { | |
| 346 return new Promise(function(fulfill, reject) { | |
| 347 callRemoteTestUtil('fakeKeyDown', | |
| 348 windowId, | |
| 349 [query, keyIdentifer, ctrlKey], | |
| 350 function(result) { | |
| 351 if (result) | |
| 352 fulfill(); | |
| 353 else | |
| 354 reject(new Error('Fail to fake key down.')); | |
| 355 }); | |
| 356 }); | |
| 357 } | |
| 358 | |
| 359 /** | |
| 360 * Executes a sequence of test steps. | |
| 361 * @constructor | |
| 362 */ | |
| 363 function StepsRunner() { | |
| 364 /** | |
| 365 * List of steps. | |
| 366 * @type {Array.<function>} | |
| 367 * @private | |
| 368 */ | |
| 369 this.steps_ = []; | |
| 370 } | |
| 371 | |
| 372 /** | |
| 373 * Creates a StepsRunner instance and runs the passed steps. | |
| 374 */ | |
| 375 StepsRunner.run = function(steps) { | |
| 376 var stepsRunner = new StepsRunner(); | |
| 377 stepsRunner.run_(steps); | |
| 378 }; | |
| 379 | |
| 380 StepsRunner.prototype = { | |
| 381 /** | |
| 382 * @return {function} The next closure. | |
| 383 */ | |
| 384 get next() { | |
| 385 return this.steps_[0]; | |
| 386 } | |
| 387 }; | |
| 388 | |
| 389 /** | |
| 390 * Runs a sequence of the added test steps. | |
| 391 * @type {Array.<function>} List of the sequential steps. | |
| 392 */ | |
| 393 StepsRunner.prototype.run_ = function(steps) { | |
| 394 this.steps_ = steps.slice(0); | |
| 395 | |
| 396 // An extra step which acts as an empty callback for optional asynchronous | |
| 397 // calls in the last provided step. | |
| 398 this.steps_.push(function() {}); | |
| 399 | |
| 400 this.steps_ = this.steps_.map(function(f) { | |
| 401 return chrome.test.callbackPass(function() { | |
| 402 this.steps_.shift(); | |
| 403 f.apply(this, arguments); | |
| 404 }.bind(this)); | |
| 405 }.bind(this)); | |
| 406 | |
| 407 this.next(); | |
| 408 }; | |
| 409 | |
| 410 /** | |
| 411 * Adds the givin entries to the target volume(s). | |
| 412 * @param {Array.<string>} volumeNames Names of target volumes. | |
| 413 * @param {Array.<TestEntryInfo>} entries List of entries to be added. | |
| 414 * @param {function(boolean)} callback Callback function to be passed the result | |
| 415 * of function. The argument is true on success. | |
| 416 */ | |
| 417 function addEntries(volumeNames, entries, callback) { | |
| 418 if (volumeNames.length == 0) { | |
| 419 callback(true); | |
| 420 return; | |
| 421 } | |
| 422 chrome.test.sendMessage(JSON.stringify({ | |
| 423 name: 'addEntries', | |
| 424 volume: volumeNames.shift(), | |
| 425 entries: entries | |
| 426 }), chrome.test.callbackPass(function(result) { | |
| 427 if (result == "onEntryAdded") | |
| 428 addEntries(volumeNames, entries, callback); | |
| 429 else | |
| 430 callback(false); | |
| 431 })); | |
| 432 }; | |
| 433 | |
| 434 /** | |
| 435 * @enum {string} | |
| 436 * @const | |
| 437 */ | |
| 438 var EntryType = Object.freeze({ | |
| 439 FILE: 'file', | |
| 440 DIRECTORY: 'directory' | |
| 441 }); | |
| 442 | |
| 443 /** | |
| 444 * @enum {string} | |
| 445 * @const | |
| 446 */ | |
| 447 var SharedOption = Object.freeze({ | |
| 448 NONE: 'none', | |
| 449 SHARED: 'shared' | |
| 450 }); | |
| 451 | |
| 452 /** | |
| 453 * @enum {string} | |
| 454 */ | |
| 455 var RootPath = Object.seal({ | |
| 456 DOWNLOADS: '/must-be-filled-in-test-setup', | |
| 457 DRIVE: '/must-be-filled-in-test-setup', | |
| 458 }); | |
| 459 | |
| 460 /** | |
| 461 * File system entry information for tests. | |
| 462 * | |
| 463 * @param {EntryType} type Entry type. | |
| 464 * @param {string} sourceFileName Source file name that provides file contents. | |
| 465 * @param {string} targetName Name of entry on the test file system. | |
| 466 * @param {string} mimeType Mime type. | |
| 467 * @param {SharedOption} sharedOption Shared option. | |
| 468 * @param {string} lastModifiedTime Last modified time as a text to be shown in | |
| 469 * the last modified column. | |
| 470 * @param {string} nameText File name to be shown in the name column. | |
| 471 * @param {string} sizeText Size text to be shown in the size column. | |
| 472 * @param {string} typeText Type name to be shown in the type column. | |
| 473 * @constructor | |
| 474 */ | |
| 475 function TestEntryInfo(type, | |
| 476 sourceFileName, | |
| 477 targetPath, | |
| 478 mimeType, | |
| 479 sharedOption, | |
| 480 lastModifiedTime, | |
| 481 nameText, | |
| 482 sizeText, | |
| 483 typeText) { | |
| 484 this.type = type; | |
| 485 this.sourceFileName = sourceFileName || ''; | |
| 486 this.targetPath = targetPath; | |
| 487 this.mimeType = mimeType || ''; | |
| 488 this.sharedOption = sharedOption; | |
| 489 this.lastModifiedTime = lastModifiedTime; | |
| 490 this.nameText = nameText; | |
| 491 this.sizeText = sizeText; | |
| 492 this.typeText = typeText; | |
| 493 Object.freeze(this); | |
| 494 }; | |
| 495 | |
| 496 TestEntryInfo.getExpectedRows = function(entries) { | |
| 497 return entries.map(function(entry) { return entry.getExpectedRow(); }); | |
| 498 }; | |
| 499 | |
| 500 /** | |
| 501 * Obtains a expected row contents of the file in the file list. | |
| 502 */ | |
| 503 TestEntryInfo.prototype.getExpectedRow = function() { | |
| 504 return [this.nameText, this.sizeText, this.typeText, this.lastModifiedTime]; | |
| 505 }; | |
| 506 | |
| 507 /** | |
| 508 * Filesystem entries used by the test cases. | |
| 509 * @type {Object.<string, TestEntryInfo>} | |
| 510 * @const | |
| 511 */ | |
| 512 var ENTRIES = { | |
| 513 hello: new TestEntryInfo( | |
| 514 EntryType.FILE, 'text.txt', 'hello.txt', | |
| 515 'text/plain', SharedOption.NONE, 'Sep 4, 1998 12:34 PM', | |
| 516 'hello.txt', '51 bytes', 'Plain text'), | |
| 517 | |
| 518 world: new TestEntryInfo( | |
| 519 EntryType.FILE, 'video.ogv', 'world.ogv', | |
| 520 'text/plain', SharedOption.NONE, 'Jul 4, 2012 10:35 AM', | |
| 521 'world.ogv', '59 KB', 'OGG video'), | |
| 522 | |
| 523 unsupported: new TestEntryInfo( | |
| 524 EntryType.FILE, 'random.bin', 'unsupported.foo', | |
| 525 'application/x-foo', SharedOption.NONE, 'Jul 4, 2012 10:36 AM', | |
| 526 'unsupported.foo', '8 KB', 'FOO file'), | |
| 527 | |
| 528 desktop: new TestEntryInfo( | |
| 529 EntryType.FILE, 'image.png', 'My Desktop Background.png', | |
| 530 'text/plain', SharedOption.NONE, 'Jan 18, 2038 1:02 AM', | |
| 531 'My Desktop Background.png', '272 bytes', 'PNG image'), | |
| 532 | |
| 533 beautiful: new TestEntryInfo( | |
| 534 EntryType.FILE, 'music.ogg', 'Beautiful Song.ogg', | |
| 535 'text/plain', SharedOption.NONE, 'Nov 12, 2086 12:00 PM', | |
| 536 'Beautiful Song.ogg', '14 KB', 'OGG audio'), | |
| 537 | |
| 538 photos: new TestEntryInfo( | |
| 539 EntryType.DIRECTORY, null, 'photos', | |
| 540 null, SharedOption.NONE, 'Jan 1, 1980 11:59 PM', | |
| 541 'photos', '--', 'Folder'), | |
| 542 | |
| 543 testDocument: new TestEntryInfo( | |
| 544 EntryType.FILE, null, 'Test Document', | |
| 545 'application/vnd.google-apps.document', | |
| 546 SharedOption.NONE, 'Apr 10, 2013 4:20 PM', | |
| 547 'Test Document.gdoc', '--', 'Google document'), | |
| 548 | |
| 549 testSharedDocument: new TestEntryInfo( | |
| 550 EntryType.FILE, null, 'Test Shared Document', | |
| 551 'application/vnd.google-apps.document', | |
| 552 SharedOption.SHARED, 'Mar 20, 2013 10:40 PM', | |
| 553 'Test Shared Document.gdoc', '--', 'Google document'), | |
| 554 | |
| 555 newlyAdded: new TestEntryInfo( | |
| 556 EntryType.FILE, 'music.ogg', 'newly added file.ogg', | |
| 557 'audio/ogg', SharedOption.NONE, 'Sep 4, 1998 12:00 AM', | |
| 558 'newly added file.ogg', '14 KB', 'OGG audio'), | |
| 559 | |
| 560 directoryA: new TestEntryInfo( | |
| 561 EntryType.DIRECTORY, null, 'A', | |
| 562 null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM', | |
| 563 'A', '--', 'Folder'), | |
| 564 | |
| 565 directoryB: new TestEntryInfo( | |
| 566 EntryType.DIRECTORY, null, 'A/B', | |
| 567 null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM', | |
| 568 'B', '--', 'Folder'), | |
| 569 | |
| 570 directoryC: new TestEntryInfo( | |
| 571 EntryType.DIRECTORY, null, 'A/B/C', | |
| 572 null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM', | |
| 573 'C', '--', 'Folder'), | |
| 574 | |
| 575 zipArchive: new TestEntryInfo( | |
| 576 EntryType.FILE, 'archive.zip', 'archive.zip', | |
| 577 'application/x-zip', SharedOption.NONE, 'Jan 1, 2014 1:00 AM', | |
| 578 'archive.zip', '533 bytes', 'Zip archive') | |
| 579 }; | |
| 580 | |
| 581 /** | |
| 582 * Basic entry set for the local volume. | |
| 583 * @type {Array.<TestEntryInfo>} | |
| 584 * @const | |
| 585 */ | |
| 586 var BASIC_LOCAL_ENTRY_SET = [ | |
| 587 ENTRIES.hello, | |
| 588 ENTRIES.world, | |
| 589 ENTRIES.desktop, | |
| 590 ENTRIES.beautiful, | |
| 591 ENTRIES.photos | |
| 592 ]; | |
| 593 | |
| 594 /** | |
| 595 * Basic entry set for the drive volume. | |
| 596 * | |
| 597 * TODO(hirono): Add a case for an entry cached by FileCache. For testing | |
| 598 * Drive, create more entries with Drive specific attributes. | |
| 599 * | |
| 600 * @type {Array.<TestEntryInfo>} | |
| 601 * @const | |
| 602 */ | |
| 603 var BASIC_DRIVE_ENTRY_SET = [ | |
| 604 ENTRIES.hello, | |
| 605 ENTRIES.world, | |
| 606 ENTRIES.desktop, | |
| 607 ENTRIES.beautiful, | |
| 608 ENTRIES.photos, | |
| 609 ENTRIES.unsupported, | |
| 610 ENTRIES.testDocument, | |
| 611 ENTRIES.testSharedDocument | |
| 612 ]; | |
| 613 | |
| 614 var NESTED_ENTRY_SET = [ | |
| 615 ENTRIES.directoryA, | |
| 616 ENTRIES.directoryB, | |
| 617 ENTRIES.directoryC | |
| 618 ]; | |
| 619 | |
| 620 /** | |
| 621 * Expected files shown in "Recent". Directories (e.g. 'photos') are not in this | |
| 622 * list as they are not expected in "Recent". | |
| 623 * | |
| 624 * @type {Array.<TestEntryInfo>} | |
| 625 * @const | |
| 626 */ | |
| 627 var RECENT_ENTRY_SET = [ | |
| 628 ENTRIES.hello, | |
| 629 ENTRIES.world, | |
| 630 ENTRIES.desktop, | |
| 631 ENTRIES.beautiful, | |
| 632 ENTRIES.unsupported, | |
| 633 ENTRIES.testDocument, | |
| 634 ENTRIES.testSharedDocument | |
| 635 ]; | |
| 636 | |
| 637 /** | |
| 638 * Expected files shown in "Offline", which should have the files | |
| 639 * "available offline". Google Documents, Google Spreadsheets, and the files | |
| 640 * cached locally are "available offline". | |
| 641 * | |
| 642 * @type {Array.<TestEntryInfo>} | |
| 643 * @const | |
| 644 */ | |
| 645 var OFFLINE_ENTRY_SET = [ | |
| 646 ENTRIES.testDocument, | |
| 647 ENTRIES.testSharedDocument | |
| 648 ]; | |
| 649 | |
| 650 /** | |
| 651 * Expected files shown in "Shared with me", which should be the entries labeled | |
| 652 * with "shared-with-me". | |
| 653 * | |
| 654 * @type {Array.<TestEntryInfo>} | |
| 655 * @const | |
| 656 */ | |
| 657 var SHARED_WITH_ME_ENTRY_SET = [ | |
| 658 ENTRIES.testSharedDocument | |
| 659 ]; | |
| 660 | |
| 661 /** | |
| 662 * Opens a Files.app's main window. | |
| 663 * | |
| 664 * TODO(mtomasz): Pass a volumeId or an enum value instead of full paths. | |
| 665 * | |
| 666 * @param {Object} appState App state to be passed with on opening Files.app. | |
| 667 * Can be null. | |
| 668 * @param {?string} initialRoot Root path to be used as a default current | |
| 669 * directory during initialization. Can be null, for no default path. | |
| 670 * @param {function(string)} Callback with the app id. | |
| 671 * @return {Promise} Promise to be fulfilled after window creating. | |
| 672 */ | |
| 673 function openNewWindow(appState, initialRoot, callback) { | |
| 674 var appId; | |
| 675 | |
| 676 // TODO(mtomasz): Migrate from full paths to a pair of a volumeId and a | |
| 677 // relative path. To compose the URL communicate via messages with | |
| 678 // file_manager_browser_test.cc. | |
| 679 var processedAppState = appState || {}; | |
| 680 if (initialRoot) { | |
| 681 processedAppState.currentDirectoryURL = | |
| 682 'filesystem:chrome-extension://' + FILE_MANAGER_EXTENSIONS_ID + | |
| 683 '/external' + initialRoot; | |
| 684 } | |
| 685 | |
| 686 return callRemoteTestUtil('openMainWindow', | |
| 687 null, | |
| 688 [processedAppState], | |
| 689 callback); | |
| 690 } | |
| 691 | |
| 692 /** | |
| 693 * Opens a Files.app's main window and waits until it is initialized. Fills | |
| 694 * the window with initial files. Should be called for the first window only. | |
| 695 * | |
| 696 * TODO(hirono): Add parameters to specify the entry set to be prepared. | |
| 697 * TODO(mtomasz): Pass a volumeId or an enum value instead of full paths. | |
| 698 * | |
| 699 * @param {Object} appState App state to be passed with on opening Files.app. | |
| 700 * Can be null. | |
| 701 * @param {?string} initialRoot Root path to be used as a default current | |
| 702 * directory during initialization. Can be null, for no default path. | |
| 703 * @param {function(string, Array.<Array.<string>>)} Callback with the app id | |
| 704 * and with the file list. | |
| 705 */ | |
| 706 function setupAndWaitUntilReady(appState, initialRoot, callback) { | |
| 707 var appId; | |
| 708 | |
| 709 StepsRunner.run([ | |
| 710 function() { | |
| 711 openNewWindow(appState, initialRoot, this.next); | |
| 712 }, | |
| 713 function(inAppId) { | |
| 714 appId = inAppId; | |
| 715 addEntries(['local'], BASIC_LOCAL_ENTRY_SET, this.next); | |
| 716 }, | |
| 717 function(success) { | |
| 718 chrome.test.assertTrue(success); | |
| 719 addEntries(['drive'], BASIC_DRIVE_ENTRY_SET, this.next); | |
| 720 }, | |
| 721 function(success) { | |
| 722 chrome.test.assertTrue(success); | |
| 723 waitForElement(appId, '#detail-table').then(this.next); | |
| 724 }, | |
| 725 function(success) { | |
| 726 waitForFileListChange(appId, 0).then(this.next); | |
| 727 }, | |
| 728 function(fileList) { | |
| 729 callback(appId, fileList); | |
| 730 this.next(); | |
| 731 } | |
| 732 ]); | |
| 733 } | |
| 734 | |
| 735 /** | |
| 736 * Verifies if there are no Javascript errors in any of the app windows. | |
| 737 * @param {function()} Completion callback. | |
| 738 */ | |
| 739 function checkIfNoErrorsOccured(callback) { | |
| 740 callRemoteTestUtil('getErrorCount', null, [], function(count) { | |
| 741 chrome.test.assertEq(0, count, 'The error count is not 0.'); | |
| 742 callback(); | |
| 743 }); | |
| 744 } | |
| 745 | |
| 746 /** | |
| 747 * Returns the name of the given file list entry. | |
| 748 * @param {Array.<string>} file An entry in a file list. | |
| 749 * @return {string} Name of the file. | |
| 750 */ | |
| 751 function getFileName(fileListEntry) { | |
| 752 return fileListEntry[0]; | |
| 753 } | |
| 754 | |
| 755 /** | |
| 756 * Returns the size of the given file list entry. | |
| 757 * @param {Array.<string>} An entry in a file list. | |
| 758 * @return {string} Size of the file. | |
| 759 */ | |
| 760 function getFileSize(fileListEntry) { | |
| 761 return fileListEntry[1]; | |
| 762 } | |
| 763 | |
| 764 /** | |
| 765 * Returns the type of the given file list entry. | |
| 766 * @param {Array.<string>} An entry in a file list. | |
| 767 * @return {string} Type of the file. | |
| 768 */ | |
| 769 function getFileType(fileListEntry) { | |
| 770 return fileListEntry[2]; | |
| 771 } | |
| 772 | |
| 773 /** | |
| 774 * Namespace for test cases. | |
| 775 */ | |
| 776 var testcase = {}; | |
| 777 | |
| 778 // Ensure the test cases are loaded. | |
| 779 window.addEventListener('load', function() { | |
| 780 var steps = [ | |
| 781 // Check for the guest mode. | |
| 782 function() { | |
| 783 chrome.test.sendMessage( | |
| 784 JSON.stringify({name: 'isInGuestMode'}), steps.shift()); | |
| 785 }, | |
| 786 // Obtain the test case name. | |
| 787 function(result) { | |
| 788 if (JSON.parse(result) != chrome.extension.inIncognitoContext) | |
| 789 return; | |
| 790 chrome.test.sendMessage( | |
| 791 JSON.stringify({name: 'getRootPaths'}), steps.shift()); | |
| 792 }, | |
| 793 // Obtain the root entry paths. | |
| 794 function(result) { | |
| 795 var roots = JSON.parse(result); | |
| 796 RootPath.DOWNLOADS = roots.downloads; | |
| 797 RootPath.DRIVE = roots.drive; | |
| 798 chrome.test.sendMessage( | |
| 799 JSON.stringify({name: 'getTestName'}), steps.shift()); | |
| 800 }, | |
| 801 // Run the test case. | |
| 802 function(testCaseName) { | |
| 803 var targetTest = testcase[testCaseName]; | |
| 804 if (!targetTest) { | |
| 805 chrome.test.fail(testCaseName + ' is not found.'); | |
| 806 return; | |
| 807 } | |
| 808 // Specify the name of test to the test system. | |
| 809 targetTest.generatedName = testCaseName; | |
| 810 chrome.test.runTests([targetTest]); | |
| 811 } | |
| 812 ]; | |
| 813 steps.shift()(); | |
| 814 }); | |
| OLD | NEW |