summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager.js180
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager_pyauto.js146
-rw-r--r--chrome/browser/resources/file_manager/js/harness.js3
-rw-r--r--chrome/browser/resources/file_manager/main.html1
-rw-r--r--chrome/test/functional/PYAUTO_TESTS5
-rw-r--r--chrome/test/functional/chromeos_file_browser.py232
-rw-r--r--chrome/test/functional/execute_javascript.py20
-rw-r--r--chrome/test/functional/test_utils.py12
-rw-r--r--chrome/test/pyautolib/chromeos/file_browser.py166
-rw-r--r--chrome/test/pyautolib/pyauto.py146
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 + ']'