Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(385)

Side by Side Diff: chrome/browser/resources/file_manager/js/test_util.js

Issue 39123003: [Files.app] Split the JavaScript files into subdirectories: common, background, and foreground (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed test failure. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 /**
6 * Namespace for test related things.
7 */
8 var test = test || {};
9
10 /**
11 * Namespace for test utility functions.
12 *
13 * Public functions in the test.util.sync and the test.util.async namespaces are
14 * published to test cases and can be called by using callRemoteTestUtil. The
15 * arguments are serialized as JSON internally. If application ID is passed to
16 * callRemoteTestUtil, the content window of the application is added as the
17 * first argument. The functions in the test.util.async namespace are passed the
18 * callback function as the last argument.
19 */
20 test.util = {};
21
22 /**
23 * Namespace for synchronous utility functions.
24 */
25 test.util.sync = {};
26
27 /**
28 * Namespace for asynchronous utility functions.
29 */
30 test.util.async = {};
31
32 /**
33 * Extension ID of the testing extension.
34 * @type {string}
35 * @const
36 */
37 test.util.TESTING_EXTENSION_ID = 'oobinhbdbiehknkpbpejbbpdbkdjmoco';
38
39 /**
40 * Interval of checking a condition in milliseconds.
41 * @type {number}
42 * @const
43 * @private
44 */
45 test.util.WAITTING_INTERVAL_ = 50;
46
47 /**
48 * Repeats the function until it returns true.
49 * @param {function()} closure Function expected to return true.
50 * @private
51 */
52 test.util.repeatUntilTrue_ = function(closure) {
53 var step = function() {
54 if (closure())
55 return;
56 setTimeout(step, test.util.WAITTING_INTERVAL_);
57 };
58 step();
59 };
60
61 /**
62 * Opens the main Files.app's window and waits until it is ready.
63 *
64 * @param {Object} appState App state.
65 * @param {function(string)} callback Completion callback with the new window's
66 * App ID.
67 */
68 test.util.async.openMainWindow = function(appState, callback) {
69 var steps = [
70 function() {
71 launchFileManager(appState,
72 undefined, // opt_type
73 undefined, // opt_id
74 steps.shift());
75 },
76 function(appId) {
77 test.util.repeatUntilTrue_(function() {
78 if (!appWindows[appId])
79 return false;
80 var contentWindow = appWindows[appId].contentWindow;
81 var table = contentWindow.document.querySelector('#detail-table');
82 if (!table)
83 return false;
84 callback(appId);
85 return true;
86 });
87 }
88 ];
89 steps.shift()();
90 };
91
92 /**
93 * Waits for a window with the specified App ID prefix. Eg. `files` will match
94 * windows such as files#0, files#1, etc.
95 *
96 * @param {string} appIdPrefix ID prefix of the requested window.
97 * @param {function(string)} callback Completion callback with the new window's
98 * App ID.
99 */
100 test.util.async.waitForWindow = function(appIdPrefix, callback) {
101 test.util.repeatUntilTrue_(function() {
102 for (var appId in appWindows) {
103 if (appId.indexOf(appIdPrefix) == 0 &&
104 appWindows[appId].contentWindow) {
105 callback(appId);
106 return true;
107 }
108 }
109 return false;
110 });
111 };
112
113 /**
114 * Gets a document in the Files.app's window, including iframes.
115 *
116 * @param {Window} contentWindow Window to be used.
117 * @param {string=} opt_iframeQuery Query for the iframe.
118 * @return {Document=} Returns the found document or undefined if not found.
119 * @private
120 */
121 test.util.sync.getDocument_ = function(contentWindow, opt_iframeQuery) {
122 if (opt_iframeQuery) {
123 var iframe = contentWindow.document.querySelector(opt_iframeQuery);
124 return iframe && iframe.contentWindow && iframe.contentWindow.document;
125 }
126
127 return contentWindow.document;
128 };
129
130 /**
131 * Gets total Javascript error count from each app window.
132 * @return {number} Error count.
133 */
134 test.util.sync.getErrorCount = function() {
135 var totalCount = JSErrorCount;
136 for (var appId in appWindows) {
137 var contentWindow = appWindows[appId].contentWindow;
138 if (contentWindow.JSErrorCount)
139 totalCount += contentWindow.JSErrorCount;
140 }
141 return totalCount;
142 };
143
144 /**
145 * Resizes the window to the specified dimensions.
146 *
147 * @param {Window} contentWindow Window to be tested.
148 * @param {number} width Window width.
149 * @param {number} height Window height.
150 * @return {boolean} True for success.
151 */
152 test.util.sync.resizeWindow = function(contentWindow, width, height) {
153 appWindows[contentWindow.appID].resizeTo(width, height);
154 return true;
155 };
156
157 /**
158 * Returns an array with the files currently selected in the file manager.
159 *
160 * @param {Window} contentWindow Window to be tested.
161 * @return {Array.<string>} Array of selected files.
162 */
163 test.util.sync.getSelectedFiles = function(contentWindow) {
164 var table = contentWindow.document.querySelector('#detail-table');
165 var rows = table.querySelectorAll('li');
166 var selected = [];
167 for (var i = 0; i < rows.length; ++i) {
168 if (rows[i].hasAttribute('selected')) {
169 selected.push(
170 rows[i].querySelector('.filename-label').textContent);
171 }
172 }
173 return selected;
174 };
175
176 /**
177 * Returns an array with the files on the file manager's file list.
178 *
179 * @param {Window} contentWindow Window to be tested.
180 * @return {Array.<Array.<string>>} Array of rows.
181 */
182 test.util.sync.getFileList = function(contentWindow) {
183 var table = contentWindow.document.querySelector('#detail-table');
184 var rows = table.querySelectorAll('li');
185 var fileList = [];
186 for (var j = 0; j < rows.length; ++j) {
187 var row = rows[j];
188 fileList.push([
189 row.querySelector('.filename-label').textContent,
190 row.querySelector('.size').textContent,
191 row.querySelector('.type').textContent,
192 row.querySelector('.date').textContent
193 ]);
194 }
195 return fileList;
196 };
197
198 /**
199 * Checkes if the given label and path of the volume are selected.
200 * @param {Window} contentWindow Window to be tested.
201 * @param {string} label Correct label the selected volume should have.
202 * @param {string} path Correct path the selected volume should have.
203 * @return {boolean} True for success.
204 */
205 test.util.sync.checkSelectedVolume = function(contentWindow, label, path) {
206 var list = contentWindow.document.querySelector('#navigation-list');
207 var rows = list.querySelectorAll('li');
208 var selected = [];
209 for (var i = 0; i < rows.length; ++i) {
210 if (rows[i].hasAttribute('selected'))
211 selected.push(rows[i]);
212 }
213 // Selected item must be one.
214 if (selected.length !== 1)
215 return false;
216
217 if (selected[0].modelItem.path !== path ||
218 selected[0].querySelector('.root-label').textContent !== label) {
219 return false;
220 }
221
222 return true;
223 };
224
225 /**
226 * Waits until the window is set to the specified dimensions.
227 *
228 * @param {Window} contentWindow Window to be tested.
229 * @param {number} width Requested width.
230 * @param {number} height Requested height.
231 * @param {function(Object)} callback Success callback with the dimensions.
232 */
233 test.util.async.waitForWindowGeometry = function(
234 contentWindow, width, height, callback) {
235 test.util.repeatUntilTrue_(function() {
236 if (contentWindow.innerWidth == width &&
237 contentWindow.innerHeight == height) {
238 callback({width: width, height: height});
239 return true;
240 }
241 return false;
242 });
243 };
244
245 /**
246 * Waits for an element and returns it as an array of it's attributes.
247 *
248 * @param {Window} contentWindow Window to be tested.
249 * @param {string} targetQuery Query to specify the element.
250 * @param {?string} iframeQuery Iframe selector or null if no iframe.
251 * @param {boolean=} opt_inverse True if the function should return if the
252 * element disappears, instead of appearing.
253 * @param {function(Object)} callback Callback with a hash array of attributes
254 * and contents as text.
255 */
256 test.util.async.waitForElement = function(
257 contentWindow, targetQuery, iframeQuery, opt_inverse, callback) {
258 test.util.repeatUntilTrue_(function() {
259 var doc = test.util.sync.getDocument_(contentWindow, iframeQuery);
260 if (!doc)
261 return false;
262 var element = doc.querySelector(targetQuery);
263 if (!element)
264 return !!opt_inverse;
265 var attributes = {};
266 for (var i = 0; i < element.attributes.length; i++) {
267 attributes[element.attributes[i].nodeName] =
268 element.attributes[i].nodeValue;
269 }
270 var text = element.textContent;
271 callback({attributes: attributes, text: text});
272 return !opt_inverse;
273 });
274 };
275
276 /**
277 * Calls getFileList until the number of displayed files is different from
278 * lengthBefore.
279 *
280 * @param {Window} contentWindow Window to be tested.
281 * @param {number} lengthBefore Number of items visible before.
282 * @param {function(Array.<Array.<string>>)} callback Change callback.
283 */
284 test.util.async.waitForFileListChange = function(
285 contentWindow, lengthBefore, callback) {
286 test.util.repeatUntilTrue_(function() {
287 var files = test.util.sync.getFileList(contentWindow);
288 files.sort();
289 var notReadyRows = files.filter(function(row) {
290 return row.filter(function(cell) { return cell == '...'; }).length;
291 });
292 if (notReadyRows.length === 0 &&
293 files.length !== lengthBefore &&
294 files.length !== 0) {
295 callback(files);
296 return true;
297 } else {
298 return false;
299 }
300 });
301 };
302
303 /**
304 * Returns an array of items on the file manager's autocomplete list.
305 *
306 * @param {Window} contentWindow Window to be tested.
307 * @return {Array.<string>} Array of items.
308 */
309 test.util.sync.getAutocompleteList = function(contentWindow) {
310 var list = contentWindow.document.querySelector('#autocomplete-list');
311 var lines = list.querySelectorAll('li');
312 var items = [];
313 for (var j = 0; j < lines.length; ++j) {
314 var line = lines[j];
315 items.push(line.innerText);
316 }
317 return items;
318 };
319
320 /**
321 * Performs autocomplete with the given query and waits until at least
322 * |numExpectedItems| items are shown, including the first item which
323 * always looks like "'<query>' - search Drive".
324 *
325 * @param {Window} contentWindow Window to be tested.
326 * @param {string} query Query used for autocomplete.
327 * @param {number} numExpectedItems number of items to be shown.
328 * @param {function(Array.<string>)} callback Change callback.
329 */
330 test.util.async.performAutocompleteAndWait = function(
331 contentWindow, query, numExpectedItems, callback) {
332 // Dispatch a 'focus' event to the search box so that the autocomplete list
333 // is attached to the search box. Note that calling searchBox.focus() won't
334 // dispatch a 'focus' event.
335 var searchBox = contentWindow.document.querySelector('#search-box input');
336 var focusEvent = contentWindow.document.createEvent('Event');
337 focusEvent.initEvent('focus', true /* bubbles */, true /* cancelable */);
338 searchBox.dispatchEvent(focusEvent);
339
340 // Change the value of the search box and dispatch an 'input' event so that
341 // the autocomplete query is processed.
342 searchBox.value = query;
343 var inputEvent = contentWindow.document.createEvent('Event');
344 inputEvent.initEvent('input', true /* bubbles */, true /* cancelable */);
345 searchBox.dispatchEvent(inputEvent);
346
347 test.util.repeatUntilTrue_(function() {
348 var items = test.util.sync.getAutocompleteList(contentWindow);
349 if (items.length >= numExpectedItems) {
350 callback(items);
351 return true;
352 } else {
353 return false;
354 }
355 });
356 };
357
358 /**
359 * Waits until a dialog with an OK button is shown and accepts it.
360 *
361 * @param {Window} contentWindow Window to be tested.
362 * @param {function()} callback Success callback.
363 */
364 test.util.async.waitAndAcceptDialog = function(contentWindow, callback) {
365 test.util.repeatUntilTrue_(function() {
366 var button = contentWindow.document.querySelector('.cr-dialog-ok');
367 if (!button)
368 return false;
369 button.click();
370 // Wait until the dialog is removed from the DOM.
371 test.util.repeatUntilTrue_(function() {
372 if (contentWindow.document.querySelector('.cr-dialog-container'))
373 return false;
374 callback();
375 return true;
376 });
377 return true;
378 });
379 };
380
381 /**
382 * Fakes pressing the down arrow until the given |filename| is selected.
383 *
384 * @param {Window} contentWindow Window to be tested.
385 * @param {string} filename Name of the file to be selected.
386 * @return {boolean} True if file got selected, false otherwise.
387 */
388 test.util.sync.selectFile = function(contentWindow, filename) {
389 var table = contentWindow.document.querySelector('#detail-table');
390 var rows = table.querySelectorAll('li');
391 for (var index = 0; index < rows.length; ++index) {
392 test.util.sync.fakeKeyDown(contentWindow, '#file-list', 'Down', false);
393 var selection = test.util.sync.getSelectedFiles(contentWindow);
394 if (selection.length === 1 && selection[0] === filename)
395 return true;
396 }
397 console.error('Failed to select file "' + filename + '"');
398 return false;
399 };
400
401 /**
402 * Open the file by selectFile and fakeMouseDoubleClick.
403 *
404 * @param {Window} contentWindow Window to be tested.
405 * @param {string} filename Name of the file to be opened.
406 * @return {boolean} True if file got selected and a double click message is
407 * sent, false otherwise.
408 */
409 test.util.sync.openFile = function(contentWindow, filename) {
410 var query = '#file-list li.table-row[selected] .filename-label span';
411 return test.util.sync.selectFile(contentWindow, filename) &&
412 test.util.sync.fakeMouseDoubleClick(contentWindow, query);
413 };
414
415 /**
416 * Selects a volume specified by its icon name
417 *
418 * @param {Window} contentWindow Window to be tested.
419 * @param {string} iconName Name of the volume icon.
420 * @param {function(boolean)} callback Callback function to notify the caller
421 * whether the target is found and mousedown and click events are sent.
422 */
423 test.util.async.selectVolume = function(contentWindow, iconName, callback) {
424 var query = '[volume-type-icon=' + iconName + ']';
425 var driveQuery = '[volume-type-icon=drive]';
426 var isDriveSubVolume = iconName == 'drive_recent' ||
427 iconName == 'drive_shared_with_me' ||
428 iconName == 'drive_offline';
429 var preSelection = false;
430 var steps = {
431 checkQuery: function() {
432 if (contentWindow.document.querySelector(query)) {
433 steps.sendEvents();
434 return;
435 }
436 // If the target volume is sub-volume of drive, we must click 'drive'
437 // before clicking the sub-item.
438 if (!preSelection) {
439 if (!isDriveSubVolume) {
440 callback(false);
441 return;
442 }
443 if (!(test.util.sync.fakeMouseDown(contentWindow, driveQuery) &&
444 test.util.sync.fakeMouseClick(contentWindow, driveQuery))) {
445 callback(false);
446 return;
447 }
448 preSelection = true;
449 }
450 setTimeout(steps.checkQuery, 50);
451 },
452 sendEvents: function() {
453 // To change the selected volume, we have to send both events 'mousedown'
454 // and 'click' to the navigation list.
455 callback(test.util.sync.fakeMouseDown(contentWindow, query) &&
456 test.util.sync.fakeMouseClick(contentWindow, query));
457 }
458 };
459 steps.checkQuery();
460 };
461
462 /**
463 * Waits the contents of file list becomes to equal to expected contents.
464 *
465 * @param {Window} contentWindow Window to be tested.
466 * @param {Array.<Array.<string>>} expected Expected contents of file list.
467 * @param {{orderCheck:boolean=, ignoreLastModifiedTime:boolean=}=} opt_options
468 * Options of the comparison. If orderCheck is true, it also compares the
469 * order of files. If ignoreLastModifiedTime is true, it compares the file
470 * without its last modified time.
471 * @param {function()} callback Callback function to notify the caller that
472 * expected files turned up.
473 */
474 test.util.async.waitForFiles = function(
475 contentWindow, expected, opt_options, callback) {
476 var options = opt_options || {};
477 test.util.repeatUntilTrue_(function() {
478 var files = test.util.sync.getFileList(contentWindow);
479 if (!options.orderCheck) {
480 files.sort();
481 expected.sort();
482 }
483 if (options.ignoreLastModifiedTime) {
484 for (var i = 0; i < Math.min(files.length, expected.length); i++) {
485 files[i][3] = '';
486 expected[i][3] = '';
487 }
488 }
489 if (chrome.test.checkDeepEq(expected, files)) {
490 callback(true);
491 return true;
492 }
493 return false;
494 });
495 };
496
497 /**
498 * Executes Javascript code on a webview and returns the result.
499 *
500 * @param {Window} contentWindow Window to be tested.
501 * @param {string} webViewQuery Selector for the web view.
502 * @param {string} code Javascript code to be executed within the web view.
503 * @param {function(*)} callback Callback function with results returned by the
504 * script.
505 */
506 test.util.async.executeScriptInWebView = function(
507 contentWindow, webViewQuery, code, callback) {
508 var webView = contentWindow.document.querySelector(webViewQuery);
509 webView.executeScript({code: code}, callback);
510 };
511
512 /**
513 * Sends an event to the element specified by |targetQuery|.
514 *
515 * @param {Window} contentWindow Window to be tested.
516 * @param {string} targetQuery Query to specify the element.
517 * @param {Event} event Event to be sent.
518 * @param {string=} opt_iframeQuery Optional iframe selector.
519 * @return {boolean} True if the event is sent to the target, false otherwise.
520 */
521 test.util.sync.sendEvent = function(
522 contentWindow, targetQuery, event, opt_iframeQuery) {
523 var doc = test.util.sync.getDocument_(contentWindow, opt_iframeQuery);
524 if (doc) {
525 var target = doc.querySelector(targetQuery);
526 if (target) {
527 target.dispatchEvent(event);
528 return true;
529 }
530 }
531 console.error('Target element for ' + targetQuery + ' not found.');
532 return false;
533 };
534
535 /**
536 * Sends an fake event having the specified type to the target query.
537 *
538 * @param {Window} contentWindow Window to be tested.
539 * @param {string} targetQuery Query to specify the element.
540 * @param {string} event Type of event.
541 * @return {boolean} True if the event is sent to the target, false otherwise.
542 */
543 test.util.sync.fakeEvent = function(contentWindow, targetQuery, event) {
544 return test.util.sync.sendEvent(
545 contentWindow, targetQuery, new Event(event));
546 };
547
548 /**
549 * Sends a fake key event to the element specified by |targetQuery| with the
550 * given |keyIdentifier| and optional |ctrl| modifier to the file manager.
551 *
552 * @param {Window} contentWindow Window to be tested.
553 * @param {string} targetQuery Query to specify the element.
554 * @param {string} keyIdentifier Identifier of the emulated key.
555 * @param {boolean} ctrl Whether CTRL should be pressed, or not.
556 * @param {string=} opt_iframeQuery Optional iframe selector.
557 * @return {boolean} True if the event is sent to the target, false otherwise.
558 */
559 test.util.sync.fakeKeyDown = function(
560 contentWindow, targetQuery, keyIdentifier, ctrl, opt_iframeQuery) {
561 var event = new KeyboardEvent(
562 'keydown',
563 { bubbles: true, keyIdentifier: keyIdentifier, ctrlKey: ctrl });
564 return test.util.sync.sendEvent(
565 contentWindow, targetQuery, event, opt_iframeQuery);
566 };
567
568 /**
569 * Sends a fake mouse click event (left button, single click) to the element
570 * specified by |targetQuery|.
571 *
572 * @param {Window} contentWindow Window to be tested.
573 * @param {string} targetQuery Query to specify the element.
574 * @param {string=} opt_iframeQuery Optional iframe selector.
575 * @return {boolean} True if the event is sent to the target, false otherwise.
576 */
577 test.util.sync.fakeMouseClick = function(
578 contentWindow, targetQuery, opt_iframeQuery) {
579 var event = new MouseEvent('click', { bubbles: true, detail: 1 });
580 return test.util.sync.sendEvent(
581 contentWindow, targetQuery, event, opt_iframeQuery);
582 };
583
584 /**
585 * Simulates a fake double click event (left button) to the element specified by
586 * |targetQuery|.
587 *
588 * @param {Window} contentWindow Window to be tested.
589 * @param {string} targetQuery Query to specify the element.
590 * @param {string=} opt_iframeQuery Optional iframe selector.
591 * @return {boolean} True if the event is sent to the target, false otherwise.
592 */
593 test.util.sync.fakeMouseDoubleClick = function(
594 contentWindow, targetQuery, opt_iframeQuery) {
595 // Double click is always preceded with a single click.
596 if (!test.util.sync.fakeMouseClick(
597 contentWindow, targetQuery, opt_iframeQuery)) {
598 return false;
599 }
600
601 // Send the second click event, but with detail equal to 2 (number of clicks)
602 // in a row.
603 var event = new MouseEvent('click', { bubbles: true, detail: 2 });
604 if (!test.util.sync.sendEvent(
605 contentWindow, targetQuery, event, opt_iframeQuery)) {
606 return false;
607 }
608
609 // Send the double click event.
610 var event = new MouseEvent('dblclick', { bubbles: true });
611 if (!test.util.sync.sendEvent(
612 contentWindow, targetQuery, event, opt_iframeQuery)) {
613 return false;
614 }
615
616 return true;
617 };
618
619 /**
620 * Sends a fake mouse down event to the element specified by |targetQuery|.
621 *
622 * @param {Window} contentWindow Window to be tested.
623 * @param {string} targetQuery Query to specify the element.
624 * @param {string=} opt_iframeQuery Optional iframe selector.
625 * @return {boolean} True if the event is sent to the target, false otherwise.
626 */
627 test.util.sync.fakeMouseDown = function(
628 contentWindow, targetQuery, opt_iframeQuery) {
629 var event = new MouseEvent('mousedown', { bubbles: true });
630 return test.util.sync.sendEvent(
631 contentWindow, targetQuery, event, opt_iframeQuery);
632 };
633
634 /**
635 * Sends a fake mouse up event to the element specified by |targetQuery|.
636 *
637 * @param {Window} contentWindow Window to be tested.
638 * @param {string} targetQuery Query to specify the element.
639 * @param {string=} opt_iframeQuery Optional iframe selector.
640 * @return {boolean} True if the event is sent to the target, false otherwise.
641 */
642 test.util.sync.fakeMouseUp = function(
643 contentWindow, targetQuery, opt_iframeQuery) {
644 var event = new MouseEvent('mouseup', { bubbles: true });
645 return test.util.sync.sendEvent(
646 contentWindow, targetQuery, event, opt_iframeQuery);
647 };
648
649 /**
650 * Selects |filename| and fakes pressing Ctrl+C, Ctrl+V (copy, paste).
651 *
652 * @param {Window} contentWindow Window to be tested.
653 * @param {string} filename Name of the file to be copied.
654 * @return {boolean} True if copying got simulated successfully. It does not
655 * say if the file got copied, or not.
656 */
657 test.util.sync.copyFile = function(contentWindow, filename) {
658 if (!test.util.sync.selectFile(contentWindow, filename))
659 return false;
660 // Ctrl+C and Ctrl+V
661 test.util.sync.fakeKeyDown(contentWindow, '#file-list', 'U+0043', true);
662 test.util.sync.fakeKeyDown(contentWindow, '#file-list', 'U+0056', true);
663 return true;
664 };
665
666 /**
667 * Selects |filename| and fakes pressing the Delete key.
668 *
669 * @param {Window} contentWindow Window to be tested.
670 * @param {string} filename Name of the file to be deleted.
671 * @return {boolean} True if deleting got simulated successfully. It does not
672 * say if the file got deleted, or not.
673 */
674 test.util.sync.deleteFile = function(contentWindow, filename) {
675 if (!test.util.sync.selectFile(contentWindow, filename))
676 return false;
677 // Delete
678 test.util.sync.fakeKeyDown(contentWindow, '#file-list', 'U+007F', false);
679 return true;
680 };
681
682 /**
683 * Wait for the elements' style to be changed as the expected values. The
684 * queries argument is a list of object that have the query property and the
685 * styles property. The query property is a string query to specify the
686 * element. The styles property is a string map of the style name and its
687 * expected value.
688 *
689 * @param {Window} contentWindow Window to be tested.
690 * @param {Array.<object>} queries Queries that specifies the elements and
691 * expected styles.
692 * @param {function()} callback Callback function to be notified the change of
693 * the styles.
694 */
695 test.util.async.waitForStyles = function(contentWindow, queries, callback) {
696 test.util.repeatUntilTrue_(function() {
697 for (var i = 0; i < queries.length; i++) {
698 var element = contentWindow.document.querySelector(queries[i].query);
699 var styles = queries[i].styles;
700 for (var name in styles) {
701 if (contentWindow.getComputedStyle(element)[name] != styles[name])
702 return false;
703 }
704 }
705 callback();
706 return true;
707 });
708 };
709
710 /**
711 * Execute a command on the document in the specified window.
712 *
713 * @param {Window} contentWindow Window to be tested.
714 * @param {string} command Command name.
715 * @return {boolean} True if the command is executed successfully.
716 */
717 test.util.sync.execCommand = function(contentWindow, command) {
718 return contentWindow.document.execCommand(command);
719 };
720
721 /**
722 * Registers message listener, which runs test utility functions.
723 */
724 test.util.registerRemoteTestUtils = function() {
725 // Register the message listener.
726 var onMessage = chrome.runtime ? chrome.runtime.onMessageExternal :
727 chrome.extension.onMessageExternal;
728 // Return true for asynchronous functions and false for synchronous.
729 onMessage.addListener(function(request, sender, sendResponse) {
730 // Check the sender.
731 if (sender.id != test.util.TESTING_EXTENSION_ID) {
732 console.error('The testing extension must be white-listed.');
733 return false;
734 }
735 // Set a global flag that we are in tests, so other components are aware
736 // of it.
737 window.IN_TEST = true;
738 // Check the function name.
739 if (!request.func || request.func[request.func.length - 1] == '_') {
740 request.func = '';
741 }
742 // Prepare arguments.
743 var args = request.args.slice(); // shallow copy
744 if (request.appId) {
745 if (!appWindows[request.appId]) {
746 console.error('Specified window not found.');
747 return false;
748 }
749 args.unshift(appWindows[request.appId].contentWindow);
750 }
751 // Call the test utility function and respond the result.
752 if (test.util.async[request.func]) {
753 args[test.util.async[request.func].length - 1] = function() {
754 console.debug('Received the result of ' + request.func);
755 sendResponse.apply(null, arguments);
756 };
757 console.debug('Waiting for the result of ' + request.func);
758 test.util.async[request.func].apply(null, args);
759 return true;
760 } else if (test.util.sync[request.func]) {
761 sendResponse(test.util.sync[request.func].apply(null, args));
762 return false;
763 } else {
764 console.error('Invalid function name.');
765 return false;
766 }
767 });
768 };
769
770 // Register the test utils.
771 test.util.registerRemoteTestUtils();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698