summaryrefslogtreecommitdiffstats
path: root/chrome/test/automation
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-18 23:43:11 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-18 23:43:11 +0000
commita9602de83c13669096f15e07e3f613461c58afbc (patch)
tree5a0f434b63ef6311b309ec2ca796c58374add0bc /chrome/test/automation
parent9d3bd0e747adae3c4caf9ddccf8e08a4de68e3bc (diff)
downloadchromium_src-a9602de83c13669096f15e07e3f613461c58afbc.zip
chromium_src-a9602de83c13669096f15e07e3f613461c58afbc.tar.gz
chromium_src-a9602de83c13669096f15e07e3f613461c58afbc.tar.bz2
Add support for interacting with the DOM in browser_tests.
BUG=none TEST=none Reivew url: http://codereview.chromium.org/660046 Review URL: http://codereview.chromium.org/1051005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42026 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/automation')
-rw-r--r--chrome/test/automation/dom_automation_browsertest.cc246
-rw-r--r--chrome/test/automation/dom_element_proxy.cc299
-rw-r--r--chrome/test/automation/dom_element_proxy.h126
-rw-r--r--chrome/test/automation/javascript_execution_controller.cc127
-rw-r--r--chrome/test/automation/javascript_execution_controller.h146
5 files changed, 944 insertions, 0 deletions
diff --git a/chrome/test/automation/dom_automation_browsertest.cc b/chrome/test/automation/dom_automation_browsertest.cc
new file mode 100644
index 0000000..c861f89
--- /dev/null
+++ b/chrome/test/automation/dom_automation_browsertest.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2010 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.
+
+#include "base/ref_counted.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/test/automation/dom_element_proxy.h"
+#include "chrome/browser/browser.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+
+namespace {
+
+class DOMAutomationTest : public InProcessBrowserTest {
+ public:
+ DOMAutomationTest() {
+ EnableDOMAutomation();
+ }
+
+ GURL GetTestURL(const char* path) {
+ std::string url("http://localhost:1337/files/dom_automation/");
+ url.append(path);
+ return GURL(url);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, FindByXPath) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(),
+ GetTestURL("find_elements/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ // Find first element.
+ DOMElementProxyRef first_div = main_doc->FindByXPath("//div");
+ ASSERT_TRUE(first_div);
+ ASSERT_NO_FATAL_FAILURE(first_div->EnsureNameMatches("0"));
+
+ // Find many elements.
+ std::vector<DOMElementProxyRef> elements;
+ ASSERT_TRUE(main_doc->FindByXPath("//div", &elements));
+ ASSERT_EQ(2u, elements.size());
+ for (size_t i = 0; i < elements.size(); i++)
+ ASSERT_NO_FATAL_FAILURE(elements[i]->EnsureNameMatches(UintToString(i)));
+
+ // Find 0 elements.
+ ASSERT_FALSE(main_doc->FindByXPath("//nosuchtag"));
+ elements.clear();
+ ASSERT_TRUE(main_doc->FindByXPath("//nosuchtag", &elements));
+ elements.clear();
+ ASSERT_EQ(0u, elements.size());
+
+ // Find with invalid xpath.
+ ASSERT_FALSE(main_doc->FindByXPath("'invalid'"));
+ ASSERT_FALSE(main_doc->FindByXPath(" / / "));
+ ASSERT_FALSE(main_doc->FindByXPath("'invalid'", &elements));
+ ASSERT_FALSE(main_doc->FindByXPath(" / / ", &elements));
+
+ // Find nested elements.
+ int nested_count = 0;
+ std::string span_name;
+ DOMElementProxyRef node = main_doc->FindByXPath("/html/body/span");
+ while (node) {
+ nested_count++;
+ span_name.append("span");
+ ASSERT_NO_FATAL_FAILURE(node->EnsureNameMatches(span_name));
+ node = node->FindByXPath("./span");
+ }
+ ASSERT_EQ(3, nested_count);
+}
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, FindBySelectors) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(),
+ GetTestURL("find_elements/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ // Find first element.
+ DOMElementProxyRef first_myclass =
+ main_doc->FindBySelectors(".myclass");
+ ASSERT_TRUE(first_myclass);
+ ASSERT_NO_FATAL_FAILURE(first_myclass->EnsureNameMatches("0"));
+
+ // Find many elements.
+ std::vector<DOMElementProxyRef> elements;
+ ASSERT_TRUE(main_doc->FindBySelectors(".myclass", &elements));
+ ASSERT_EQ(2u, elements.size());
+ for (size_t i = 0; i < elements.size(); i++)
+ ASSERT_NO_FATAL_FAILURE(elements[i]->EnsureNameMatches(UintToString(i)));
+
+ // Find 0 elements.
+ ASSERT_FALSE(main_doc->FindBySelectors("#nosuchid"));
+ elements.clear();
+ ASSERT_TRUE(main_doc->FindBySelectors("#nosuchid", &elements));
+ ASSERT_EQ(0u, elements.size());
+
+ // Find with invalid selectors.
+ ASSERT_FALSE(main_doc->FindBySelectors("1#2"));
+ ASSERT_FALSE(main_doc->FindBySelectors("1#2", &elements));
+
+ // Find nested elements.
+ int nested_count = 0;
+ std::string span_name;
+ DOMElementProxyRef node = main_doc->FindBySelectors("span");
+ while (node) {
+ nested_count++;
+ span_name.append("span");
+ ASSERT_NO_FATAL_FAILURE(node->EnsureNameMatches(span_name));
+ node = node->FindBySelectors("span");
+ }
+ ASSERT_EQ(3, nested_count);
+}
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, FindByText) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(),
+ GetTestURL("find_elements/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ // Find first element.
+ DOMElementProxyRef first_text = main_doc->FindByText("div_text");
+ ASSERT_TRUE(first_text);
+ ASSERT_NO_FATAL_FAILURE(first_text->EnsureNameMatches("0"));
+
+ // Find many elements.
+ std::vector<DOMElementProxyRef> elements;
+ ASSERT_TRUE(main_doc->FindByText("div_text", &elements));
+ ASSERT_EQ(2u, elements.size());
+ for (size_t i = 0; i < elements.size(); i++)
+ ASSERT_NO_FATAL_FAILURE(elements[i]->EnsureNameMatches(UintToString(i)));
+
+ // Find 0 elements.
+ ASSERT_FALSE(main_doc->FindByText("nosuchtext"));
+ elements.clear();
+ ASSERT_TRUE(main_doc->FindByText("nosuchtext", &elements));
+ ASSERT_EQ(0u, elements.size());
+
+ // Find nested elements.
+ int nested_count = 0;
+ std::string span_name;
+ DOMElementProxyRef node = main_doc->FindByText("span_text");
+ while (node) {
+ nested_count++;
+ span_name.append("span");
+ ASSERT_NO_FATAL_FAILURE(node->EnsureNameMatches(span_name));
+ node = node->FindByText("span_text");
+ }
+ ASSERT_EQ(3, nested_count);
+
+ // Find only visible text.
+ DOMElementProxyRef shown_td = main_doc->FindByText("table_text");
+ ASSERT_TRUE(shown_td);
+ ASSERT_NO_FATAL_FAILURE(shown_td->EnsureNameMatches("shown"));
+
+ // Find text in inputs.
+ ASSERT_TRUE(main_doc->FindByText("textarea_text"));
+ ASSERT_TRUE(main_doc->FindByText("input_text"));
+}
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, Frames) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(), GetTestURL("frames/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ // Get both frame elements.
+ std::vector<DOMElementProxyRef> frame_elements;
+ ASSERT_TRUE(main_doc->FindByXPath("//frame", &frame_elements));
+ ASSERT_EQ(2u, frame_elements.size());
+
+ // Get both frames, checking their contents are correct.
+ DOMElementProxyRef frame1 = frame_elements[0]->GetContentDocument();
+ DOMElementProxyRef frame2 = frame_elements[1]->GetContentDocument();
+ ASSERT_TRUE(frame1 && frame2);
+ DOMElementProxyRef frame_div = frame1->FindByXPath("/html/body/div");
+ ASSERT_TRUE(frame_div);
+ ASSERT_NO_FATAL_FAILURE(frame_div->EnsureInnerHTMLMatches("frame 1"));
+ frame_div = frame2->FindByXPath("/html/body/div");
+ ASSERT_TRUE(frame_div);
+ ASSERT_NO_FATAL_FAILURE(frame_div->EnsureInnerHTMLMatches("frame 2"));
+
+ // Get both inner iframes, checking their contents are correct.
+ DOMElementProxyRef iframe1 =
+ frame1->GetDocumentFromFrame("0");
+ DOMElementProxyRef iframe2 =
+ frame2->GetDocumentFromFrame("0");
+ ASSERT_TRUE(iframe1 && iframe2);
+ frame_div = iframe1->FindByXPath("/html/body/div");
+ ASSERT_TRUE(frame_div);
+ ASSERT_NO_FATAL_FAILURE(frame_div->EnsureInnerHTMLMatches("iframe 1"));
+ frame_div = iframe2->FindByXPath("/html/body/div");
+ ASSERT_TRUE(frame_div);
+ ASSERT_NO_FATAL_FAILURE(frame_div->EnsureInnerHTMLMatches("iframe 2"));
+
+ // Get nested frame.
+ ASSERT_EQ(iframe1.get(), main_doc->GetDocumentFromFrame("0", "0").get());
+ ASSERT_EQ(iframe2.get(), main_doc->GetDocumentFromFrame("1", "0").get());
+}
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, Events) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(), GetTestURL("events/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ // Click link and make sure text changes.
+ DOMElementProxyRef link = main_doc->FindBySelectors("a");
+ ASSERT_TRUE(link && link->Click());
+ ASSERT_NO_FATAL_FAILURE(link->EnsureTextMatches("clicked"));
+
+ // Click input button and make sure textfield changes.
+ DOMElementProxyRef button = main_doc->FindBySelectors("#button");
+ DOMElementProxyRef textfield = main_doc->FindBySelectors("#textfield");
+ ASSERT_TRUE(textfield && button && button->Click());
+ ASSERT_NO_FATAL_FAILURE(textfield->EnsureTextMatches("clicked"));
+
+ // Type in the textfield.
+ ASSERT_TRUE(textfield->SetText("test"));
+ ASSERT_NO_FATAL_FAILURE(textfield->EnsureTextMatches("test"));
+
+ // Type in the textarea.
+ DOMElementProxyRef textarea = main_doc->FindBySelectors("textarea");
+ ASSERT_TRUE(textarea && textarea->Type("test"));
+ ASSERT_NO_FATAL_FAILURE(textarea->EnsureTextMatches("textareatest"));
+}
+
+IN_PROC_BROWSER_TEST_F(DOMAutomationTest, StringEscape) {
+ StartHTTPServer();
+ ui_test_utils::NavigateToURL(browser(),
+ GetTestURL("string_escape/test.html"));
+ DOMElementProxyRef main_doc = ui_test_utils::GetActiveDOMDocument(browser());
+
+ DOMElementProxyRef textarea = main_doc->FindBySelectors("textarea");
+ ASSERT_TRUE(textarea);
+ ASSERT_NO_FATAL_FAILURE(textarea->EnsureTextMatches(WideToUTF8(L"\u00FF")));
+
+ const wchar_t* set_and_expect_strings[] = {
+ L"\u00FF and \u00FF",
+ L"\n \t \\",
+ L"' \""
+ };
+ for (size_t i = 0; i < 3; i++) {
+ ASSERT_TRUE(textarea->SetText(WideToUTF8(set_and_expect_strings[i])));
+ ASSERT_NO_FATAL_FAILURE(textarea->EnsureTextMatches(
+ WideToUTF8(set_and_expect_strings[i])));
+ }
+}
+
+} // namespace
diff --git a/chrome/test/automation/dom_element_proxy.cc b/chrome/test/automation/dom_element_proxy.cc
new file mode 100644
index 0000000..c7bc6da
--- /dev/null
+++ b/chrome/test/automation/dom_element_proxy.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2010 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.
+
+#include "chrome/test/automation/dom_element_proxy.h"
+
+#include "base/json/string_escape.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Convenience wrapper for GetDoubleQuotedJson function.
+std::string GetDoubleQuotedJson(std::string utf8_string) {
+ return base::GetDoubleQuotedJson(UTF8ToUTF16(utf8_string));
+}
+
+} // namespace
+
+DOMElementProxyRef DOMElementProxy::GetContentDocument() {
+ const char* script = "%s.contentDocument;";
+ DOMElementProxy* element = NULL;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()), &element);
+ return element;
+}
+
+DOMElementProxyRef DOMElementProxy::GetDocumentFromFrame(
+ const std::vector<std::string>& frame_names) {
+ if (!is_valid())
+ return NULL;
+
+ const char* script = "domAutomation.getDocumentFromFrame(%s, %s);";
+ std::string string_script = StringPrintf(
+ script, this->GetReferenceJavaScript().c_str(),
+ JavaScriptExecutionController::Serialize(frame_names).c_str());
+ DOMElementProxy* element = NULL;
+ executor_->ExecuteJavaScriptAndParse(string_script, &element);
+ return element;
+}
+
+DOMElementProxyRef DOMElementProxy::GetDocumentFromFrame(
+ const std::string& frame_name) {
+ if (!is_valid())
+ return NULL;
+
+ std::vector<std::string> frame_names;
+ frame_names.push_back(frame_name);
+ return GetDocumentFromFrame(frame_names);
+}
+
+DOMElementProxyRef DOMElementProxy::GetDocumentFromFrame(
+ const std::string& frame_name1, const std::string& frame_name2) {
+ if (!is_valid())
+ return NULL;
+
+ std::vector<std::string> frame_names;
+ frame_names.push_back(frame_name1);
+ frame_names.push_back(frame_name2);
+ return GetDocumentFromFrame(frame_names);
+}
+
+DOMElementProxyRef DOMElementProxy::GetDocumentFromFrame(
+ const std::string& frame_name1, const std::string& frame_name2,
+ const std::string& frame_name3) {
+ if (!is_valid())
+ return NULL;
+
+ std::vector<std::string> frame_names;
+ frame_names.push_back(frame_name1);
+ frame_names.push_back(frame_name2);
+ frame_names.push_back(frame_name3);
+ return GetDocumentFromFrame(frame_names);
+}
+
+bool DOMElementProxy::FindByXPath(const std::string& xpath,
+ std::vector<DOMElementProxyRef>* elements) {
+ DCHECK(elements);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.findByXPath(%s, %s);";
+ std::vector<DOMElementProxy*> element_pointers;
+ if (!executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(xpath).c_str()),
+ &element_pointers))
+ return false;
+ for (size_t i = 0; i < element_pointers.size(); i++)
+ elements->push_back(element_pointers[i]);
+ return true;
+}
+
+DOMElementProxyRef DOMElementProxy::FindByXPath(const std::string& xpath) {
+ if (!is_valid())
+ return NULL;
+
+ const char* script = "domAutomation.find1ByXPath(%s, %s);";
+ DOMElementProxy* element = NULL;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(xpath).c_str()),
+ &element);
+ return element;
+}
+
+bool DOMElementProxy::FindBySelectors(
+ const std::string& selectors, std::vector<DOMElementProxyRef>* elements) {
+ DCHECK(elements);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.findBySelectors(%s, %s);";
+ std::vector<DOMElementProxy*> element_pointers;
+ if (!executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(selectors).c_str()),
+ &element_pointers))
+ return false;
+ for (size_t i = 0; i < element_pointers.size(); i++)
+ elements->push_back(element_pointers[i]);
+ return true;
+}
+
+DOMElementProxyRef DOMElementProxy::FindBySelectors(
+ const std::string& selectors) {
+ if (!is_valid())
+ return NULL;
+
+ const char* script = "domAutomation.find1BySelectors(%s, %s);";
+ DOMElementProxy* element = NULL;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(selectors).c_str()),
+ &element);
+ return element;
+}
+
+bool DOMElementProxy::FindByText(const std::string& text,
+ std::vector<DOMElementProxyRef>* elements) {
+ DCHECK(elements);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.findByText(%s, %s);";
+ std::vector<DOMElementProxy*> element_pointers;
+ if (!executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(text).c_str()),
+ &element_pointers))
+ return false;
+ for (size_t i = 0; i < element_pointers.size(); i++)
+ elements->push_back(element_pointers[i]);
+ return true;
+}
+
+DOMElementProxyRef DOMElementProxy::FindByText(const std::string& text) {
+ if (!is_valid())
+ return NULL;
+
+ const char* script = "domAutomation.find1ByText(%s, %s);";
+ DOMElementProxy* element = NULL;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(text).c_str()),
+ &element);
+ return element;
+}
+
+bool DOMElementProxy::Click() {
+ const char* script = "domAutomation.click(%s);";
+ if (!is_valid())
+ return false;
+
+ return executor_->ExecuteJavaScript(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()));
+}
+
+bool DOMElementProxy::Type(const std::string& text) {
+ const char* script = "domAutomation.type(%s, %s);";
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(text).c_str()),
+ &success);
+ return success;
+}
+
+bool DOMElementProxy::SetText(const std::string& text) {
+ const char* script = "domAutomation.setText(%s, %s);";
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(text).c_str()),
+ &success);
+ return success;
+}
+
+bool DOMElementProxy::GetProperty(const std::string& property,
+ std::string* out) {
+ DCHECK(out);
+ if (!is_valid())
+ return false;
+
+ const char* script = "%s.%s;";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(property).c_str()),
+ out);
+}
+
+bool DOMElementProxy::GetAttribute(const std::string& attribute,
+ std::string* out) {
+ DCHECK(out);
+ if (!is_valid())
+ return false;
+
+ const char* script = "%s.getAttribute(%s);";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str(),
+ GetDoubleQuotedJson(attribute).c_str()),
+ out);
+}
+
+bool DOMElementProxy::GetText(std::string* text) {
+ DCHECK(text);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.getText(%s);";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()), text);
+}
+
+bool DOMElementProxy::GetInnerHTML(std::string* html) {
+ DCHECK(html);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.getInnerHTML(%s);";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()), html);
+}
+
+bool DOMElementProxy::GetId(std::string* id) {
+ DCHECK(id);
+ if (!is_valid())
+ return false;
+
+ const char* script = "%s.id;";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()), id);
+}
+
+bool DOMElementProxy::GetName(std::string* name) {
+ return GetAttribute("name", name);
+}
+
+bool DOMElementProxy::GetVisibility(bool* visibility) {
+ DCHECK(visibility);
+ if (!is_valid())
+ return false;
+
+ const char* script = "domAutomation.isVisible(%s);";
+ return executor_->ExecuteJavaScriptAndParse(
+ StringPrintf(script, this->GetReferenceJavaScript().c_str()),
+ visibility);
+}
+
+void DOMElementProxy::EnsureTextMatches(const std::string& expected_text) {
+ std::string text;
+ ASSERT_TRUE(GetText(&text));
+ ASSERT_EQ(expected_text, text);
+}
+
+void DOMElementProxy::EnsureInnerHTMLMatches(const std::string& expected_html) {
+ std::string html;
+ ASSERT_TRUE(GetInnerHTML(&html));
+ ASSERT_EQ(expected_html, html);
+}
+
+void DOMElementProxy::EnsureNameMatches(const std::string& expected_name) {
+ std::string name;
+ ASSERT_TRUE(GetName(&name));
+ ASSERT_EQ(expected_name, name);
+}
+
+void DOMElementProxy::EnsureVisibilityMatches(bool expected_visibility) {
+ bool visibility;
+ ASSERT_TRUE(GetVisibility(&visibility));
+ ASSERT_EQ(expected_visibility, visibility);
+}
diff --git a/chrome/test/automation/dom_element_proxy.h b/chrome/test/automation/dom_element_proxy.h
new file mode 100644
index 0000000..2057e0c0
--- /dev/null
+++ b/chrome/test/automation/dom_element_proxy.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_AUTOMATION_DOM_ELEMENT_PROXY_H_
+#define CHROME_TEST_AUTOMATION_DOM_ELEMENT_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "chrome/test/automation/javascript_execution_controller.h"
+
+class DOMElementProxy;
+
+typedef scoped_refptr<DOMElementProxy> DOMElementProxyRef;
+
+// This class presents the interface to actions that can be performed on
+// a given DOM element. Note that this object can be invalidated at any
+// time. In that case, any subsequent calls will return false immediately.
+class DOMElementProxy : public JavaScriptObjectProxy {
+ public:
+ DOMElementProxy(JavaScriptExecutionController* executor, int handle)
+ : JavaScriptObjectProxy(executor, handle) {}
+
+ // Returns the document for this element, which must be of type frame.
+ // Returns NULL on failure.
+ DOMElementProxyRef GetContentDocument();
+
+ // Finds the frame which matches the list of given names, starting from
+ // the window that contains this element. Each name in the list is used to
+ // select the next sub frame. Returns NULL on failure.
+ // A vector of "2" and "ad" is equivalent to the javascript:
+ // frame.frames["2"].frames["ad"].
+ DOMElementProxyRef GetDocumentFromFrame(
+ const std::vector<std::string>& frame_names);
+
+ // Same as above but with different argument for convenience.
+ DOMElementProxyRef GetDocumentFromFrame(const std::string& frame_name);
+
+ // Same as above but with different argument for convenience.
+ DOMElementProxyRef GetDocumentFromFrame(const std::string& frame_name1,
+ const std::string& frame_name2);
+
+ // Same as above but with different argument for convenience.
+ DOMElementProxyRef GetDocumentFromFrame(const std::string& frame_name1,
+ const std::string& frame_name2, const std::string& frame_name3);
+
+ // Adds the elements from this element's descendants that satisfy the
+ // XPath query |xpath| to the vector |elements|.
+ // Returns true on success.
+ bool FindByXPath(const std::string& xpath,
+ std::vector<DOMElementProxyRef>* elements);
+
+ // Same as above, but returns the first element, or NULL if none.
+ DOMElementProxyRef FindByXPath(const std::string& xpath);
+
+ // Adds the elements from this element's descendants that match the
+ // CSS Selectors |selectors| to the vector |elements|.
+ // Returns true on success.
+ bool FindBySelectors(const std::string& selectors,
+ std::vector<DOMElementProxyRef>* elements);
+
+ // Same as above, but returns the first element, or NULL if none.
+ DOMElementProxyRef FindBySelectors(const std::string& selectors);
+
+ // Adds the elements from this element's descendants which have text that
+ // matches |text|. This includes text from input elements.
+ // Returns true on success.
+ bool FindByText(const std::string& text,
+ std::vector<DOMElementProxyRef>* elements);
+
+ // Same as above, but returns the first element, or NULL if none.
+ DOMElementProxyRef FindByText(const std::string& text);
+
+ // Dispatches a click MouseEvent to the element and all its parents.
+ // Returns true on success.
+ bool Click();
+
+ // Adds |text| to this element. Only valid for textareas and textfields.
+ // Returns true on success.
+ bool Type(const std::string& text);
+
+ // Sets the input text to |text|. Only valid for textareas and textfields.
+ // Returns true on success.
+ bool SetText(const std::string& text);
+
+ // Gets the element's value for its |property|. Returns true on success.
+ bool GetProperty(const std::string& property,
+ std::string* out);
+
+ // Gets the element's value for its |attribute|. Returns true on success.
+ bool GetAttribute(const std::string& attribute,
+ std::string* out);
+
+ // Retrieves all the text in this element. This includes the value
+ // of textfields and inputs. Returns true on success.
+ bool GetText(std::string* text);
+
+ // Retrieves the element's inner HTML. Returns true on success.
+ bool GetInnerHTML(std::string* html);
+
+ // Retrieves the element's id. Returns true on success.
+ bool GetId(std::string* id);
+
+ // Retrieves the element's name. Returns true on success.
+ bool GetName(std::string* name);
+
+ // Retrieves the element's visibility. Returns true on success.
+ bool GetVisibility(bool* visilibity);
+
+ // Asserts that |expected_text| matches all the text in this element. This
+ // includes the value of textfields and inputs.
+ void EnsureTextMatches(const std::string& expected_text);
+
+ // Asserts that |expected_html| matches the element's inner html.
+ void EnsureInnerHTMLMatches(const std::string& expected_html);
+
+ // Asserts that |expected_name| matches the element's name.
+ void EnsureNameMatches(const std::string& expected_name);
+
+ // Asserts that |expected_visibility| matches the element's visibility.
+ void EnsureVisibilityMatches(bool expected_visibility);
+};
+
+#endif // CHROME_TEST_AUTOMATION_DOM_ELEMENT_PROXY_H_
diff --git a/chrome/test/automation/javascript_execution_controller.cc b/chrome/test/automation/javascript_execution_controller.cc
new file mode 100644
index 0000000..26d32d0
--- /dev/null
+++ b/chrome/test/automation/javascript_execution_controller.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2010 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.
+
+#include "chrome/test/automation/javascript_execution_controller.h"
+
+#include "base/json/string_escape.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/json_value_serializer.h"
+
+using base::GetDoubleQuotedJson;
+
+// JavaScriptObjectProxy methods
+JavaScriptObjectProxy::JavaScriptObjectProxy(
+ JavaScriptExecutionController* executor, int handle)
+ : executor_(executor->AsWeakPtr()), handle_(handle) {}
+
+JavaScriptObjectProxy::~JavaScriptObjectProxy() {
+ if (is_valid())
+ executor_->Remove(handle_);
+}
+
+std::string JavaScriptObjectProxy::GetReferenceJavaScript() {
+ return JavaScriptExecutionController::GetReferenceJavaScript(this);
+}
+
+// JavaScriptExecutionController methods
+bool JavaScriptExecutionController::ExecuteJavaScript(
+ const std::string& script) {
+ std::string json;
+ return ExecuteJavaScript(script, &json);
+}
+
+// static
+std::string JavaScriptExecutionController::GetReferenceJavaScript(
+ JavaScriptObjectProxy* object) {
+ return StringPrintf("domAutomation.getObject(%d)", object->handle());
+}
+
+// static
+std::string JavaScriptExecutionController::Serialize(
+ const std::vector<std::string>& vector) {
+ std::string javascript = "[";
+ for (size_t i = 0; i < vector.size(); i++) {
+ javascript.append(GetDoubleQuotedJson(UTF8ToUTF16(vector[i])));
+ if (i < vector.size() - 1)
+ javascript.append(",");
+ }
+ javascript.append("]");
+ return javascript;
+}
+
+void JavaScriptExecutionController::Remove(int handle) {
+ ExecuteJavaScript(StringPrintf("domAutomation.removeObject(%d);", handle));
+ handle_to_object_.erase(handle);
+ if (handle_to_object_.empty())
+ LastObjectRemoved();
+}
+
+bool JavaScriptExecutionController::ExecuteJavaScript(
+ const std::string& original_script, std::string* json) {
+ std::string script =
+ "domAutomationController.setAutomationId(0);"
+ "domAutomation.evaluateJavaScript(";
+ script.append(GetDoubleQuotedJson(UTF8ToUTF16(original_script)));
+ script.append(");");
+ return ExecuteJavaScriptAndGetJSON(script, json);
+}
+
+bool JavaScriptExecutionController::ParseJSON(const std::string& json,
+ scoped_ptr<Value>* result) {
+ JSONStringValueSerializer parse(json);
+ std::string parsing_error;
+ scoped_ptr<Value> root_value(parse.Deserialize(&parsing_error));
+
+ if (!root_value.get()) {
+ if (parsing_error.length())
+ LOG(ERROR) << "Cannot parse JSON response: " << parsing_error;
+ else
+ LOG(ERROR) << "JSON response is empty";
+ return false;
+ }
+
+ // The response must be a list of 3 components:
+ // -success(boolean): whether the javascript was evaluated with no errors
+ // -error(string): the evaluation error message or the empty string if
+ // no error occurred
+ // -result(string): the result of the evaluation (in JSON), or the
+ // exact error if an error occurred (in JSON)
+ bool success;
+ std::string evaluation_error;
+ Value* evaluation_result_value;
+ if (!root_value->IsType(Value::TYPE_LIST)) {
+ LOG(ERROR) << "JSON response was not in correct format";
+ return false;
+ }
+ ListValue* list = static_cast<ListValue*>(root_value.get());
+ if (!list->GetBoolean(0, &success) ||
+ !list->GetString(1, &evaluation_error) ||
+ !list->Remove(2, &evaluation_result_value)) {
+ LOG(ERROR) << "JSON response was not in correct format";
+ return false;
+ }
+ if (!success) {
+ LOG(WARNING) << "JavaScript evaluation did not complete successfully."
+ << evaluation_error;
+ return false;
+ }
+ result->reset(evaluation_result_value);
+ return true;
+}
+
+bool JavaScriptExecutionController::ConvertResponse(Value* value,
+ bool* result) {
+ return value->GetAsBoolean(result);
+}
+
+bool JavaScriptExecutionController::ConvertResponse(Value* value,
+ int* result) {
+ return value->GetAsInteger(result);
+}
+
+bool JavaScriptExecutionController::ConvertResponse(Value* value,
+ std::string* result) {
+ return value->GetAsString(result);
+}
diff --git a/chrome/test/automation/javascript_execution_controller.h b/chrome/test/automation/javascript_execution_controller.h
new file mode 100644
index 0000000..0472eee
--- /dev/null
+++ b/chrome/test/automation/javascript_execution_controller.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_AUTOMATION_JAVASCRIPT_EXECUTION_CONTROLLER_H_
+#define CHROME_TEST_AUTOMATION_JAVASCRIPT_EXECUTION_CONTROLLER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/values.h"
+#include "base/weak_ptr.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class JavaScriptExecutionController;
+
+// This class is a proxy to an object in JavaScript. It holds a handle which
+// can be used to retrieve the actual object in JavaScript scripts.
+class JavaScriptObjectProxy
+ : public base::RefCountedThreadSafe<JavaScriptObjectProxy> {
+ public:
+ JavaScriptObjectProxy(JavaScriptExecutionController* executor, int handle);
+ virtual ~JavaScriptObjectProxy();
+
+ // Returns JavaScript which can be used for retrieving the actual object
+ // associated with this proxy.
+ std::string GetReferenceJavaScript();
+
+ int handle() const { return handle_; }
+ bool is_valid() const { return executor_; }
+
+ protected:
+ base::WeakPtr<JavaScriptExecutionController> executor_;
+ int handle_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(JavaScriptObjectProxy);
+};
+
+// This class handles the execution of arbitrary JavaScript, preparing it for
+// execution, and parsing its result (in JSON). It keeps track of all returned
+// JavaScript objects.
+class JavaScriptExecutionController
+ : public base::SupportsWeakPtr<JavaScriptExecutionController> {
+ public:
+ JavaScriptExecutionController() {}
+ virtual ~JavaScriptExecutionController() {}
+
+ // Executes |script| and parse return value.
+ // A corresponding ConvertResponse(Value* value, T* result) must exist
+ // for type T.
+ template <typename T>
+ bool ExecuteJavaScriptAndParse(const std::string& script, T* result) {
+ std::string json;
+ if (!ExecuteJavaScript(script, &json))
+ return false;
+ scoped_ptr<Value> value;
+ if (!ParseJSON(json, &value))
+ return false;
+ return ConvertResponse(value.get(), result);
+ }
+
+ // Executes |script| with no return.
+ bool ExecuteJavaScript(const std::string& script);
+
+ // Returns JavaScript which can be used for retrieving the actual object
+ // associated with the proxy |object|.
+ static std::string GetReferenceJavaScript(JavaScriptObjectProxy* object);
+
+ // Returns the equivalent JSON for |vector|.
+ static std::string Serialize(const std::vector<std::string>& vector);
+
+ protected:
+ // Executes |script| and sets the JSON response |json|. Returns true
+ // on success.
+ virtual bool ExecuteJavaScriptAndGetJSON(const std::string& script,
+ std::string* json) = 0;
+
+ // Called when this controller is tracking its first object. Used by
+ // reference counted subclasses.
+ virtual void FirstObjectAdded() {}
+
+ // Called when this controller is no longer tracking any objects. Used by
+ // reference counted subclasses.
+ virtual void LastObjectRemoved() {}
+
+ private:
+ typedef std::map<int, JavaScriptObjectProxy*> HandleToObjectMap;
+
+ friend class JavaScriptObjectProxy;
+ // Called by JavaScriptObjectProxy on destruct.
+ void Remove(int handle);
+
+ bool ParseJSON(const std::string& json, scoped_ptr<Value>* result);
+
+ bool ExecuteJavaScript(const std::string& script, std::string* json);
+
+ bool ConvertResponse(Value* value, bool* result);
+ bool ConvertResponse(Value* value, int* result);
+ bool ConvertResponse(Value* value, std::string* result);
+
+ template<class JavaScriptObject>
+ bool ConvertResponse(Value* value, JavaScriptObject** result) {
+ int handle;
+ if (!value->GetAsInteger(&handle))
+ return false;
+
+ HandleToObjectMap::const_iterator iter = handle_to_object_.find(handle);
+ if (iter == handle_to_object_.end()) {
+ *result = new JavaScriptObject(this, handle);
+ if (handle_to_object_.empty())
+ FirstObjectAdded();
+ handle_to_object_.insert(std::make_pair(handle, *result));
+ } else {
+ *result = static_cast<JavaScriptObject*>(iter->second);
+ }
+ return true;
+ }
+
+ template<typename T>
+ bool ConvertResponse(Value* value, std::vector<T>* result) {
+ if (!value->IsType(Value::TYPE_LIST))
+ return false;
+
+ ListValue* list = static_cast<ListValue*>(value);
+ for (size_t i = 0; i < list->GetSize(); i++) {
+ Value* inner_value;
+ if (!list->Get(i, &inner_value))
+ return false;
+ T item;
+ ConvertResponse(inner_value, &item);
+ result->push_back(item);
+ }
+ return true;
+ }
+
+ // Weak pointer to all the object proxies that we create.
+ HandleToObjectMap handle_to_object_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaScriptExecutionController);
+};
+
+#endif // CHROME_TEST_AUTOMATION_JAVASCRIPT_EXECUTION_CONTROLLER_H_