durdyev / webdriverxx

A C++ client library for Selenium Webdriver
MIT License
202 stars 41 forks source link
cpp selenium

Webdriver++

A C++ client library for Selenium Webdriver. You can use this library in any C++ project.

Install

mkdir build
cd build && cmake ..
sudo make && sudo make install

A quick example

Dependencies

You need to download and run selenium-server before run this example. http://www.seleniumhq.org/download/

#include <webdriverxx/webdriverxx.h>
using namespace webdriverxx;

int main() {
    WebDriver firefox = Start(Firefox());
    firefox
        .Navigate("http://google.com")
        .FindElement(ByCss("input[name=q]"))
        .SendKeys("Hello, world!")
        .Submit();
    return 0;    
}

Features

More examples

#include <webdriverxx/webdriver.h> and using namespace webdriverxx are assumed in all examples.

Start browser

#include <webdriverxx/browsers/firefox.h>

WebDriver ff = Start(Firefox());
#include <webdriverxx/browsers/chrome.h>

WebDriver gc = Start(Chrome());
#include <webdriverxx/browsers/ie.h>

WebDriver ie = Start(InternetExplorer());

Use proxy

WebDriver ie = Start(InternetExplorer().SetProxy(
    SocksProxy("127.0.0.1:3128")
        .SetUsername("user")
        .SetPassword("12345")
        .SetNoProxyFor("custom.host")
    ));
WebDriver ff = Start(Firefox().SetProxy(DirectConnection()));

Navigate browser

driver
    .Navigate("http://facebook.com")
    .Navigate("http://twitter.com")
    .Back()
    .Forward()
    .Refresh();

Find elements

// Throws exception if no match is found in the document
Element menu = driver.FindElement(ById("menu"));

// Returns empty vector if no such elements
// The search is performed inside the menu element
std::vector<Element> items = menu.FindElements(ByClass("item"));

Send keyboard input

// Sends text input or a shortcut to the element
driver.FindElement(ByTag("input")).SendKeys("Hello, world!");

// Sends text input or a shortcut to the active window
driver.SendKeys(Shortcut() << keys::Control << "t");

Execute Javascript

// Simple script, no parameters
driver.Execute("console.log('Hi there!')");

// A script with one parameter
driver.Execute("document.title = arguments[0]", JsArgs() << "Cowabunga!");

// A script with more than one parameter
driver.Execute("document.title = arguments[0] + '-' + arguments[1]",
        JsArgs() << "Beep" << "beep");

// Arrays or containers can also be used as parameters
const char* ss[] = { "Yabba", "dabba", "doo" };
driver.Execute("document.title = arguments[0].join(', ')", JsArgs() << ss);

// Even an Element can be passed to a script
auto element = driver.FindElement(ByTag("input"));
driver.Execute("arguments[0].value = 'That was nuts!'", JsArgs() << element);

Get something from Javascript

// Scalar types
auto title = driver.Eval<std::string>("return document.title")
auto number = driver.Eval<int>("return 123");
auto another_number = driver.Eval<double>("return 123.5");
auto flag = driver.Eval<bool>("return true");

// Containers (all std::back_inserter compatible)
std::vector<std::string> v = driver.Eval<std::vector<std::string>>(
        "return [ 'abc', 'def' ]"
        );

// Elements!
Element document_element = driver.Eval<Element>("return document.documentElement");

Wait implicitly for asynchronous operations

driver.SetImplicitTimeoutMs(5000);

// Should poll the DOM for 5 seconds before throwing an exception.
auto element = driver.FindElement(ByName("async_element"));

Wait explicitly for asynchronous operations

#include <webdriverxx/wait.h>

auto find_element = [&]{ return driver.FindElement(ById("async_element")); };
Element element = WaitForValue(find_element);
#include <webdriverxx/wait.h>

auto element_is_selected = [&]{
    return driver.FindElement(ById("asynchronously_loaded_element")).IsSelected();
    };
WaitUntil(element_is_selected);

Use matchers from Google Mock for waiting

#define WEBDRIVERXX_ENABLE_GMOCK_MATCHERS
#include <webdriverxx/wait_match.h>

driver.Navigate("http://initial_url.host.net");
auto url = [&]{ return driver.GetUrl(); };
using namespace ::testing;
auto final_url = WaitForMatch(url, HasSubstr("some_magic"));

Testing with real browsers

Prerequisites:

selenium-server -p 4444 &
./webdriverxx --browser=<firefox|chrome|...>

Advanced topics

Unicode

The library is designed to be encoding-agnostic. It doesn't make any assumptions about encodings. All strings are transferred as is, without modifications.

The WebDriver protocol is based on UTF-8, so all strings passed to the library/received from the library should be/are encoded using UTF-8.

Thread safety

Use common capabilities for all browsers

Capabilities common;
common.SetProxy(DirectConnection());
auto ff = Start(Firefox(common));
auto ie = Start(InternetExplorer(common));
auto gc = Start(Chrome(common));

Use required capabilities

Capabilities required = /* ... */;
auto ff = Start(Firefox(), required);

Use custom URL for connecting to WebDriver

const char* url = "http://localhost:4444/wd/hub/";

auto ff = Start(Firefox(), url);

// or
auto ff = Start(Firefox(), Capabilities() /* required */, url);

Transfer objects between C++ and Javascript

namespace custom {

struct Object {
    std::string string;
    int number;
};

// Conversion functions should be in the same namespace as the object
picojson::value CustomToJson(const Object& value) {
    return JsonObject()
        .Set("string", value.string)
        .Set("number", value.number);
}

void CustomFromJson(const picojson::value& value, Object& result) {
    assert(value.is<picojson::object>());
    result.string = FromJson<std::string>(value.get("string"));
    result.number = FromJson<int>(value.get("number"));
}

} // namespace custom

custom::Object o1 = { "abc", 123 };
driver.Execute("var o1 = arguments[0];", JsArgs() << o1);
custom::Object o1_copy = driver.Eval<custom::Object>("return o1");
custom::Object o2 = driver.Eval<custom::Object>("return { string: 'abc', number: 123 }");

Copyright © 2014 Sergey Kogan. Licensed under The MIT license.