diff options
author | jmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-23 01:16:15 +0000 |
---|---|---|
committer | jmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-23 01:16:15 +0000 |
commit | 481be489436ba5329a247923c02a33ad2e67c2a5 (patch) | |
tree | a5912ae6d86e4e717293fd5d689da01eb02b6146 /chrome/test/webdriver | |
parent | 802865bb461ac9eef86d8961e842edbb4c2a66f2 (diff) | |
download | chromium_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.cc | 111 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/find_element_commands.h | 74 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/implicit_wait_command.cc | 45 | ||||
-rw-r--r-- | chrome/test/webdriver/commands/implicit_wait_command.h | 42 | ||||
-rw-r--r-- | chrome/test/webdriver/server.cc | 17 | ||||
-rw-r--r-- | chrome/test/webdriver/webdriver_remote_tests.py | 74 |
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) |