summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-29 22:46:34 +0000
committerjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-29 22:46:34 +0000
commitd551cdcc3c87e382d24d1912fc41c6489bb0bd40 (patch)
treed94f97d26792478d1113a73b7cf89eb1ea6b8678 /chrome
parent1a5af201a654bf80ce1bd6721b31a97311ae67a6 (diff)
downloadchromium_src-d551cdcc3c87e382d24d1912fc41c6489bb0bd40.zip
chromium_src-d551cdcc3c87e382d24d1912fc41c6489bb0bd40.tar.gz
chromium_src-d551cdcc3c87e382d24d1912fc41c6489bb0bd40.tar.bz2
Added the execute command will will run javascript on the remote
client. BUG=none TEST=webdriver_remote_tests.py Review URL: http://codereview.chromium.org/3618018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64511 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/test/webdriver/commands/command.cc5
-rw-r--r--chrome/test/webdriver/commands/command.h16
-rw-r--r--chrome/test/webdriver/commands/execute_command.cc78
-rw-r--r--chrome/test/webdriver/commands/execute_command.h45
-rw-r--r--chrome/test/webdriver/commands/webelement_command.cc86
-rw-r--r--chrome/test/webdriver/commands/webelement_command.h43
-rw-r--r--chrome/test/webdriver/server.cc3
-rw-r--r--chrome/test/webdriver/webdriver_remote_tests.py66
9 files changed, 329 insertions, 15 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 602d213..0556402 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -672,6 +672,8 @@
'test/webdriver/commands/command.cc',
'test/webdriver/commands/create_session.h',
'test/webdriver/commands/create_session.cc',
+ 'test/webdriver/commands/execute_command.h',
+ 'test/webdriver/commands/execute_command.cc',
'test/webdriver/commands/navigate_commands.h',
'test/webdriver/commands/navigate_commands.cc',
'test/webdriver/commands/response.h',
diff --git a/chrome/test/webdriver/commands/command.cc b/chrome/test/webdriver/commands/command.cc
index d735295..e524f6e 100644
--- a/chrome/test/webdriver/commands/command.cc
+++ b/chrome/test/webdriver/commands/command.cc
@@ -17,6 +17,11 @@ const char* const Response::kStackTrace = "stackTrace";
const char* const Response::kFileName = "fileName";
const char* const Response::kLineNumber = "lineNumber";
+bool Command::GetStringParameter(const std::string& key,
+ string16* out) const {
+ return parameters_.get() != NULL && parameters_->GetString(key, out);
+}
+
bool Command::GetStringASCIIParameter(const std::string& key,
std::string* out) const {
return parameters_.get() != NULL && parameters_->GetStringASCII(key, out);
diff --git a/chrome/test/webdriver/commands/command.h b/chrome/test/webdriver/commands/command.h
index 0969f49..55d4c92 100644
--- a/chrome/test/webdriver/commands/command.h
+++ b/chrome/test/webdriver/commands/command.h
@@ -12,6 +12,7 @@
#include "base/scoped_ptr.h"
#include "base/values.h"
#include "base/json/json_writer.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
#include "chrome/test/webdriver/error_codes.h"
#include "chrome/test/webdriver/commands/response.h"
@@ -55,8 +56,12 @@ class Command {
return i < path_segments_.size() ? path_segments_.at(i) : "";
}
- // Returns the command parameter with the given |key| as a string. Returns
- // false if there is no such parameter, or if it is not a string.
+ // Returns the command parameter with the given |key| as a UTF-16 string.
+ // Returns true on success.
+ bool GetStringParameter(const std::string& key, string16* out) const;
+
+ // Returns the command parameter with the given |key| as a ASCII string.
+ // Returns true on success.
bool GetStringASCIIParameter(const std::string& key, std::string* out) const;
// Returns the command parameter with the given |key| as a boolean. Returns
@@ -71,6 +76,13 @@ class Command {
const std::vector<std::string> path_segments_;
const scoped_ptr<const DictionaryValue> parameters_;
+ // An autorelease pool must exist on any thread where Objective C is used,
+ // even implicitly. Otherwise the warning:
+ // "Objects autoreleased with no pool in place."
+ // is printed for every object deallocted. Since every incomming command to
+ // chrome driver is allocated a new thread, the release pool is declared here.
+ base::mac::ScopedNSAutoreleasePool autorelease_pool;
+
DISALLOW_COPY_AND_ASSIGN(Command);
};
diff --git a/chrome/test/webdriver/commands/execute_command.cc b/chrome/test/webdriver/commands/execute_command.cc
new file mode 100644
index 0000000..4ee9059
--- /dev/null
+++ b/chrome/test/webdriver/commands/execute_command.cc
@@ -0,0 +1,78 @@
+// 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 <string>
+
+#include "base/utf_string_conversions.h"
+#include "base/json/json_reader.h"
+
+#include "chrome/test/webdriver/commands/execute_command.h"
+
+namespace webdriver {
+
+const char kArgs[] = "args";
+const char kScript[] = "script";
+
+bool ExecuteCommand::Init(Response* const response) {
+ if (!WebDriverCommand::Init(response)) {
+ SET_WEBDRIVER_ERROR(response, "Failure on Init for execute command",
+ kInternalServerError);
+ return false;
+ }
+
+ if (!GetStringParameter(kScript, &script_)) {
+ SET_WEBDRIVER_ERROR(response, "No script to execute specified",
+ kBadRequest);
+ return false;
+ }
+
+ has_args_= GetStringASCIIParameter(kArgs, &args_);
+ return true;
+}
+
+void ExecuteCommand::ExecutePost(Response* const response) {
+ int error_code = 0;
+ std::string error_msg;
+ Value* params = NULL;
+ Value* result = NULL;
+
+ if (has_args_) {
+ params = base::JSONReader::ReadAndReturnError(args_, true,
+ &error_code, &error_msg);
+ if (error_code != 0) {
+ LOG(INFO) << "Could not parse the JSON arguments passed in "
+ << "got error code: " << error_code << ", " << error_msg;
+ SET_WEBDRIVER_ERROR(response, "Arguments are not valid json objects",
+ kBadRequest);
+ return;
+ }
+ } else {
+ LOG(INFO) << "No args for this script";
+ // If there are no args required just use an empty list.
+ params = new ListValue();
+ }
+
+ if (params->GetType() != Value::TYPE_LIST) {
+ LOG(INFO) << "Data passed in to script must be a json list";
+ SET_WEBDRIVER_ERROR(response, "Arguments are not in list format",
+ kBadRequest);
+ return;
+ }
+
+ ListValue* script_args = static_cast<ListValue*>(params);
+ ErrorCode error = session_->ExecuteScript(UTF16ToWide(script_),
+ script_args, &result);
+
+ if (error != kSuccess) {
+ SET_WEBDRIVER_ERROR(response, "Failed to execute script",
+ kInternalServerError);
+ return;
+ }
+
+ response->set_value(result);
+ response->set_status(kSuccess);
+}
+
+} // namspace webdriver
+
diff --git a/chrome/test/webdriver/commands/execute_command.h b/chrome/test/webdriver/commands/execute_command.h
new file mode 100644
index 0000000..837ca5a
--- /dev/null
+++ b/chrome/test/webdriver/commands/execute_command.h
@@ -0,0 +1,45 @@
+// 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_WEBDRIVER_COMMANDS_EXECUTE_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_EXECUTE_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Inject a snippet of javascript into the page and return its result.
+// WebElements that should be passed to the script as an argument should be
+// specified in the arguments array as WebElement JSON arguments. Likewise,
+// any WebElements in the script result will be returned to the client as
+// WebElement JSON objects. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute
+class ExecuteCommand : public WebDriverCommand {
+ public:
+ ExecuteCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~ExecuteCommand() {}
+
+ virtual bool Init(Response* const response);
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ string16 script_;
+ bool has_args_;
+ std::string args_;
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(ExecuteCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_EXECUTE_COMMAND_H_
+
diff --git a/chrome/test/webdriver/commands/webelement_command.cc b/chrome/test/webdriver/commands/webelement_command.cc
new file mode 100644
index 0000000..9486c7b
--- /dev/null
+++ b/chrome/test/webdriver/commands/webelement_command.cc
@@ -0,0 +1,86 @@
+// 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/webdriver/commands/webelement_command.h"
+
+#include "third_party/webdriver/atoms.h"
+#include "chrome/test/webdriver/error_codes.h"
+#include "chrome/test/webdriver/utility_functions.h"
+
+namespace webdriver {
+
+bool WebElementCommand::Init(Response* response) {
+ if (WebDriverCommand::Init(response)) {
+ SET_WEBDRIVER_ERROR(response, "Failure on Init for web element command",
+ kInternalServerError);
+ return false;
+ }
+
+ // There should be at least 5 segments to match
+ // "/session/$session/element/$id"
+ if (path_segments_.size() < 5) {
+ SET_WEBDRIVER_ERROR(response, "Path segments is less than 5",
+ kBadRequest);
+ return false;
+ }
+
+ // We cannot verify the ID is valid until we execute the command and
+ // inject the ID into the in-page cache.
+ element_id = path_segments_.at(4);
+ return true;
+}
+
+bool WebElementCommand::GetElementLocation(bool in_view, int* x, int* y) {
+ scoped_ptr<ListValue> args(new ListValue());
+ Value* result = NULL;
+
+ std::wstring jscript = build_atom(GET_LOCATION, sizeof GET_LOCATION);
+ if (in_view) {
+ jscript.append(L"arguments[0].scrollIntoView();");
+ }
+ jscript.append(L"return getLocation(arguments[0]);");
+
+ args->Append(GetElementIdAsDictionaryValue(element_id));
+
+ ErrorCode error = session_->ExecuteScript(jscript, args.get(), &result);
+ if (error != kSuccess) {
+ LOG(INFO) << "Javascript failed to execute" << std::endl;
+ return false;
+ }
+
+ if (result == NULL || result->GetType() != Value::TYPE_DICTIONARY) {
+ LOG(ERROR) << "Expected JavaScript atom to return a dictionary";
+ return false;
+ }
+
+ DictionaryValue* dict = static_cast<DictionaryValue*>(result);
+ return dict->GetInteger("x", x) && dict->GetInteger("y", y);
+}
+
+bool WebElementCommand::GetElementSize(int* width, int* height) {
+ scoped_ptr<ListValue> args(new ListValue());
+ Value* result = NULL;
+
+ std::wstring jscript = build_atom(GET_SIZE, sizeof GET_LOCATION);
+ args->Append(GetElementIdAsDictionaryValue(element_id));
+
+ ErrorCode error = session_->ExecuteScript(jscript, args.get(), &result);
+ if (error != kSuccess) {
+ LOG(ERROR) << "Javascript failed to execute" << std::endl;
+ return false;
+ }
+
+ if (result == NULL || result->GetType() != Value::TYPE_DICTIONARY) {
+ LOG(ERROR) << "Expected JavaScript atom to return "
+ << "{width:number, height:number} dictionary." << std::endl;
+ return false;
+ }
+
+ DictionaryValue* dict = static_cast<DictionaryValue*>(result);
+ return dict->GetInteger("width", width) &&
+ dict->GetInteger("height", height);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/webelement_command.h b/chrome/test/webdriver/commands/webelement_command.h
new file mode 100644
index 0000000..a093b05
--- /dev/null
+++ b/chrome/test/webdriver/commands/webelement_command.h
@@ -0,0 +1,43 @@
+// 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_WEBDRIVER_COMMANDS_WEBELEMENT_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_WEBELEMENT_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Handles commands that interact with a web element in the WebDriver REST
+// service.
+class WebElementCommand : public WebDriverCommand {
+ public:
+ inline WebElementCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters),
+ path_segments_(path_segments) {}
+ virtual ~WebElementCommand() {}
+
+ virtual bool Init(Response* response);
+
+ protected:
+ bool GetElementLocation(bool in_view, int* x, int* y);
+ bool GetElementSize(int* width, int* height);
+
+ const std::vector<std::string>& path_segments_;
+ std::string element_id;
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(WebElementCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_WEBELEMENT_COMMAND_H_
+
diff --git a/chrome/test/webdriver/server.cc b/chrome/test/webdriver/server.cc
index 57d031f..f80659b 100644
--- a/chrome/test/webdriver/server.cc
+++ b/chrome/test/webdriver/server.cc
@@ -25,6 +25,7 @@
#include "chrome/test/webdriver/session_manager.h"
#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/commands/create_session.h"
+#include "chrome/test/webdriver/commands/execute_command.h"
#include "chrome/test/webdriver/commands/navigate_commands.h"
#include "chrome/test/webdriver/commands/session_with_id.h"
#include "chrome/test/webdriver/commands/source_command.h"
@@ -58,6 +59,7 @@ void SetCallback(struct mg_context* ctx, const char* pattern) {
void InitCallbacks(struct mg_context* ctx) {
SetCallback<CreateSession>(ctx, "/session");
SetCallback<BackCommand>(ctx, "/session/*/back");
+ SetCallback<ExecuteCommand>(ctx, "/session/*/execute");
SetCallback<ForwardCommand>(ctx, "/session/*/forward");
SetCallback<RefreshCommand>(ctx, "/session/*/refresh");
SetCallback<SourceCommand>(ctx, "/session/*/source");
@@ -81,6 +83,7 @@ int main(int argc, char *argv[]) {
CommandLine cmd_line = CommandLine(argc, argv);
#elif OS_WIN
std::string c;
+
for (int i = 0; i < argc; ++i) {
c += std::string(argv[i]);
}
diff --git a/chrome/test/webdriver/webdriver_remote_tests.py b/chrome/test/webdriver/webdriver_remote_tests.py
index d12cc3b..43371c7 100644
--- a/chrome/test/webdriver/webdriver_remote_tests.py
+++ b/chrome/test/webdriver/webdriver_remote_tests.py
@@ -15,6 +15,7 @@ import string
import subprocess
import sys
import time
+import types
import unittest
import urllib2
from selenium.remote.webdriver import WebDriver
@@ -40,11 +41,7 @@ WEBDRIVER_PROCESS = None
if not WEBDRIVER_SERVER_URL:
WEBDRIVER_SERVER_URL = 'http://localhost:%d' % WEBDRIVER_PORT
-class TestNavigation(unittest.TestCase):
-
- SEARCH = "http://www.google.com/webhp?hl=en"
- NEWS = "http://www.google.com/news?hl=en"
-
+class RemoteWebDriverTest(unittest.TestCase):
"""A new instance of chrome driver is started for every test case"""
def setUp(self):
global WEBDRIVER_SERVER_URL
@@ -57,7 +54,7 @@ class TestNavigation(unittest.TestCase):
sys.exit(-1)
time.sleep(5)
- self.driver = WebDriver.WebDriver(WEBDRIVER_SERVER_URL, "chrome", "ANY");
+ self.driver = WebDriver.WebDriver(WEBDRIVER_SERVER_URL, "chrome", "ANY")
self.assertTrue(self.driver)
def tearDown(self):
@@ -78,6 +75,54 @@ class TestNavigation(unittest.TestCase):
WEBDRIVER_PROCESS.kill()
WEBDRIVER_PROCESS = None
+ """Preforms a string search ignoring case"""
+ def assertFind(self, text, search):
+ text = string.lower(text)
+ search = string.lower(search)
+ self.assertNotEqual(-1, string.find(text, search))
+
+class TestJavaScriptExecution(RemoteWebDriverTest):
+ """ Test the execute javascript ability of the remote driver"""
+ def testNoModification(self):
+ self.driver.get("http://www.google.com")
+ title = self.driver.execute_script("return document.title")
+ self.assertEqual(title, self.driver.get_title())
+
+ def testModification(self):
+ self.driver.get("http://www.google.com")
+ title = self.driver.get_title()
+ self.assertFind(title, "google")
+ r = self.driver.execute_script("return document.Foo")
+ self.assertTrue(r == None)
+ self.driver.execute_script("document.Foo = \"Hello\"")
+ r = self.driver.execute_script("return document.Foo")
+ self.assertTrue(r == "Hello")
+
+ def testComplexObjectFetch(self):
+ self.driver.get("http://www.google.com")
+ loc = self.driver.execute_script("return window.location")
+ self.assertTrue(type(loc) is types.DictType)
+ self.assertTrue(loc.has_key("hostname"))
+ self.assertFind(loc["hostname"], "google")
+
+ def testJavascriptExeception(self):
+ self.driver.get("http://www.google.com")
+ self.assertRaises(ErrorInResponseException, self.driver.execute_script,
+ "return windows");
+
+ def testJavascriptWithNoReturn(self):
+ self.driver.get("http://www.google.com")
+ try:
+ ret = self.driver.execute_script("return window.foobar")
+ self.assertTrue(type(ret) is types.NoneType)
+ except:
+ self.assertTrue(False)
+
+
+class TestNavigation(RemoteWebDriverTest):
+ SEARCH = "http://www.google.com/webhp?hl=en"
+ NEWS = "http://www.google.com/news?hl=en"
+
"""Verifies that navigation to a specific page is correct by asserting on the
the reported URL. The function will not work with pages that redirect."""
def navigate(self, url):
@@ -88,12 +133,6 @@ class TestNavigation(unittest.TestCase):
u = self.driver.get_current_url()
self.assertEqual(u, url)
- """Preforms a string search ignoring case"""
- def assertFind(self, text, search):
- text = string.lower(text)
- search = string.lower(search)
- self.assertNotEqual(-1, string.find(text, search))
-
def testNavigateToURL(self):
# No redirects are allowed on the google home page.
self.navigate(self.SEARCH)
@@ -112,7 +151,7 @@ class TestNavigation(unittest.TestCase):
self.navigate(self.SEARCH)
# Go back to news.
- self.driver.back();
+ self.driver.back()
self.assertURL(self.NEWS)
# Move forward to search.
@@ -143,6 +182,7 @@ class TestNavigation(unittest.TestCase):
self.assertFind(source, u"feeling lucky") # Lucky button.
self.assertFind(source, u"google search") # Searh button.
+
if __name__ == '__main__':
parser = optparse.OptionParser('%prog [options]')
parser.add_option('-u', '--url', dest='url', action='store',