Skip to content
Snippets Groups Projects
Commit 74ddb63b authored by jmikhail@google.com's avatar jmikhail@google.com
Browse files

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@70107 0039d316-1c4b-4281-b951-d872f2087c98
parent f5494d49
No related branches found
No related tags found
No related merge requests found
......@@ -685,6 +685,10 @@
'test/webdriver/commands/command.cc',
'test/webdriver/commands/create_session.h',
'test/webdriver/commands/create_session.cc',
'test/webdriver/commands/find_element_commands.h',
'test/webdriver/commands/find_element_commands.cc',
'test/webdriver/commands/implicit_wait_command.h',
'test/webdriver/commands/implicit_wait_command.cc',
'test/webdriver/commands/execute_command.h',
'test/webdriver/commands/execute_command.cc',
'test/webdriver/commands/navigate_commands.h',
......
// 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
// 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_
// 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* const 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
// 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* const 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_
......@@ -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);
......
......@@ -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)
......
......@@ -173,6 +173,17 @@ class WebDriver(object):
resp = self._execute(Command.GET_TITLE)
return resp['value']
def set_implicit_wait(self, wait):
"""Set the amount of time the driver should wait when searching for
elements. When searching for a single element, the driver should poll
the page until an element is found or the timeout expires, whichever
occurs first. When searching for multiple elements, the driver should
poll the page until at least one element is found or the timeout
expires, at which point it should return an empty list. If this
command is never sent, the driver should default to an implicit wait
of 0ms."""
self._execute(Command.IMPLICIT_WAIT, {'ms': wait})
def find_element_by_id(self, id_):
"""Finds element by id."""
return self._find_element_by("id", id_)
......
......@@ -76,4 +76,5 @@ class Command(object):
GET_ELEMENT_ATTRIBUTE = "getElementAttribute"
GET_ELEMENT_VALUE_OF_CSS_PROPERTY = "getElementValueOfCssProperty"
ELEMENT_EQUALS = "elementEquals"
IMPLICIT_WAIT = "setImplicitWait"
SCREENSHOT = "screenshot"
......@@ -128,6 +128,7 @@ class RemoteConnection(object):
Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'),
Command.GET_TITLE: ('GET', '/session/$sessionId/title'),
Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'),
Command.IMPLICIT_WAIT: ('POST', '/session/$sessionId/timeouts/implicit_wait'),
Command.SCREENSHOT: ('GET', '/session/$sessionId/screenshot'),
Command.SET_BROWSER_VISIBLE:
('POST', '/session/$sessionId/visible'),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment