diff options
author | jmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-29 22:46:34 +0000 |
---|---|---|
committer | jmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-29 22:46:34 +0000 |
commit | d551cdcc3c87e382d24d1912fc41c6489bb0bd40 (patch) | |
tree | d94f97d26792478d1113a73b7cf89eb1ea6b8678 /chrome/test/webdriver | |
parent | 1a5af201a654bf80ce1bd6721b31a97311ae67a6 (diff) | |
download | chromium_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/test/webdriver')
-rw-r--r-- | chrome/test/webdriver/commands/command.cc | 5 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/command.h | 16 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/execute_command.cc | 78 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/execute_command.h | 45 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/webelement_command.cc | 86 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/webelement_command.h | 43 | ||||
-rw-r--r-- | chrome/test/webdriver/server.cc | 3 | ||||
-rw-r--r-- | chrome/test/webdriver/webdriver_remote_tests.py | 66 |
8 files changed, 327 insertions, 15 deletions
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', |