diff options
-rw-r--r-- | chrome/browser/resources/file_manager/js/file_manager.js | 180 | ||||
-rw-r--r-- | chrome/browser/resources/file_manager/js/file_manager_pyauto.js | 146 | ||||
-rw-r--r-- | chrome/browser/resources/file_manager/js/harness.js | 3 | ||||
-rw-r--r-- | chrome/browser/resources/file_manager/main.html | 1 | ||||
-rw-r--r-- | chrome/test/functional/PYAUTO_TESTS | 5 | ||||
-rw-r--r-- | chrome/test/functional/chromeos_file_browser.py | 232 | ||||
-rw-r--r-- | chrome/test/functional/execute_javascript.py | 20 | ||||
-rw-r--r-- | chrome/test/functional/test_utils.py | 12 | ||||
-rw-r--r-- | chrome/test/pyautolib/chromeos/file_browser.py | 166 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 146 |
10 files changed, 867 insertions, 44 deletions
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js index 9e05ad1..f8198dd 100644 --- a/chrome/browser/resources/file_manager/js/file_manager.js +++ b/chrome/browser/resources/file_manager/js/file_manager.js @@ -123,6 +123,10 @@ function FileManager(dialogDom, filesystem, rootEntries) { var self = this; + // The list of callbacks to be invoked during the directory rescan after + // all paste tasks are complete. + this.pasteSuccessCallbacks_ = []; + // The list of active mount points to distinct them from other directories. chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { self.mountPoints_ = mountPoints; @@ -1020,6 +1024,18 @@ FileManager.prototype = { if (this.clipboard_.isCut) this.clipboard_ = null; this.updateCommands_(); + self = this; + this.rescanDirectory_(function() { + var callback; + while (callback = self.pasteSuccessCallbacks_.shift()) { + try { + callback(); + } catch (ex) { + console.error('Caught exception while inovking callback: ' + + callback, ex); + } + } + }); } else if (event.reason == 'ERROR') { switch (event.error.reason) { @@ -1040,16 +1056,16 @@ FileManager.prototype = { this.showButterError(strf('PASTE_UNEXPECTED_ERROR', event.error)); break; } - + this.rescanDirectory_(); } else if (event.reason == 'CANCELLED') { this.showButter(str('PASTE_CANCELLED')); + this.rescanDirectory_(); } else { console.log('Unknown event reason: ' + event.reason); + this.rescanDirectory_(); } - this.rescanDirectory_(); }; - /** * Respond to a command being executed. */ @@ -1624,6 +1640,8 @@ FileManager.prototype = { // Automated tests need to wait for this, otherwise we crash in browser_test // cleanup because the worker process still has URL requests in-flight. chrome.test.sendMessage('worker-initialized'); + // PyAuto tests monitor this state by polling this variable + this.workerInitialized_ = true; }; FileManager.prototype.onMetadataLog_ = function(arglist) { @@ -2201,6 +2219,85 @@ FileManager.prototype = { }; /** + * Add the file/directory with given name to the current selection. + * + * @param {string} name The name of the entry to select. + * @return {boolean} Whether entry exists. + */ + FileManager.prototype.addItemToSelection = function(name) { + var entryExists = false; + for (var i = 0; i < this.dataModel_.length; i++) { + if (this.dataModel_.item(i).name == name) { + this.currentList_.selectionModel.setIndexSelected(i, true); + this.currentList_.scrollIndexIntoView(i); + this.currentList_.focus(); + entryExists = true; + break; + } + } + return entryExists; + } + + /** + * Return the name of the entries in the current directory + * + * @return {object} Array of entry names. + */ + FileManager.prototype.listDirectory = function() { + var list = [] + for (var i = 0; i < this.dataModel_.length; i++) { + list.push(this.dataModel_.item(i).name); + } + return list; + } + + /** + * Open the item selected + */ + FileManager.prototype.doOpen = function() { + switch (this.dialogType_) { + case FileManager.DialogType.SELECT_FOLDER: + case FileManager.DialogType.SELECT_OPEN_FILE: + case FileManager.DialogType.SELECT_OPEN_MULTI_FILE: + this.onOk_(); + break; + default: + throw new Error('Cannot open an item in this dialog type.'); + } + } + + /** + * Save the item using the given name + * + * @param {string} name The name given to item to be saved + */ + FileManager.prototype.doSaveAs = function(name) { + if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { + this.filenameInput_.value = name; + this.onOk_(); + } + else { + throw new Error('Cannot save an item in this dialog type.'); + } + } + + /** + * Return full path of the current directory + */ + FileManager.prototype.getCurrentDirectory = function() { + return this.currentDirEntry_.fullPath; + } + + /** + * Used by tests to wait before interacting with the file maanager + */ + FileManager.prototype.isInitialized = function() { + var initialized = (this.workerInitialized_ != null) && + (this.directoryChanged_ != null); + return initialized; + } + + /** * Change the current directory to the directory represented by a * DirectoryEntry. * @@ -2215,7 +2312,8 @@ FileManager.prototype = { */ FileManager.prototype.changeDirectoryEntry = function(dirEntry, opt_saveHistory, - opt_selectedEntry) { + opt_selectedEntry, + opt_callback) { if (typeof opt_saveHistory == 'undefined') { opt_saveHistory = true; } else { @@ -2246,6 +2344,7 @@ FileManager.prototype = { e.newDirEntry = dirEntry; e.saveHistory = opt_saveHistory; e.selectedEntry = opt_selectedEntry; + e.opt_callback = opt_callback; this.currentDirEntry_ = dirEntry; this.dispatchEvent(e); } @@ -2265,11 +2364,13 @@ FileManager.prototype = { */ FileManager.prototype.changeDirectory = function(path, opt_saveHistory, - opt_selectedEntry) { + opt_selectedEntry, + opt_callback) { if (path == '/') return this.changeDirectoryEntry(this.filesystem_.root, opt_saveHistory, - opt_selectedEntry); + opt_selectedEntry, + opt_callback); var self = this; @@ -2277,7 +2378,7 @@ FileManager.prototype = { path, {create: false}, function(dirEntry) { self.changeDirectoryEntry( - dirEntry, opt_saveHistory, opt_selectedEntry); + dirEntry, opt_saveHistory, opt_selectedEntry, opt_callback); }, function(err) { console.error('Error changing directory to: ' + path + ', ' + err); @@ -2294,7 +2395,7 @@ FileManager.prototype = { }); }; - FileManager.prototype.deleteEntries = function(entries, force) { + FileManager.prototype.deleteEntries = function(entries, force, opt_callback) { if (!force) { var self = this; var msg; @@ -2314,7 +2415,10 @@ FileManager.prototype = { var self = this; function onDelete() { if (--count == 0) - self.rescanDirectory_(); + self.rescanDirectory_(function() { + if (opt_callback) + opt_callback(); + }); } for (var i = 0; i < entries.length; i++) { @@ -2383,13 +2487,13 @@ FileManager.prototype = { /** * Queue up a file copy operation based on the current clipboard. */ - FileManager.prototype.pasteFromClipboard = function(successCallback, - errorCallback) { + FileManager.prototype.pasteFromClipboard = function(successCallback) { if (!this.clipboard_) return null; this.showButter(str('PASTE_STARTED'), {timeout: 0}); + this.pasteSuccessCallbacks_.push(successCallback); this.copyManager_.queueCopy(this.clipboard_.sourceDirEntry, this.currentDirEntry_, this.clipboard_.entries, @@ -2639,9 +2743,18 @@ FileManager.prototype = { this.rescanDirectory_(function() { if (event.selectedEntry) self.selectEntry(event.selectedEntry); + if (event.opt_callback) { + try { + event.opt_callback(); + } catch (ex) { + console.error('Caught exception while inovking callback: ', ex); + } + } // For tests that open the dialog to empty directories, everything // is loaded at this point. chrome.test.sendMessage('directory-change-complete'); + // PyAuto tests monitor this state by polling this variable + self.directoryChanged_ = true; }); }; @@ -2902,23 +3015,14 @@ FileManager.prototype = { this.cancelRename_(); }; - FileManager.prototype.commitRename_ = function() { - var entry = this.renameInput_.currentEntry; - var newName = this.renameInput_.value; - if (!this.validateFileName_(newName)) - return; - - this.renameInput_.currentEntry = null; - this.lastLabelClick_ = null; - - if (this.renameInput_.parentNode) - this.renameInput_.parentNode.removeChild(this.renameInput_); - - this.refocus(); - + FileManager.prototype.renameEntry = function(entry, newName, opt_callback) { var self = this; function onSuccess() { - self.rescanDirectory_(function() { self.selectEntry(newName) }); + self.rescanDirectory_(function() { + self.selectEntry(newName); + if (opt_callback) + opt_callback(); + }); } function onError(err) { @@ -2941,6 +3045,22 @@ FileManager.prototype = { resolveCallback, resolveCallback); }; + FileManager.prototype.commitRename_ = function() { + var entry = this.renameInput_.currentEntry; + var newName = this.renameInput_.value; + if (!this.validateFileName_(newName)) + return; + + this.renameInput_.currentEntry = null; + this.lastLabelClick_ = null; + + if (this.renameInput_.parentNode) + this.renameInput_.parentNode.removeChild(this.renameInput_); + + this.refocus(); + this.renameEntry(entry, newName); + }; + FileManager.prototype.cancelRename_ = function(event) { this.renameInput_.currentEntry = null; @@ -2997,11 +3117,15 @@ FileManager.prototype = { promptForName(str('DEFAULT_NEW_FOLDER_NAME')); }; - FileManager.prototype.createNewFolder = function(name) { + FileManager.prototype.createNewFolder = function(name, opt_callback) { var self = this; function onSuccess(dirEntry) { - self.rescanDirectory_(function() { self.selectEntry(name) }); + self.rescanDirectory_(function() { + self.selectEntry(name); + if (opt_callback) + opt_callback(); + }); } function onError(err) { diff --git a/chrome/browser/resources/file_manager/js/file_manager_pyauto.js b/chrome/browser/resources/file_manager/js/file_manager_pyauto.js new file mode 100644 index 0000000..fd327fc --- /dev/null +++ b/chrome/browser/resources/file_manager/js/file_manager_pyauto.js @@ -0,0 +1,146 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * pyautoAPI object provides a set of functions used by PyAuto tests + * to drive the file manager. + * + * Refer to chrome/test/functional/chromeos_file_browser.py for examples + * of how this API is used. + */ +var pyautoAPI = { + /** + * Add the item with given name to the current selection. + * + * @param {string} name Name of the item to add to selection + * @return {boolean} Whether item exists. + */ + addItemToSelection: function(name) { + var itemExists = fileManager.addItemToSelection(name); + window.domAutomationController.send(itemExists); + }, + + /** + * List all items in the current directory. + * We assume names do not contain '|' charecter. + * + * @return {string} A delimeter seperated string of item names. + */ + listDirectory: function() { + var list = fileManager.listDirectory(); + window.domAutomationController.send(list.join('|')); + }, + + /** + * Save the item using the given name. + * + * @param {string} name Name given to item to be saved. + */ + saveItemAs: function(name) { + fileManager.doSaveAs(name); + window.domAutomationController.send('done'); + }, + + /** + * Open selected item. + */ + openItem: function() { + fileManager.doOpen(); + window.domAutomationController.send('done'); + }, + + /** + * Copy selected items to clipboard. + */ + copyItems: function() { + fileManager.copySelectionToClipboard(); + window.domAutomationController.send('done'); + }, + + /** + * Cut selected items to clipboard. + */ + cutItems: function() { + fileManager.cutSelectionToClipboard(); + window.domAutomationController.send('done'); + }, + + /** + * Paste items from clipboard. + */ + pasteItems: function() { + fileManager.pasteFromClipboard(this.sendDone_); + }, + + /** + * Rename selected item. + * + * @param {string} name New name of the item. + */ + renameItem: function(name) { + entry = fileManager.selection.entries[0]; + fileManager.renameEntry(entry, name, this.sendDone_); + }, + + /** + * Delete selected entries. + */ + deleteItems: function() { + entries = fileManager.selection.entries; + fileManager.deleteEntries(entries, true, this.sendDone_); + }, + + /** + * Create directory. + * + * @param {string} name Name of the directory. + */ + createDirectory: function(name) { + fileManager.createNewFolder(name, this.sendDone_); + }, + + /** + * Change to a directory. + * + * A path starting with '/' * is absolute, otherwise it is relative to the + * current directory. + * + * @param {string} path Path to directory. + */ + changeDirectory: function(path) { + if (path.charAt(0) != '/') + path = fileManager.getCurrentDirectory() + '/' + path + fileManager.changeDirectory(path, undefined, undefined, this.sendDone_); + }, + + /** + * Get the absolute path of current directory. + * + * @return {string} Path to the current directory. + */ + currentDirectory: function() { + path = fileManager.getCurrentDirectory() + window.domAutomationController.send(path); + }, + + /** + * Callback function signalling completion of operation. + */ + sendDone_: function() { + window.domAutomationController.send('done'); + }, + + /** + * Returns whether the file manager is initialized. + * + * This function is polled by pyauto before calling any + * of the functions above. + * + * @return {boolean} Whether file manager is initialied. + */ + isInitialized: function() { + var initialized = (fileManager != null) && fileManager.isInitialized() + window.domAutomationController.send(initialized); + }, +}; diff --git a/chrome/browser/resources/file_manager/js/harness.js b/chrome/browser/resources/file_manager/js/harness.js index 5dda51d..f8e6713 100644 --- a/chrome/browser/resources/file_manager/js/harness.js +++ b/chrome/browser/resources/file_manager/js/harness.js @@ -91,6 +91,9 @@ var harness = { get fileManager() { return document.getElementById('dialog').contentWindow.fileManager; }, + get pyautoAPI() { + return document.getElementById('dialog').contentWindow.pyautoAPI; + }, /** * Import a list of File objects into harness.filesystem. diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html index 584b682..6fa9f76 100644 --- a/chrome/browser/resources/file_manager/main.html +++ b/chrome/browser/resources/file_manager/main.html @@ -77,6 +77,7 @@ <script src="js/util.js"></script> <script src="js/file_copy_manager.js"></script> <script src="js/file_manager.js"></script> + <script src="js/file_manager_pyauto.js"></script> <script src="js/dialogs.js"></script> <script src="js/main.js"></script> diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS index c1637dc..00a8f50 100644 --- a/chrome/test/functional/PYAUTO_TESTS +++ b/chrome/test/functional/PYAUTO_TESTS @@ -119,8 +119,6 @@ '-special_tabs.SpecialTabsTest.testSpecialURLTabs', # crbug.com/88593 '-sync.SyncTest.testPersonalStuffSyncSection', - # crbug.com/94683 - '-execute_javascript.ExecuteJavascriptTest.testExecuteJavascriptInExtension', # crbug.com/95031 '-notifications.NotificationsTest.testOriginPrefsNotSavedInIncognito', # crbug.com/91578 @@ -136,8 +134,6 @@ '-autofill.AutofillTest.testProfilesNotMergedWhenNoMinAddressData', # crbug.com/95141 '-enterprise.EnterpriseTest.testDisable3DAPI', - # crbug.com/94683 - '-execute_javascript.ExecuteJavascriptTest.testExecuteJavascriptInExtension', # Some download test fails on win official builder, only thru buildbot # when downloading the small zip file. # crbug.com/91225 @@ -289,6 +285,7 @@ 'chromeos_basic', 'chromeos_browser', 'chromeos_firmware_version_checker', + 'chromeos_file_browser', 'chromeos_power', 'chromeos_prefs', 'chromeos_security', diff --git a/chrome/test/functional/chromeos_file_browser.py b/chrome/test/functional/chromeos_file_browser.py new file mode 100644 index 0000000..602d034 --- /dev/null +++ b/chrome/test/functional/chromeos_file_browser.py @@ -0,0 +1,232 @@ +#!/usr/bin/python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import sys +import unittest + +import pyauto_functional +import pyauto + +import chromeos.file_browser +import test_utils + + +class ChromeosFileBrowserTest(pyauto.PyUITest): + """Tests for ChromeOS File Browser (full page and dialog).""" + + def _GetFullPageFileBrowser(self, tab_index=0, windex=0): + """Display the full page file browser in the current tab. + + Returns: + ChromeosFileBrowser object. + """ + self.NavigateToURL('chrome://files') + executor = pyauto.PyUITest.JavascriptExecutorInTab(self, tab_index, windex) + file_browser = chromeos.file_browser.FileBrowser(self, executor) + if file_browser.WaitUntilInitialized(): + return file_browser + else: + return None + + def _GetSaveAsDialogFileBrowser(self): + """Display the save-as file browser dialog. + + The current tab should not be 'about:blank'. + + Returns: + ChromeosFileBrowser object. + """ + self.ApplyAccelerator(pyauto.IDC_SAVE_PAGE) + dialog = self.WaitUntilExtensionViewLoaded(view_type='EXTENSION_DIALOG') + self.assertTrue( + dialog, + msg='Could not find a loaded "save-as" file browser' + 'dialog (views = %s).' % self.GetBrowserInfo()['extension_views']) + executor = \ + pyauto.PyUITest.JavascriptExecutorInRenderView(self, dialog) + file_browser = chromeos.file_browser.FileBrowser(self, executor) + if file_browser.WaitUntilInitialized(): + return file_browser + else: + return None + + def _GetOpenDialogFileBrowser(self): + """Display the open file browser dialog. + + Returns: + ChromeosFileBrowser object. + """ + self.ApplyAccelerator(pyauto.IDC_OPEN_FILE) + dialog = self.WaitUntilExtensionViewLoaded(view_type='EXTENSION_DIALOG') + self.assertTrue( + dialog, + msg='Could not find a loaded "open" file browser dialog: views = %s.' % + self.GetBrowserInfo()['extension_views']) + executor = \ + pyauto.PyUITest.JavascriptExecutorInRenderView(self, dialog) + file_browser = chromeos.file_browser.FileBrowser(self, executor) + if file_browser.WaitUntilInitialized(): + return file_browser + else: + return None + + def testOpenWavFile(self): + """Test we can open a 'wav' file from the file browser dialog.""" + test_utils.CopyFileFromDataDirToDownloadDir(self, 'media/bear.wav') + file_browser = self._GetOpenDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self.assertTrue(file_browser.Select('bear.wav'), + msg='"bear.wav" does not exist.') + dialog = self.WaitUntilExtensionViewLoaded(view_type='EXTENSION_DIALOG') + file_browser.Open() + self.assertTrue(self.WaitUntilExtensionViewClosed(dialog), + msg='File browser dialog was not closed.') + + def testSavePage(self): + """Test we can save the current page using the file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + dialog = self.WaitUntilExtensionViewLoaded(view_type='EXTENSION_DIALOG') + file_browser.Save('apple') + self.assertTrue(self.WaitUntilExtensionViewClosed(dialog)) + file_browser = self._GetOpenDialogFileBrowser() + self.assertTrue(file_browser.Select('apple.html')) + + def testSelectMultipleFoldersInFullPage(self): + """Test we can select multiple folders in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + file_browser.CreateDirectory('apples') + file_browser.CreateDirectory('oranges') + self.assertEqual(file_browser.DirectoryContents(), + set(['apples', 'oranges']), + msg='Failed to create directories (list = %s).' % + file_browser.DirectoryContents()) + file_browser.Select('apples') + file_browser.Select('oranges') + file_browser.Delete() + self.assertFalse(file_browser.DirectoryContents(), + msg='Failed to delete directories (list = %s).' % + file_browser.DirectoryContents()) + + def _CreateFolder(self, file_browser): + """Create folders and then change into them.""" + top_directory = file_browser.CurrentDirectory() + tree = ['deep', 'deeper', 'deepest'] + for directory in tree: + file_browser.CreateDirectory(directory) + file_browser.ChangeDirectory(directory) + self.assertEqual(file_browser.CurrentDirectory(), + top_directory + '/' + '/'.join(tree), + msg='Ended up in wrong directory (%s)' % + file_browser.CurrentDirectory()) + + def testCreateFolderInFullPage(self): + """Test we can create a folder in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CreateFolder(file_browser) + + def testCreateFolderInDialog(self): + """Test we can create a folder in a save-as file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CreateFolder(file_browser) + + def _RenameFolder(self, file_browser): + """Create a folder and then rename it.""" + file_browser.CreateDirectory('apples') + file_browser.Select('apples') + file_browser.Rename('oranges') + self.assertFalse(file_browser.Select('apples')) + self.assertTrue(file_browser.Select('oranges')) + + def testRenameFolderInFullPage(self): + """Test we can rename a folder in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._RenameFolder(file_browser) + + def testRenameFolderInDialog(self): + """Test we can rename a folder in a save-as file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._RenameFolder(file_browser) + + def _DeleteFolder(self, file_browser): + """Create a folder and then delete it.""" + file_browser.CreateDirectory('apples') + file_browser.Select('apples') + file_browser.Delete() + self.assertFalse(file_browser.Select('apples')) + + def testDeleteFolderInFullPage(self): + """Test we can delete a folder in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._DeleteFolder(file_browser) + + def testDeleteFolderInDialog(self): + """Test we can delete a folder in a save-as file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._DeleteFolder(file_browser) + + def _CopyFolder(self, file_browser): + """Create a folder and then copy and paste it to itself.""" + file_browser.CreateDirectory('apples') + file_browser.Select('apples') + file_browser.Copy() + file_browser.ChangeDirectory('apples') + file_browser.Paste() + self.assertTrue(file_browser.Select('apples')) + + def testCopyFolderInFullPage(self): + """Test we can copy and paste a folder in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CopyFolder(file_browser) + + def testCopyFolderInDialog(self): + """Test we can copy and paste a folder in a save-as file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CopyFolder(file_browser) + + def _CutFolder(self, file_browser): + """Create two folders and cut and paste one inside the other.""" + top_directory = file_browser.CurrentDirectory() + file_browser.CreateDirectory('apples') + file_browser.Select('apples') + file_browser.Cut() + file_browser.CreateDirectory('oranges') + file_browser.ChangeDirectory('oranges') + file_browser.Paste() + self.assertTrue(file_browser.Select('apples')) + file_browser.ChangeDirectory(top_directory) + self.assertFalse(file_browser.Select('apples')) + + def testCutFolderInFullPage(self): + """Test we can cut and paste a folder in the full page file browser.""" + file_browser = self._GetFullPageFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CutFolder(file_browser) + + def testCutFolderInDialog(self): + """Test we can cut and paste a folder in a save-as file browser dialog.""" + self.NavigateToURL('chrome://version') + file_browser = self._GetSaveAsDialogFileBrowser() + self.assertTrue(file_browser, msg='File browser failed to initialize.') + self._CutFolder(file_browser) + + +if __name__ == '__main__': + pyauto_functional.Main() diff --git a/chrome/test/functional/execute_javascript.py b/chrome/test/functional/execute_javascript.py index af7a9d0..b935a51 100644 --- a/chrome/test/functional/execute_javascript.py +++ b/chrome/test/functional/execute_javascript.py @@ -46,28 +46,26 @@ class ExecuteJavascriptTest(PyUITest): msg='Extension was disabled by default') # Get the background page's view. - info = self.GetBrowserInfo()['extension_views'] - view = [x for x in info if - x['extension_id'] == ext_id and - x['view_type'] == 'EXTENSION_BACKGROUND_PAGE'] - self.assertEqual(1, len(view), - msg='problematic background view: view = %s.' % view) - background_view = view[0] + background_view = self.WaitUntilExtensionViewLoaded( + view_type='EXTENSION_BACKGROUND_PAGE') + self.assertTrue(background_view, + msg='problematic background view: views = %s.' % + self.GetBrowserInfo()['extension_views']) # Get values from background page's DOM v = self.ExecuteJavascriptInRenderView( 'window.domAutomationController.send(' - 'document.getElementById("myinput").nodeName)', background_view['view']) + 'document.getElementById("myinput").nodeName)', background_view) self.assertEqual(v, 'INPUT', msg='Incorrect value returned (v = %s).' % v) v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(bool_var)', background_view['view']) + 'window.domAutomationController.send(bool_var)', background_view) self.assertEqual(v, True, msg='Incorrect value returned (v = %s).' % v) v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(int_var)', background_view['view']) + 'window.domAutomationController.send(int_var)', background_view) self.assertEqual(v, 42, msg='Incorrect value returned (v = %s).' % v) v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(str_var)', background_view['view']) + 'window.domAutomationController.send(str_var)', background_view) self.assertEqual(v, 'foo', msg='Incorrect value returned (v = %s).' % v) diff --git a/chrome/test/functional/test_utils.py b/chrome/test/functional/test_utils.py index 2dbcfee..df1b9ee 100644 --- a/chrome/test/functional/test_utils.py +++ b/chrome/test/functional/test_utils.py @@ -9,6 +9,7 @@ import email import logging import os import platform +import shutil import smtplib import subprocess import types @@ -20,6 +21,17 @@ import pyauto_utils """Commonly used functions for PyAuto tests.""" +def CopyFileFromDataDirToDownloadDir(test, file_path): + """Copy a file from data directory to downloads directory. + + Args: + test: derived from pyauto.PyUITest - base class for UI test cases. + path: path of the file relative to the data directory + """ + data_file = os.path.join(test.DataDir(), file_path) + download_dir = test.GetDownloadDirectory().value() + shutil.copy(data_file, download_dir) + def DownloadFileFromDownloadsDataDir(test, file_name): """Download a file from downloads data directory, in first tab, first window. diff --git a/chrome/test/pyautolib/chromeos/file_browser.py b/chrome/test/pyautolib/chromeos/file_browser.py new file mode 100644 index 0000000..112958a --- /dev/null +++ b/chrome/test/pyautolib/chromeos/file_browser.py @@ -0,0 +1,166 @@ +#!/usr/bin/python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +class FileBrowser(object): + """This class provides an API for automating the ChromeOS File Browser. + + Example: + # Create and change into 'hello world' folder. + executor = pyauto.PyUITest.JavascriptExecutorInTab(self) + file_browser = chromeos.file_browser.FileBrowser(self, executor) + if file_browser.WaitUntilInitialized(): + file_browser.CreateDirectory('hello world') + file_browser.ChangeDirectory('hello world') + + For complete examples refer to chromeos_file_browser.py. + """ + + def __init__(self, ui_test, executor): + """Initialize FileBrowser. + + Args: + ui_test: derived from pyauto.PyUITest - base class for UI test cases. + executor: derived from pyauto.PyUITest.JavascriptExecutor. + """ + self._ui_test = ui_test + self.executor = executor + + def Select(self, name): + """Add entry with given name to the current selection. + + Args: + name: Name of the entry to add to selection + + Returns: + Whether entry exists. + """ + script = """ + pyautoAPI.addItemToSelection('%s'); + """ % name + return self.executor.Execute(script) + + def DirectoryContents(self): + """Return a set containing all entries in the current directory. + + Returns: + A set of entries. + """ + script = """ + pyautoAPI.listDirectory(); + """ + list = self.executor.Execute(script) + if list: + return set(list.split('|')) + else: + return None + + def Save(self, name): + """Save the entry using the given name. + + Args: + name: Name given to entry to be saved. + """ + script = """ + pyautoAPI.saveItemAs('%s'); + """ % name + self.executor.Execute(script) + + def Open(self): + """Open selected entries.""" + script = """ + pyautoAPI.openItem(); + """ + self.executor.Execute(script) + + def Copy(self): + """Copy selected entries to clipboard.""" + script = """ + pyautoAPI.copyItems(); + """ + self.executor.Execute(script) + + def Cut(self): + """Cut selected entries to clipboard. """ + script = """ + pyautoAPI.cutItems(); + """ + self.executor.Execute(script) + + def Paste(self): + """Paste entries from clipboard.""" + script = """ + pyautoAPI.pasteItems(); + """ + self.executor.Execute(script) + + def Rename(self, name): + """Rename selected entry. + + Args: + name: New name of the entry. + """ + script = """ + pyautoAPI.renameItem('%s'); + """ % name + self.executor.Execute(script) + + def Delete(self): + """Delete selected entries.""" + script = """ + pyautoAPI.deleteItems(); + """ + self.executor.Execute(script) + + def CreateDirectory(self, name): + """Create directory. + + Args: + name: Name of the directory. + """ + script = """ + pyautoAPI.createDirectory('%s'); + """ % name + self.executor.Execute(script) + + def ChangeDirectory(self, path): + """Change to a directory. + + A path starting with '/' is absolute, otherwise it is relative to the + current directory. + + Args: + name: Path to directory. + """ + script = """ + pyautoAPI.changeDirectory('%s'); + """ % path + self.executor.Execute(script) + + def CurrentDirectory(self): + """Get the absolute path of current directory. + + Returns: + Path to the current directory. + """ + script = """ + pyautoAPI.currentDirectory(); + """ + return self.executor.Execute(script) + + def WaitUntilInitialized(self): + """Returns whether the file manager is initialized. + + This should be called before calling any of the functions above. + + Returns: + Whether file manager is initialied. + """ + def _IsInitialized(): + script = """ + pyautoAPI.isInitialized(); + """ + return self.executor.Execute(script) + return self._ui_test.WaitUntil(lambda: _IsInitialized()) diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 9bee755..653e443 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -658,6 +658,56 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): if self._ui_test.action_timeout_ms() != self._saved_timeout: self._ui_test.set_action_timeout_ms(self._saved_timeout) + class JavascriptExecutor(object): + """Abstract base class for JavaScript injection. + + Derived classes should override Execute method.""" + def Execute(self, script): + pass + + class JavascriptExecutorInTab(JavascriptExecutor): + """Wrapper for injecting JavaScript in a tab.""" + def __init__(self, ui_test, tab_index=0, windex=0, frame_xpath=''): + """Initialize. + + Refer to ExecuteJavascriptInTab() for the complete argument list + description. + + Args: + ui_test: a PyUITest object + """ + self._ui_test = ui_test + self.windex = windex + self.tab_index = tab_index + self.frame_xpath = frame_xpath + + def Execute(self, script): + """Execute script in the tab.""" + return self._ui_test.ExecuteJavascriptInTab(script, + self.tab_index, + self.windex, + self.frame_xpath) + + class JavascriptExecutorInRenderView(JavascriptExecutor): + """Wrapper for injecting JavaScript in an extension view.""" + def __init__(self, ui_test, view, frame_xpath=''): + """Initialize. + + Refer to ExecuteJavascriptInRenderView() for the complete argument list + description. + + Args: + ui_test: a PyUITest object + """ + self._ui_test = ui_test + self.view = view + self.frame_xpath = frame_xpath + + def Execute(self, script): + """Execute script in the render view.""" + return self._ui_test.ExecuteJavascriptInRenderView(script, + self.view, + self.frame_xpath) def _GetResultFromJSONRequest(self, cmd_dict, windex=0, timeout=-1): """Issue call over the JSON automation channel and fetch output. @@ -1271,6 +1321,7 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): u'extension_id': u'dgcoklnmbeljaehamekjpeidmbicddfj', u'url': u'chrome-extension://dgcoklnmbeljaehamekjpeidmbicddfj/' 'bg.html', + u'loaded': True, u'view': { u'render_process_id': 2, u'render_view_id': 1}, @@ -1570,6 +1621,67 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): } self._GetResultFromJSONRequest(cmd_dict, windex=-1) + def WaitUntilExtensionViewLoaded(self, name=None, extension_id=None, + url=None, view_type=None): + """Wait for a loaded extension view matching all the given properties. + + If no matching extension views are found, wait for one to be loaded. + If there are more than one matching extension view, return one at random. + Uses WaitUntil so timeout is capped by automation timeout. + Refer to extension_view dictionary returned in GetBrowserInfo() + for sample input/output values. + + Args: + name: (optional) Name of the extension. + extension_id: (optional) ID of the extension. + url: (optional) URL of the extension view. + view_type: (optional) Type of the extension view. + + Returns: + The 'view' property of the extension view. + None, if no view loaded. + + Raises: + pyauto_errors.JSONInterfaceError if the automation returns an error. + """ + def _GetExtensionViewLoaded(): + extension_views = self.GetBrowserInfo()['extension_views'] + for extension_view in extension_views: + if ((name and name != extension_view['name']) or + (extension_id and extension_id != extension_view['extension_id']) or + (url and url != extension_view['url']) or + (view_type and view_type != extension_view['view_type'])): + continue + if extension_view['loaded']: + return extension_view['view'] + return False + + if self.WaitUntil(lambda: _GetExtensionViewLoaded()): + return _GetExtensionViewLoaded() + return None + + def WaitUntilExtensionViewClosed(self, view): + """Wait for the given extension view to to be closed. + + Uses WaitUntil so timeout is capped by automation timeout. + Refer to extension_view dictionary returned by GetBrowserInfo() + for sample input value. + + Args: + view: 'view' property of extension view. + + Raises: + pyauto_errors.JSONInterfaceError if the automation returns an error. + """ + def _IsExtensionViewClosed(): + extension_views = self.GetBrowserInfo()['extension_views'] + for extension_view in extension_views: + if view == extension_view['view']: + return False + return True + + return self.WaitUntil(lambda: _IsExtensionViewClosed()) + def SelectTranslateOption(self, option, tab_index=0, window_index=0): """Selects one of the options in the drop-down menu for the translate bar. @@ -2274,6 +2386,38 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): } return self._GetResultFromJSONRequest(cmd_dict, windex=windex) + def ExecuteJavascriptInTab(self, js, tab_index=0, windex=0, frame_xpath=''): + """Executes a script in the specified frame of a tab. + + The invoked javascript function must send a result back via the + domAutomationController.send function, or this function will never return. + + Args: + js: script to be executed + windex: index of the window + tab_index: index of the tab + frame_xpath: XPath of the frame to execute the script. Default is no + frame. Example: '//frames[1]'. + + Returns: + a value that was sent back via the domAutomationController.send method + + Raises: + pyauto_errors.JSONInterfaceError if the automation call returns an error. + """ + cmd_dict = { + 'command': 'ExecuteJavascript', + 'javascript' : js, + 'windex' : windex, + 'tab_index' : tab_index, + 'frame_xpath' : frame_xpath, + } + result = self._GetResultFromJSONRequest(cmd_dict)['result'] + # Wrap result in an array before deserializing because valid JSON has an + # array or an object as the root. + json_string = '[' + result + ']' + return json.loads(json_string)[0] + def ExecuteJavascriptInRenderView(self, js, view, frame_xpath=''): """Executes a script in the specified frame of an render view. @@ -2305,7 +2449,7 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): 'view' : view, 'frame_xpath' : frame_xpath, } - result = self._GetResultFromJSONRequest(cmd_dict)['result'] + result = self._GetResultFromJSONRequest(cmd_dict, windex=-1)['result'] # Wrap result in an array before deserializing because valid JSON has an # array or an object as the root. json_string = '[' + result + ']' |