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 |