1 // Copyright 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.
8 * Class to manipulate the window in the remote extension.
10 * @param {string} extensionId ID of extension to be manipulated.
13 function RemoteCall(extensionId
) {
14 this.extensionId_
= extensionId
;
18 * Checks whether step by step tests are enabled or not.
19 * @return {Promise<bool>}
21 RemoteCall
.isStepByStepEnabled = function() {
22 return new Promise(function(fulfill
) {
23 chrome
.commandLinePrivate
.hasSwitch(
24 'enable-file-manager-step-by-step-tests', fulfill
);
29 * Calls a remote test util in Files.app's extension. See: test_util.js.
31 * @param {string} func Function name.
32 * @param {?string} appId Target window's App ID or null for functions
33 * not requiring a window.
34 * @param {Array<*>} args Array of arguments.
35 * @param {function(*)=} opt_callback Callback handling the function's result.
36 * @return {Promise} Promise to be fulfilled with the result of the remote
39 RemoteCall
.prototype.callRemoteTestUtil
=
40 function(func
, appId
, args
, opt_callback
) {
41 return RemoteCall
.isStepByStepEnabled().then(function(stepByStep
) {
44 return new Promise(function(onFulfilled
) {
45 console
.info('Executing: ' + func
+ ' on ' + appId
+ ' with args: ');
47 console
.info('Type step() to continue...');
48 window
.step = function() {
50 onFulfilled(stepByStep
);
53 }).then(function(stepByStep
) {
54 return new Promise(function(onFulfilled
) {
55 chrome
.runtime
.sendMessage(
64 console
.info('Returned value:');
65 console
.info(arguments
);
68 opt_callback
.apply(null, arguments
);
69 onFulfilled(arguments
[0]);
76 * Waits until a window having the given ID prefix appears.
77 * @param {string} windowIdPrefix ID prefix of the requested window.
78 * @return {Promise} promise Promise to be fulfilled with a found window's ID.
80 RemoteCall
.prototype.waitForWindow = function(windowIdPrefix
) {
81 return repeatUntil(function() {
82 return this.callRemoteTestUtil('getWindows', null, []).
83 then(function(windows
) {
84 for (var id
in windows
) {
85 if (id
.indexOf(windowIdPrefix
) === 0)
88 return pending('Window with the prefix %s is not found.', windowIdPrefix
);
94 * Closes a window and waits until the window is closed.
96 * @param {string} windowId ID of the window to close.
97 * @return {Promise} promise Promise to be fulfilled with the result (true:
98 * success, false: failed).
100 RemoteCall
.prototype.closeWindowAndWait = function(windowId
) {
101 // Closes the window.
102 return this.callRemoteTestUtil('closeWindow', null, [windowId
]).then(
104 // Returns false when the closing is failed.
108 return repeatUntil(function() {
109 return this.callRemoteTestUtil('getWindows', null, []).then(
111 for (var id
in windows
) {
112 if (id
=== windowId
) {
113 // Window is still available. Continues waiting.
114 return pending('Window with the prefix %s is not found.',
118 // Window is not available. Closing is done successfully.
128 * Waits until the window turns to the given size.
129 * @param {string} windowId Target window ID.
130 * @param {number} width Requested width in pixels.
131 * @param {number} height Requested height in pixels.
133 RemoteCall
.prototype.waitForWindowGeometry
=
134 function(windowId
, width
, height
) {
135 return repeatUntil(function() {
136 return this.callRemoteTestUtil('getWindows', null, []).
137 then(function(windows
) {
138 if (!windows
[windowId
])
139 return pending('Window %s is not found.', windowId
);
140 if (windows
[windowId
].outerWidth
!== width
||
141 windows
[windowId
].outerHeight
!== height
) {
142 return pending('Expected window size is %j, but it is %j',
143 {width
: width
, height
: height
},
151 * Waits for the specified element appearing in the DOM.
152 * @param {string} windowId Target window ID.
153 * @param {string} query Query string for the element.
154 * @param {string=} opt_iframeQuery Query string for the iframe containing the
156 * @return {Promise} Promise to be fulfilled when the element appears.
158 RemoteCall
.prototype.waitForElement
=
159 function(windowId
, query
, opt_iframeQuery
) {
160 return repeatUntil(function() {
161 return this.callRemoteTestUtil(
164 [query
, opt_iframeQuery
]
165 ).then(function(elements
) {
166 if (elements
.length
> 0)
170 'Element %s (maybe in iframe %s) is not found.',
178 * Waits for the specified element leaving from the DOM.
179 * @param {string} windowId Target window ID.
180 * @param {string} query Query string for the element.
181 * @param {string=} opt_iframeQuery Query string for the iframe containing the
183 * @return {Promise} Promise to be fulfilled when the element is lost.
185 RemoteCall
.prototype.waitForElementLost
=
186 function(windowId
, query
, opt_iframeQuery
) {
187 return repeatUntil(function() {
188 return this.callRemoteTestUtil(
191 [query
, opt_iframeQuery
]
192 ).then(function(elements
) {
193 if (elements
.length
> 0)
194 return pending('Elements %j is still exists.', elements
);
201 * Sends a fake key down event.
202 * @param {string} windowId Window ID.
203 * @param {string} query Query for the target element.
204 * @param {string} keyIdentifer Key identifier.
205 * @param {boolean} ctrlKey Control key flag.
206 * @return {Promise} Promise to be fulfilled or rejected depending on the
209 RemoteCall
.prototype.fakeKeyDown
=
210 function(windowId
, query
, keyIdentifer
, ctrlKey
) {
211 var resultPromise
= this.callRemoteTestUtil(
212 'fakeKeyDown', windowId
, [query
, keyIdentifer
, ctrlKey
]);
213 return resultPromise
.then(function(result
) {
217 return Promise
.reject('Fail to fake key down.');
222 * Gets file entries just under the volume.
224 * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
225 * @param {Array<string>} names File name list.
226 * @return {Promise} Promise to be fulfilled with file entries or rejected
227 * depending on the result.
229 RemoteCall
.prototype.getFilesUnderVolume = function(volumeType
, names
) {
230 return this.callRemoteTestUtil(
231 'getFilesUnderVolume', null, [volumeType
, names
]);
235 * Waits for a single file.
236 * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
237 * @param {string} name File name.
238 * @return {!Promise} Promise to be fulfilled when the file had found.
240 RemoteCall
.prototype.waitForAFile = function(volumeType
, name
) {
241 return repeatUntil(function() {
242 return this.getFilesUnderVolume(volumeType
, [name
])
243 .then(function(urls
) {
244 if (urls
.length
=== 1)
246 return pending('"' + name
+ '" is not found.');
252 * Class to manipulate the window in the remote extension.
254 * @param {string} extensionId ID of extension to be manipulated.
255 * @extends {RemoteCall}
258 function RemoteCallFilesApp() {
259 RemoteCall
.apply(this, arguments
);
262 RemoteCallFilesApp
.prototype.__proto__
= RemoteCall
.prototype;
265 * Waits for the file list turns to the given contents.
266 * @param {string} windowId Target window ID.
267 * @param {Array<Array<string>>} expected Expected contents of file list.
268 * @param {{orderCheck:boolean=, ignoreLastModifiedTime:boolean=}=} opt_options
269 * Options of the comparison. If orderCheck is true, it also compares the
270 * order of files. If ignoreLastModifiedTime is true, it compares the file
271 * without its last modified time.
272 * @return {Promise} Promise to be fulfilled when the file list turns to the
275 RemoteCallFilesApp
.prototype.waitForFiles
=
276 function(windowId
, expected
, opt_options
) {
277 var options
= opt_options
|| {};
278 return repeatUntil(function() {
279 return this.callRemoteTestUtil(
280 'getFileList', windowId
, []).then(function(files
) {
281 if (!options
.orderCheck
) {
285 for (var i
= 0; i
< Math
.min(files
.length
, expected
.length
); i
++) {
286 if (options
.ignoreFileSize
) {
290 if (options
.ignoreLastModifiedTime
) {
295 if (!chrome
.test
.checkDeepEq(expected
, files
)) {
296 return pending('waitForFiles: expected: %j actual %j.',
305 * Waits until the number of files in the file list is changed from the given
307 * TODO(hirono): Remove the function.
309 * @param {string} windowId Target window ID.
310 * @param {number} lengthBefore Number of items visible before.
311 * @return {Promise} Promise to be fulfilled with the contents of files.
313 RemoteCallFilesApp
.prototype.waitForFileListChange
=
314 function(windowId
, lengthBefore
) {
315 return repeatUntil(function() {
316 return this.callRemoteTestUtil(
317 'getFileList', windowId
, []).then(function(files
) {
319 var notReadyRows
= files
.filter(function(row
) {
320 return row
.filter(function(cell
) { return cell
== '...'; }).length
;
322 if (notReadyRows
.length
=== 0 &&
323 files
.length
!== lengthBefore
&&
324 files
.length
!== 0) {
327 return pending('The number of file is %d. Not changed.', lengthBefore
);
334 * Waits until the given taskId appears in the executed task list.
335 * @param {string} windowId Target window ID.
336 * @param {string} taskId Task ID to watch.
337 * @return {Promise} Promise to be fulfilled when the task appears in the
338 * executed task list.
340 RemoteCallFilesApp
.prototype.waitUntilTaskExecutes
=
341 function(windowId
, taskId
) {
342 return repeatUntil(function() {
343 return this.callRemoteTestUtil('getExecutedTasks', windowId
, []).
344 then(function(executedTasks
) {
345 if (executedTasks
.indexOf(taskId
) === -1)
346 return pending('Executed task is %j', executedTasks
);
352 * Check if the next tabforcus'd element has the given ID or not.
353 * @param {string} windowId Target window ID.
354 * @param {string} elementId String of 'id' attribute which the next tabfocus'd
355 * element should have.
356 * @return {Promise} Promise to be fulfilled with the result.
358 RemoteCallFilesApp
.prototype.checkNextTabFocus
=
359 function(windowId
, elementId
) {
360 return remoteCall
.callRemoteTestUtil('fakeKeyDown',
362 ['body', 'U+0009', false]).then(
364 chrome
.test
.assertTrue(result
);
365 return remoteCall
.callRemoteTestUtil('getActiveElement',
368 }).then(function(element
) {
369 if (!element
|| !element
.attributes
['id'])
372 if (element
.attributes
['id'] === elementId
) {
375 console
.error('The ID of the element should be "' + elementId
+
376 '", but "' + element
.attributes
['id'] + '"');
383 * Waits until the current directory is changed.
384 * @param {string} windowId Target window ID.
385 * @param {string} expectedPath Path to be changed to.
386 * @return {Promise} Promise to be fulfilled when the current directory is
387 * changed to expectedPath.
389 RemoteCallFilesApp
.prototype.waitUntilCurrentDirectoryIsChanged
=
390 function(windowId
, expectedPath
) {
391 return repeatUntil(function () {
392 return this.callRemoteTestUtil('getBreadcrumbPath', windowId
, []).then(
394 if(path
!== expectedPath
)
395 return pending('Expected path is %s', expectedPath
);
401 * Class to manipulate the window in the remote extension.
403 * @param {string} extensionId ID of extension to be manipulated.
404 * @extends {RemoteCall}
407 function RemoteCallGallery() {
408 RemoteCall
.apply(this, arguments
);
411 RemoteCallGallery
.prototype.__proto__
= RemoteCall
.prototype;
414 * Waits until the expected image is shown.
416 * @param {document} document Document.
417 * @param {number} width Expected width of the image.
418 * @param {number} height Expected height of the image.
419 * @param {string|null} name Expected name of the image.
420 * @return {Promise} Promsie to be fulfilled when the check is passed.
422 RemoteCallGallery
.prototype.waitForSlideImage
=
423 function(windowId
, width
, height
, name
) {
426 expected
.width
= width
;
428 expected
.height
= height
;
430 expected
.name
= name
;
432 return repeatUntil(function() {
433 var query
= '.gallery[mode="slide"] .content canvas.fullres';
435 this.waitForElement(windowId
, '.filename-spacer input'),
436 this.waitForElement(windowId
, query
)
437 ]).then(function(args
) {
438 var nameBox
= args
[0];
439 var fullResCanvas
= args
[1];
441 if (width
&& fullResCanvas
)
442 actual
.width
= Number(fullResCanvas
.attributes
.width
);
443 if (height
&& fullResCanvas
)
444 actual
.height
= Number(fullResCanvas
.attributes
.height
);
446 actual
.name
= nameBox
.value
;
448 if (!chrome
.test
.checkDeepEq(expected
, actual
)) {
449 return pending('Slide mode state, expected is %j, actual is %j.',
457 RemoteCallGallery
.prototype.changeNameAndWait = function(windowId
, newName
) {
458 return this.callRemoteTestUtil('changeName', windowId
, [newName
]
460 return this.waitForSlideImage(windowId
, 0, 0, newName
);
465 * Shorthand for clicking an element.
466 * @param {AppWindow} appWindow Application window.
467 * @param {string} query Query for the element.
468 * @param {Promise} Promise to be fulfilled with the clicked element.
470 RemoteCallGallery
.prototype.waitAndClickElement = function(windowId
, query
) {
471 return this.waitForElement(windowId
, query
).then(function(element
) {
472 return this.callRemoteTestUtil('fakeMouseClick', windowId
, [query
])
473 .then(function() { return element
; });
478 * Waits for the "Press Enter" message.
480 * @param {AppWindow} appWindow App window.
481 * @return {Promise} Promise to be fulfilled when the element appears.
483 RemoteCallGallery
.prototype.waitForPressEnterMessage = function(appId
) {
484 return this.waitForElement(appId
, '.prompt-wrapper .prompt').
485 then(function(element
) {
486 chrome
.test
.assertEq(
487 'Press Enter when done', element
.text
.trim());
492 * Shorthand for selecting an image in thumbnail mode.
493 * @param {string} appId App id.
494 * @param {string} name File name to be selected.
495 * @return {!Promise<boolean>} A promise which will be resolved with true if the
496 * thumbnail has clicked. This method does not guarantee whether the
497 * thumbnail has actually selected or not.
499 RemoteCallGallery
.prototype.selectImageInThumbnailMode = function(appId
, name
) {
500 return this.callRemoteTestUtil('fakeMouseClick', appId
,
501 ['.thumbnail-view > ul > li[title="' + name
+ '"] > .selection.frame']);