summaryrefslogtreecommitdiffstats
path: root/chrome/test/webdriver
diff options
context:
space:
mode:
authorjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-23 01:16:15 +0000
committerjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-23 01:16:15 +0000
commit481be489436ba5329a247923c02a33ad2e67c2a5 (patch)
treea5912ae6d86e4e717293fd5d689da01eb02b6146 /chrome/test/webdriver
parent802865bb461ac9eef86d8961e842edbb4c2a66f2 (diff)
downloadchromium_src-481be489436ba5329a247923c02a33ad2e67c2a5.zip
chromium_src-481be489436ba5329a247923c02a33ad2e67c2a5.tar.gz
chromium_src-481be489436ba5329a247923c02a33ad2e67c2a5.tar.bz2
Implemnts the commands in webdriver to preform searching of elements on a page.
/session/:sessionId/timeouts/implicit_wait /session/:sessionId/element /session/:sessionId/elements /session/:sessionId/element/:id/element /session/:sessionId/element/:id/elements BUG=none TEST=webdriver_remote_tests.py Review URL: http://codereview.chromium.org/3643002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70015 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/webdriver')
-rw-r--r--chrome/test/webdriver/commands/find_element_commands.cc111
-rw-r--r--chrome/test/webdriver/commands/find_element_commands.h74
-rw-r--r--chrome/test/webdriver/commands/implicit_wait_command.cc45
-rw-r--r--chrome/test/webdriver/commands/implicit_wait_command.h42
-rw-r--r--chrome/test/webdriver/server.cc17
-rw-r--r--chrome/test/webdriver/webdriver_remote_tests.py74
6 files changed, 345 insertions, 18 deletions
diff --git a/chrome/test/webdriver/commands/find_element_commands.cc b/chrome/test/webdriver/commands/find_element_commands.cc
new file mode 100644
index 0000000..acda1393
--- /dev/null
+++ b/chrome/test/webdriver/commands/find_element_commands.cc
@@ -0,0 +1,111 @@
+// 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/find_element_commands.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "third_party/webdriver/atoms.h"
+#include "chrome/test/webdriver/error_codes.h"
+#include "chrome/test/webdriver/utility_functions.h"
+
+namespace webdriver {
+
+bool FindElementCommand::Init(Response* const response) {
+ if (!WebDriverCommand::Init(response)) {
+ SET_WEBDRIVER_ERROR(response, "Failure on Init for find element",
+ kInternalServerError);
+ return false;
+ }
+
+ if (!GetStringASCIIParameter("using", &use_) ||
+ !GetStringASCIIParameter("value", &value_)) {
+ SET_WEBDRIVER_ERROR(response,
+ "Request is missing required 'using' and/or 'value' data", kBadRequest);
+ return false;
+ }
+
+ // TODO(jmikhail): The findElement(s) atom should handle this conversion.
+ if ("class name" == use_) {
+ use_ = "className";
+ } else if ("link text" == use_) {
+ use_ = "linkText";
+ } else if ("partial link text" == use_) {
+ use_ = "partialLinkText";
+ } else if ("tag name" == use_) {
+ use_ = "tagName";
+ }
+
+ // Searching under a custom root if the URL pattern is
+ // "/session/$session/element/$id/element(s)"
+ root_element_id_ = GetPathVariable(4);
+
+ return true;
+}
+
+void FindElementCommand::ExecutePost(Response* const response) {
+ scoped_ptr<ListValue> args(new ListValue());
+ DictionaryValue* locator = new DictionaryValue();
+ ErrorCode error;
+ std::wstring jscript;
+ Value* result = NULL;
+ bool done = false;
+
+ // Set the command we are using to locate the value beging searched for.
+ locator->SetString(use_, value_);
+ args->Append(locator);
+ args->Append(root_element_id_.size() == 0 ? Value::CreateNullValue() :
+ WebDriverCommand::GetElementIdAsDictionaryValue(root_element_id_));
+ if (find_one_element_) {
+ jscript = build_atom(FIND_ELEMENT, sizeof FIND_ELEMENT);
+ jscript.append(L"var result = findElement(arguments[0], arguments[1]);")
+ .append(L"if (!result) {")
+ .append(L"var e = Error('Unable to locate element');")
+ .append(L"e.code = ")
+ .append(UTF8ToWide(base::IntToString(kNoSuchElement)))
+ .append(L";throw e;")
+ .append(L"} else { return result; }");
+ } else {
+ jscript = build_atom(FIND_ELEMENTS, sizeof FIND_ELEMENT);
+ jscript.append(L"return findElements(arguments[0], arguments[1]);");
+ }
+
+ // The element search needs to loop until at least one element is found or the
+ // session's implicit wait timeout expires, whichever occurs first.
+ base::Time start_time = base::Time::Now();
+
+ while (!done) {
+ if (result) {
+ delete result;
+ result = NULL;
+ }
+
+ error = session_->ExecuteScript(jscript, args.get(), &result);
+ if (error == kSuccess) {
+ // If searching for many elements, make sure we found at least one before
+ // stopping.
+ done = find_one_element_ ||
+ (result->GetType() == Value::TYPE_LIST &&
+ static_cast<ListValue*>(result)->GetSize() > 0);
+ } else if (error != kNoSuchElement) {
+ SET_WEBDRIVER_ERROR(response, "Internal error in find_element atom",
+ kInternalServerError);
+ return;
+ }
+
+ int64 elapsed_time = (base::Time::Now() - start_time).InMilliseconds();
+ done = done || elapsed_time > session_->implicit_wait();
+ PlatformThread::Sleep(50); // Prevent a busy loop that eats the cpu.
+ }
+
+ response->set_value(result);
+ response->set_status(error);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/find_element_commands.h b/chrome/test/webdriver/commands/find_element_commands.h
new file mode 100644
index 0000000..cc45d10
--- /dev/null
+++ b/chrome/test/webdriver/commands/find_element_commands.h
@@ -0,0 +1,74 @@
+// 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_FIND_ELEMENT_COMMANDS_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_FIND_ELEMENT_COMMANDS_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/command.h"
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Base class for searching a page, this class can find either a single
+// webelement or return multiple matches.
+class FindElementCommand : public WebDriverCommand {
+ public:
+ FindElementCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters,
+ const bool find_one_element)
+ : WebDriverCommand(path_segments, parameters),
+ find_one_element_(find_one_element) {}
+ virtual ~FindElementCommand() {}
+
+ virtual bool Init(Response* const response);
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return false; }
+ const bool find_one_element_;
+ std::string root_element_id_;
+ std::string use_;
+ std::string value_;
+
+ DISALLOW_COPY_AND_ASSIGN(FindElementCommand);
+};
+
+// Search for an element on the page, starting from the document root.
+// The located element will be returned as a WebElement JSON object. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/element
+class FindOneElementCommand : public FindElementCommand {
+ public:
+ FindOneElementCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : FindElementCommand(path_segments, parameters, true) {}
+ virtual ~FindOneElementCommand() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FindOneElementCommand);
+};
+
+// Search for multiple elements on the page, starting from the identified
+// element. The located elements will be returned as a WebElement JSON
+// objects. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/elements
+class FindManyElementsCommand : public FindElementCommand {
+ public:
+ FindManyElementsCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : FindElementCommand(path_segments, parameters, false) {}
+ virtual ~FindManyElementsCommand() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FindManyElementsCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_FIND_ELEMENT_COMMANDS_H_
+
diff --git a/chrome/test/webdriver/commands/implicit_wait_command.cc b/chrome/test/webdriver/commands/implicit_wait_command.cc
new file mode 100644
index 0000000..4e2156f
--- /dev/null
+++ b/chrome/test/webdriver/commands/implicit_wait_command.cc
@@ -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.
+
+#include <string>
+
+#include "base/utf_string_conversions.h"
+#include "chrome/test/webdriver/commands/implicit_wait_command.h"
+
+namespace webdriver {
+
+bool ImplicitWaitCommand::Init(Response* response) {
+ if (!(WebDriverCommand::Init(response))) {
+ SET_WEBDRIVER_ERROR(response, "Failure on Init for find element",
+ kInternalServerError);
+ return false;
+ }
+
+ // Record the requested wait time.
+ if (!GetIntegerParameter("ms", &ms_to_wait_)) {
+ SET_WEBDRIVER_ERROR(response, "Request missing ms parameter",
+ kBadRequest);
+ return false;
+ }
+
+ return true;
+}
+
+void ImplicitWaitCommand::ExecutePost(Response* const response) {
+ // Validate the wait time before setting it to the session.
+ if (ms_to_wait_ < 0) {
+ SET_WEBDRIVER_ERROR(response, "Wait must be non-negative",
+ kBadRequest);
+ return;
+ }
+
+ session_->set_implicit_wait(ms_to_wait_);
+ LOG(INFO) << "Implicit wait set to: " << ms_to_wait_ << " ms";
+
+ response->set_value(new StringValue("success"));
+ response->set_status(kSuccess);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/implicit_wait_command.h b/chrome/test/webdriver/commands/implicit_wait_command.h
new file mode 100644
index 0000000..a765303
--- /dev/null
+++ b/chrome/test/webdriver/commands/implicit_wait_command.h
@@ -0,0 +1,42 @@
+// 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_IMPLICIT_WAIT_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_IMPLICIT_WAIT_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Set the amount of time the driver should wait when searching for elements.
+// If this command is never sent, the driver will default to an implicit wait
+// of 0 ms. Until the webelement commands are checked in we do no use this
+// variable. For more information see:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/timeouts/implicit_wait
+class ImplicitWaitCommand : public WebDriverCommand {
+ public:
+ inline ImplicitWaitCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters), ms_to_wait_(0) {}
+ virtual ~ImplicitWaitCommand() {}
+
+ virtual bool Init(Response* response);
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ int ms_to_wait_;
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(ImplicitWaitCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_IMPLICIT_WAIT_COMMAND_H_
+
diff --git a/chrome/test/webdriver/server.cc b/chrome/test/webdriver/server.cc
index 8465f14..4760928 100644
--- a/chrome/test/webdriver/server.cc
+++ b/chrome/test/webdriver/server.cc
@@ -26,6 +26,8 @@
#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/find_element_commands.h"
+#include "chrome/test/webdriver/commands/implicit_wait_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"
@@ -68,6 +70,13 @@ void InitCallbacks(struct mg_context* ctx) {
SetCallback<URLCommand>(ctx, "/session/*/url");
SetCallback<SpeedCommand>(ctx, "/session/*/speed");
+ // WebElement commands
+ SetCallback<ImplicitWaitCommand>(ctx, "/session/*/timeouts/implicit_wait");
+ SetCallback<FindOneElementCommand>(ctx, "/session/*/element");
+ SetCallback<FindManyElementsCommand>(ctx, "/session/*/elements");
+ SetCallback<FindOneElementCommand>(ctx, "/session/*/element/*/element");
+ SetCallback<FindManyElementsCommand>(ctx, "/session/*/elements/*/elements");
+
// Since the /session/* is a wild card that would match the above URIs, this
// line MUST be the last registered URI with the server.
SetCallback<SessionWithID>(ctx, "/session/*");
@@ -80,7 +89,7 @@ void InitCallbacks(struct mg_context* ctx) {
int main(int argc, char *argv[]) {
struct mg_context *ctx;
base::AtExitManager exit;
- std::string port = "8080";
+ std::string port = "9515";
#ifdef OS_POSIX
CommandLine cmd_line = CommandLine(argc, argv);
#elif OS_WIN
@@ -110,14 +119,14 @@ int main(int argc, char *argv[]) {
session->SetIPAddress(port);
// Initialize SHTTPD context.
- // Listen on port 8080 or port specified on command line.
- // TODO(jmikhail) Maybe add port 8081 as a secure connection.
+ // Listen on port 9515 or port specified on command line.
+ // TODO(jmikhail) Maybe add port 9516 as a secure connection.
ctx = mg_start();
mg_set_option(ctx, "ports", port.c_str());
webdriver::InitCallbacks(ctx);
- std::cout << "Starting server" << std::endl;
+ std::cout << "Starting server on port: " << port << std::endl;
// The default behavior is to run this service forever.
while (true)
PlatformThread::Sleep(3600);
diff --git a/chrome/test/webdriver/webdriver_remote_tests.py b/chrome/test/webdriver/webdriver_remote_tests.py
index 43371c7..1ff2b44 100644
--- a/chrome/test/webdriver/webdriver_remote_tests.py
+++ b/chrome/test/webdriver/webdriver_remote_tests.py
@@ -18,8 +18,10 @@ import time
import types
import unittest
import urllib2
-from selenium.remote.webdriver import WebDriver
from selenium.common.exceptions import ErrorInResponseException
+from selenium.common.exceptions import NoSuchElementException
+from selenium.remote.webdriver import WebDriver
+from selenium.remote.webdriver.webelement import WebElement
from urlparse import urlparse
@@ -42,6 +44,19 @@ if not WEBDRIVER_SERVER_URL:
WEBDRIVER_SERVER_URL = 'http://localhost:%d' % WEBDRIVER_PORT
class RemoteWebDriverTest(unittest.TestCase):
+ 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):
+ self.driver.get(url)
+ self.assertURL(url)
+
+ def assertURL(self, url):
+ u = self.driver.get_current_url()
+ self.assertEqual(u, url)
+
"""A new instance of chrome driver is started for every test case"""
def setUp(self):
global WEBDRIVER_SERVER_URL
@@ -81,6 +96,50 @@ class RemoteWebDriverTest(unittest.TestCase):
search = string.lower(search)
self.assertNotEqual(-1, string.find(text, search))
+class TestFindElement(RemoteWebDriverTest):
+ def testFindByName(self):
+ navigate(SEARCH)
+ # Find the Google search button.
+ q = self.driver.find_element_by_name("q")
+ self.assertTrue(isinstance(q, WebElement))
+ # Trying looking for an element not on the page.
+ self.assertRaises(NoSuchElementException,
+ self.driver.find_elment_by_name, "q2")
+ # Try to find the Google search button using the multiple find method.
+ q = self.driver.find_elements_by_name("q")
+ self.assertTrue(isinstance(q, list))
+ self.assertTrue(len(q), 1)
+ self.assertTrue(isinstance(q[0], WebElement))
+ # Try finding something not on page, with multiple find an empty array
+ # should return and no exception thrown.
+ q = self.driver.find_elements_by_name("q2")
+ assertTrue(q == [])
+ # Find a hidden element on the page
+ q = self.driver.find_element_by_name("oq")
+ self.assertTrue(isinstance(q, WebElement))
+
+ def testFindElementById(self):
+ navigate(SEARCH)
+ # Find the padding for the logo near the search bar.
+ elem = self.driver.find_element_by_id("logocont")
+ self.assertTrue(isinstance(elem, WebElement))
+ # Look for an ID not there.
+ self.assertRaises(NoSuchElementException,
+ self.driver.find_element_by_id, "logocont")
+
+ def testFindElementById0WithTimeout(self):
+ self.set_implicit_wait(0)
+ navigate(SEARCH)
+ # Find the padding for the logo near the search bar.
+ elem = self.driver.find_element_by_id("logocont")
+ self.assertTrue(isinstance(elem, WebElement))
+ self.set_implicit_wait(5000)
+ navigate(SEARCH)
+ # Look for an ID not there.
+ self.assertRaises(NoSuchElementException,
+ self.driver.find_element_by_id, "logocont")
+
+
class TestJavaScriptExecution(RemoteWebDriverTest):
""" Test the execute javascript ability of the remote driver"""
def testNoModification(self):
@@ -120,19 +179,6 @@ class TestJavaScriptExecution(RemoteWebDriverTest):
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):
- self.driver.get(url)
- self.assertURL(url)
-
- def assertURL(self, url):
- u = self.driver.get_current_url()
- self.assertEqual(u, url)
-
def testNavigateToURL(self):
# No redirects are allowed on the google home page.
self.navigate(self.SEARCH)