diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 01:39:04 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 01:39:04 +0000 |
commit | c3a44d60fb7d03edbb55dd49efb7a41041065833 (patch) | |
tree | d8bf6a690af05fc4bb4ecba932ce9531b4372918 | |
parent | 44574ee7d6da72dd47d4d16187a6176e12863ab7 (diff) | |
download | chromium_src-c3a44d60fb7d03edbb55dd49efb7a41041065833.zip chromium_src-c3a44d60fb7d03edbb55dd49efb7a41041065833.tar.gz chromium_src-c3a44d60fb7d03edbb55dd49efb7a41041065833.tar.bz2 |
Fix minor issues in chromedriver dealing with design mode, frame switching,
and opacity. Also, clean up some of the error messages.
BUG=88685,88686,88810,88957
TEST=none
Review URL: http://codereview.chromium.org/7347010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92946 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/test/webdriver/commands/execute_async_script_command.cc | 2 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/execute_command.cc | 2 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/response.cc | 2 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/webelement_commands.cc | 3 | ||||
-rw-r--r-- | chrome/test/webdriver/session.cc | 62 | ||||
-rw-r--r-- | chrome/test/webdriver/session.h | 1 | ||||
-rw-r--r-- | chrome/test/webdriver/test/chromedriver_tests.py | 110 | ||||
-rw-r--r-- | chrome/test/webdriver/test/content_editable.html | 11 | ||||
-rw-r--r-- | chrome/test/webdriver/test/design_mode_doc.html | 9 | ||||
-rw-r--r-- | chrome/test/webdriver/test/switch_to_frame_by_index.html | 10 | ||||
-rw-r--r-- | chrome/test/webdriver/test/transparent.html | 12 | ||||
-rw-r--r-- | chrome/test/webdriver/webdriver_error.cc | 49 | ||||
-rw-r--r-- | chrome/test/webdriver/webdriver_error.h | 3 |
13 files changed, 213 insertions, 63 deletions
diff --git a/chrome/test/webdriver/commands/execute_async_script_command.cc b/chrome/test/webdriver/commands/execute_async_script_command.cc index cf3ec1a..b0e9aaf 100644 --- a/chrome/test/webdriver/commands/execute_async_script_command.cc +++ b/chrome/test/webdriver/commands/execute_async_script_command.cc @@ -41,7 +41,7 @@ void ExecuteAsyncScriptCommand::ExecutePost(Response* const response) { Error* error = session_->ExecuteAsyncScript( session_->current_target(), script, args, &result); if (error) { - error->AddDetails("Original script: " + script); + error->AddDetails("Script execution failed. Script: " + script); response->SetError(error); return; } diff --git a/chrome/test/webdriver/commands/execute_command.cc b/chrome/test/webdriver/commands/execute_command.cc index 044d0d0..65ec162 100644 --- a/chrome/test/webdriver/commands/execute_command.cc +++ b/chrome/test/webdriver/commands/execute_command.cc @@ -40,7 +40,7 @@ void ExecuteCommand::ExecutePost(Response* const response) { Value* result = NULL; Error* error = session_->ExecuteScript(script, args, &result); if (error) { - error->AddDetails("Original script: " + script); + error->AddDetails("Script execution failed. Script: " + script); response->SetError(error); return; } diff --git a/chrome/test/webdriver/commands/response.cc b/chrome/test/webdriver/commands/response.cc index 0060fa0..6b96bae 100644 --- a/chrome/test/webdriver/commands/response.cc +++ b/chrome/test/webdriver/commands/response.cc @@ -59,7 +59,7 @@ void Response::SetValue(Value* value) { void Response::SetError(Error* error) { DictionaryValue* error_dict = new DictionaryValue(); - error_dict->SetString(kMessageKey, error->ToString()); + error_dict->SetString(kMessageKey, error->GetMessage()); SetStatus(error->code()); SetValue(error_dict); diff --git a/chrome/test/webdriver/commands/webelement_commands.cc b/chrome/test/webdriver/commands/webelement_commands.cc index 0ea0c01..561bd53 100644 --- a/chrome/test/webdriver/commands/webelement_commands.cc +++ b/chrome/test/webdriver/commands/webelement_commands.cc @@ -161,7 +161,8 @@ bool ElementDisplayedCommand::DoesGet() { void ElementDisplayedCommand::ExecuteGet(Response* const response) { bool is_displayed; Error* error = session_->IsElementDisplayed( - session_->current_target(), element, &is_displayed); + session_->current_target(), element, false /* ignore_opacity */, + &is_displayed); if (error) { response->SetError(error); return; diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc index e59e7b8..e6c30a4 100644 --- a/chrome/test/webdriver/session.cc +++ b/chrome/test/webdriver/session.cc @@ -150,7 +150,8 @@ Error* Session::ExecuteAsyncScript(const FrameId& frame_id, Error* Session::SendKeys(const WebElementId& element, const string16& keys) { bool is_displayed = false; - Error* error = IsElementDisplayed(current_target_, element, &is_displayed); + Error* error = IsElementDisplayed( + current_target_, element, true /* ignore_opacity */, &is_displayed); if (error) return error; if (!is_displayed) @@ -165,28 +166,36 @@ Error* Session::SendKeys(const WebElementId& element, const string16& keys) { ListValue args; args.Append(element.ToValue()); - // Focus the element if not focused already. If it is an editable element, - // focus the editing host element instead. Descendants of an editing host - // element cannot be focused in chrome. - // See http://www.w3.org/TR/html5/editing.html#attr-contenteditable. + // Focus the target element in order to send keys to it. + // First, the currently active element is blurred, if it is different from + // the target element. We do not want to blur an element unnecessarily, + // because this may cause us to lose the current cursor position in the + // element. + // Secondly, we focus the target element. + // Thirdly, we check if the new active element is the target element. If not, + // we throw an error. + // Additional notes: + // - |document.activeElement| is the currently focused element, or body if + // no element is focused + // - Even if |document.hasFocus()| returns true and the active element is + // the body, sometimes we still need to focus the body element for send + // keys to work. Not sure why + // - You cannot focus a descendant of a content editable node // TODO(jleyba): Update this to use the correct atom. const char* kFocusScript = "var elem = arguments[0];" - "if (elem.isContentEditable) {" - " while (elem.parentNode && elem.parentNode.isContentEditable)" - " elem = elem.parentNode;" - "}" - "if (elem != document.activeElement) {" - " if(document.activeElement)" - " document.activeElement.blur();" - " elem.focus();" - "}"; + "var doc = elem.ownerDocument || elem;" + "var prevActiveElem = doc.activeElement;" + "if (elem != prevActiveElem && prevActiveElem)" + " prevActiveElem.blur();" + "elem.focus();" + "if (elem != doc.activeElement)" + " throw new Error('Failed to send keys because cannot focus element.');"; Value* unscoped_result = NULL; error = ExecuteScript(kFocusScript, &args, &unscoped_result); - if (error) { - error->AddDetails("Failed to focus element before sending keys"); + if (error) return error; - } + error = NULL; RunSessionTask(NewRunnableMethod( this, @@ -523,6 +532,7 @@ Error* Session::SwitchToWindow(const std::string& name) { if (!switch_to_id) return new Error(kNoSuchWindow); + frame_elements_.clear(); current_target_ = FrameId(switch_to_id, FramePath()); return NULL; } @@ -564,7 +574,7 @@ Error* Session::SwitchToFrameWithIndex(int index) { "console.info(frame == null ? 'found nothing' : frame);" "if (!frame) { return null; }" "frame_xpath = ((frame.tagName == 'IFRAME' ? " - " '/html/body//iframe' : '/html/frameset/frame') + index);" + " '(/html/body//iframe)' : '/html/frameset/frame') + index);" "return [frame, frame_xpath];"; ListValue args; args.Append(Value::CreateIntegerValue(index)); @@ -923,11 +933,13 @@ Error* Session::GetElementBorder(const FrameId& frame_id, Error* Session::IsElementDisplayed(const FrameId& frame_id, const WebElementId& element, + bool ignore_opacity, bool* is_displayed) { std::string script = base::StringPrintf( "return (%s).apply(null, arguments);", atoms::IS_DISPLAYED); ListValue args; args.Append(element.ToValue()); + args.Append(Value::CreateBooleanValue(ignore_opacity)); Value* unscoped_result = NULL; Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result); @@ -996,7 +1008,8 @@ Error* Session::GetElementTagName(const FrameId& frame_id, Error* Session::GetClickableLocation(const WebElementId& element, gfx::Point* location) { bool is_displayed = false; - Error* error = IsElementDisplayed(current_target_, element, &is_displayed); + Error* error = IsElementDisplayed( + current_target_, element, true /* ignore_opacity */, &is_displayed); if (error) return error; if (!is_displayed) @@ -1163,12 +1176,11 @@ Error* Session::ExecuteScriptAndParseResponse(const FrameId& frame_id, ErrorCode code = static_cast<ErrorCode>(status); if (code != kSuccess) { DictionaryValue* error_dict; - std::string error_msg = "Script threw an exception"; - if (result_dict->GetDictionary("value", &error_dict)) { - std::string exception; - if (error_dict->GetString("message", &exception)) - error_msg += ": " + exception; - } + std::string error_msg; + if (result_dict->GetDictionary("value", &error_dict)) + error_dict->GetString("message", &error_msg); + if (error_msg.empty()) + error_msg = "Script failed with error code: " + base::IntToString(code); return new Error(code, error_msg); } diff --git a/chrome/test/webdriver/session.h b/chrome/test/webdriver/session.h index 3842f00..9409029 100644 --- a/chrome/test/webdriver/session.h +++ b/chrome/test/webdriver/session.h @@ -243,6 +243,7 @@ class Session { // Gets whether the element is currently displayed. Error* IsElementDisplayed(const FrameId& frame_id, const WebElementId& element, + bool ignore_opacity, bool* is_visible); // Gets whether the element is currently enabled. diff --git a/chrome/test/webdriver/test/chromedriver_tests.py b/chrome/test/webdriver/test/chromedriver_tests.py index c50ae9b..65a2887 100644 --- a/chrome/test/webdriver/test/chromedriver_tests.py +++ b/chrome/test/webdriver/test/chromedriver_tests.py @@ -385,6 +385,11 @@ class MouseTest(ChromeDriverTest): super(MouseTest, self).setUp() self._driver = self.GetNewDriver() + def testCanClickTransparentElement(self): + self._driver.get(GetTestDataUrl() + '/transparent.html') + self._driver.find_element_by_tag_name('a').click() + self.assertTrue(self._driver.execute_script('return window.success')) + def testClickElementThatNeedsContainerScrolling(self): self._driver.get(GetTestDataUrl() + '/test_page.html') self._driver.find_element_by_name('hidden_scroll').click() @@ -420,13 +425,55 @@ class TypingTest(ChromeDriverTest): def setUp(self): super(TypingTest, self).setUp() self._driver = self.GetNewDriver() - self._driver.get(GetTestDataUrl() + '/test_page.html') - # See http://crbug.com/85243. - def testCanSendKeysToDescendantOfEditingHost(self): - self._driver.find_element_by_name('editable_child').send_keys('moo') - text = self._driver.find_element_by_name('editable').text - self.assertEquals('mooeditable', text) + def testSendKeysToEditingHostDiv(self): + self._driver.get(GetTestDataUrl() + '/content_editable.html') + div = self._driver.find_element_by_name('editable') + # Break into two to ensure element doesn't lose focus. + div.send_keys('hi') + div.send_keys(' there') + self.assertEquals('hi there', div.text) + + def testSendKeysToNonFocusableChildOfEditingHost(self): + self._driver.get(GetTestDataUrl() + '/content_editable.html') + child = self._driver.find_element_by_name('editable_child') + self.assertRaises(WebDriverException, child.send_keys, 'hi') + + def testSendKeysToFocusableChildOfEditingHost(self): + self._driver.get(GetTestDataUrl() + '/content_editable.html') + child = self._driver.find_element_by_tag_name('input') + child.send_keys('hi') + child.send_keys(' there') + self.assertEquals('hi there', child.get_attribute('value')) + + def testSendKeysToDesignModePage(self): + self._driver.get(GetTestDataUrl() + '/design_mode_doc.html') + body = self._driver.find_element_by_tag_name('body') + body.send_keys('hi') + body.send_keys(' there') + self.assertEquals('hi there', body.text) + + def testSendKeysToDesignModeIframe(self): + self._driver.get(GetTestDataUrl() + '/content_editable.html') + self._driver.switch_to_frame(0) + body = self._driver.find_element_by_tag_name('body') + body.send_keys('hi') + body.send_keys(' there') + self.assertEquals('hi there', body.text) + + def testSendKeysToTransparentElement(self): + self._driver.get(GetTestDataUrl() + '/transparent.html') + text_box = self._driver.find_element_by_tag_name('input') + text_box.send_keys('hi') + self.assertEquals('hi', text_box.get_attribute('value')) + + def testSendKeysDesignModePageAfterNavigate(self): + self._driver.get(GetTestDataUrl() + '/test_page.html') + self._driver.get(GetTestDataUrl() + '/design_mode_doc.html') + body = self._driver.find_element_by_tag_name('body') + body.send_keys('hi') + body.send_keys(' there') + self.assertEquals('hi there', body.text) class UrlBaseTest(unittest.TestCase): @@ -579,6 +626,57 @@ class FileUploadControlTest(ChromeDriverTest): self.assertTrue(f['name'] in filenames) +class FrameSwitchingTest(ChromeDriverTest): + + def testGetWindowHandles(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/test_page.html') + driver.execute_script('window.popup = window.open("about:blank")') + self.assertEquals(2, len(driver.window_handles)) + driver.execute_script('window.popup.close()') + self.assertEquals(1, len(driver.window_handles)) + + def testSwitchToSameWindow(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/test_page.html') + driver.switch_to_window(driver.window_handles[0]) + self.assertEquals('test_page.html', driver.current_url.split('/')[-1]) + + def testClosedWindowThrows(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/test_page.html') + driver.execute_script('window.open("about:blank")') + driver.close() + self.assertRaises(WebDriverException, driver.close) + + def testSwitchFromClosedWindow(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/test_page.html') + driver.execute_script('window.open("about:blank")') + driver.close() + driver.switch_to_window(driver.window_handles[0]) + self.assertEquals('about:blank', driver.current_url) + + def testSwitchToWindowWhileInSubframe(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/test_page.html') + driver.execute_script('window.open("about:blank")') + driver.switch_to_frame(0) + driver.switch_to_window(driver.window_handles[1]) + self.assertEquals('about:blank', driver.current_url) + + # Tests that the indexing is absolute and not based on index of frame in its + # parent element. + # See crbug.com/88685. + def testSwitchToFrameByIndex(self): + driver = self.GetNewDriver({'chrome.switches': ['disable-popup-blocking']}) + driver.get(GetTestDataUrl() + '/switch_to_frame_by_index.html') + for i in range(3): + driver.switch_to_frame(i) + self.assertEquals(str(i), driver.current_url.split('?')[-1]) + driver.switch_to_default_content() + + """Chrome functional test section. All implementation tests of ChromeDriver should go above. diff --git a/chrome/test/webdriver/test/content_editable.html b/chrome/test/webdriver/test/content_editable.html new file mode 100644 index 0000000..82b5c35 --- /dev/null +++ b/chrome/test/webdriver/test/content_editable.html @@ -0,0 +1,11 @@ +<html> + <body> + <div name='editable' contentEditable> + <input type='text'></input> + </div> + <div name='editable2' contentEditable> + <div name='editable_child'>editable</div> + </div> + <iframe src='design_mode_doc.html'></iframe> + </body> +</html> diff --git a/chrome/test/webdriver/test/design_mode_doc.html b/chrome/test/webdriver/test/design_mode_doc.html new file mode 100644 index 0000000..3b92bb0 --- /dev/null +++ b/chrome/test/webdriver/test/design_mode_doc.html @@ -0,0 +1,9 @@ +<html> + <script> + window.onload = function() { + document.designMode = "on"; + } + </script> + <body> + </body> +</html> diff --git a/chrome/test/webdriver/test/switch_to_frame_by_index.html b/chrome/test/webdriver/test/switch_to_frame_by_index.html new file mode 100644 index 0000000..36b2dc1 --- /dev/null +++ b/chrome/test/webdriver/test/switch_to_frame_by_index.html @@ -0,0 +1,10 @@ +<html> + <body> + <iframe src='test_page.html?0'></iframe> + <div> + <div>Some text</div> + <iframe src='test_page.html?1'></iframe> + </div> + <iframe src='test_page.html?2'></iframe> + </body> +</html> diff --git a/chrome/test/webdriver/test/transparent.html b/chrome/test/webdriver/test/transparent.html new file mode 100644 index 0000000..6739596 --- /dev/null +++ b/chrome/test/webdriver/test/transparent.html @@ -0,0 +1,12 @@ +<html> + <script> + var success = false; + function succeed() { + success = true; + } + </script> + <body> + <a style='opacity: 0' onclick='succeed()'>Click here</a> + <input style='opacity: 0' type='text'></input> + </body> +</html> diff --git a/chrome/test/webdriver/webdriver_error.cc b/chrome/test/webdriver/webdriver_error.cc index 3db95d8..07098c0 100644 --- a/chrome/test/webdriver/webdriver_error.cc +++ b/chrome/test/webdriver/webdriver_error.cc @@ -11,34 +11,34 @@ namespace webdriver { namespace { // Returns the string equivalent of the given |ErrorCode|. -const char* ErrorCodeToString(ErrorCode code) { +const char* DefaultMessageForErrorCode(ErrorCode code) { switch (code) { case kSuccess: - return "SUCCESS"; + return "Success"; case kNoSuchElement: - return "NO_SUCH_ELEMENT"; + return "The element could not be found"; case kNoSuchFrame: - return "NO_SUCH_FRAME"; + return "The frame could not be found"; case kUnknownCommand: - return "UNKNOWN_COMMAND"; + return "Unknown command"; case kStaleElementReference: - return "STALE_ELEMENT_REFERENCE"; + return "Element reference is invalid"; case kElementNotVisible: - return "ELEMENT_NOT_VISIBLE"; + return "Element is not visible"; case kInvalidElementState: - return "INVALID_ELEMENT_STATE"; + return "Element is in an invalid state"; case kUnknownError: - return "UNKNOWN_ERROR"; + return "Unknown error"; case kElementNotSelectable: - return "ELEMENT_NOT_SELECTABLE"; + return "Element is not selectable"; case kXPathLookupError: - return "XPATH_LOOKUP_ERROR"; + return "XPath lookup error"; case kNoSuchWindow: - return "NO_SUCH_WINDOW"; + return "The window could not be found"; case kInvalidCookieDomain: - return "INVALID_COOKIE_DOMAIN"; + return "The cookie domain is invalid"; case kUnableToSetCookie: - return "UNABLE_TO_SET_COOKIE"; + return "Unable to set cookie"; default: return "<unknown>"; } @@ -63,15 +63,12 @@ void Error::AddDetails(const std::string& details) { details_ = details + ";\n " + details_; } -std::string Error::ToString() const { - std::string error; - if (code_ != kUnknownError) { - error += ErrorCodeToString(code_); - error += ": "; - } - if (details_.length()) { - error += details_; - } +std::string Error::GetMessage() const { + std::string msg; + if (details_.length()) + msg = details_; + else + msg = DefaultMessageForErrorCode(code_); // Only include a stacktrace on Linux. Windows and Mac have all symbols // stripped in release builds. @@ -81,11 +78,11 @@ std::string Error::ToString() const { if (count > 0) { std::ostringstream ostream; trace_.OutputToStream(&ostream); - error += "\n"; - error += ostream.str(); + msg += "\n"; + msg += ostream.str(); } #endif - return error; + return msg; } ErrorCode Error::code() const { diff --git a/chrome/test/webdriver/webdriver_error.h b/chrome/test/webdriver/webdriver_error.h index 0a232da..c0c094f 100644 --- a/chrome/test/webdriver/webdriver_error.h +++ b/chrome/test/webdriver/webdriver_error.h @@ -49,8 +49,7 @@ class Error { void AddDetails(const std::string& details); - // Returns a formatted string describing the error. For logging purposes. - std::string ToString() const; + std::string GetMessage() const; ErrorCode code() const; const std::string& details() const; |