Full Code of durdyev/webdriverxx for AI

master 11c4addbb3f7 cached
83 files
200.1 KB
55.9k tokens
422 symbols
1 requests
Download .txt
Showing preview only (219K chars total). Download the full file or copy to clipboard to get everything.
Repository: durdyev/webdriverxx
Branch: master
Commit: 11c4addbb3f7
Files: 83
Total size: 200.1 KB

Directory structure:
gitextract_52i55o91/

├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include/
│   ├── webdriverxx/
│   │   ├── browsers/
│   │   │   ├── chrome.h
│   │   │   ├── firefox.h
│   │   │   ├── ie.h
│   │   │   └── phantom.h
│   │   ├── by.h
│   │   ├── capabilities.h
│   │   ├── client.h
│   │   ├── client.inl
│   │   ├── conversions.h
│   │   ├── detail/
│   │   │   ├── error_handling.h
│   │   │   ├── factories.h
│   │   │   ├── factories_impl.h
│   │   │   ├── finder.h
│   │   │   ├── finder.inl
│   │   │   ├── http_client.h
│   │   │   ├── http_connection.h
│   │   │   ├── http_request.h
│   │   │   ├── keyboard.h
│   │   │   ├── meta_tools.h
│   │   │   ├── resource.h
│   │   │   ├── shared.h
│   │   │   ├── time.h
│   │   │   ├── to_string.h
│   │   │   └── types.h
│   │   ├── element.h
│   │   ├── element.inl
│   │   ├── errors.h
│   │   ├── js_args.h
│   │   ├── keys.h
│   │   ├── picojson.h
│   │   ├── response_status_code.h
│   │   ├── session.h
│   │   ├── session.inl
│   │   ├── types.h
│   │   ├── wait.h
│   │   ├── wait_match.h
│   │   ├── webdriver.h
│   │   └── window.h
│   └── webdriverxx.h
└── test/
    ├── CMakeLists.txt
    ├── alerts_test.cpp
    ├── browsers_test.cpp
    ├── capabilities_test.cpp
    ├── client_test.cpp
    ├── conversions_test.cpp
    ├── element_test.cpp
    ├── environment.h
    ├── examples_test.cpp
    ├── finder_test.cpp
    ├── frames_test.cpp
    ├── http_connection_test.cpp
    ├── js_test.cpp
    ├── keyboard_test.cpp
    ├── main.cpp
    ├── mouse_test.cpp
    ├── pages/
    │   ├── alerts.html
    │   ├── element.html
    │   ├── finder.html
    │   ├── frame1.html
    │   ├── frame2.html
    │   ├── frame3.html
    │   ├── frames.html
    │   ├── frameset.html
    │   ├── js.html
    │   ├── keyboard.html
    │   ├── mouse.html
    │   ├── navigation1.html
    │   ├── navigation2.html
    │   ├── redirect.html
    │   ├── session.html
    │   └── webdriver.html
    ├── resource_test.cpp
    ├── session_test.cpp
    ├── shared_test.cpp
    ├── to_string_test.cpp
    ├── wait_match_test.cpp
    ├── wait_test.cpp
    └── webdriver_test.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app


================================================
FILE: .travis.yml
================================================
language: cpp

compiler:
    - gcc
    - clang

branches:
  only:
    - master
    - dev

before_script:
    - mkdir build
    - cd build
    - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
    - phantomjs --webdriver=7777 --webdriver-loglevel=WARN &
    - npm install http-server -g
    - http-server ./test/pages --silent &

script:
    - cmake --build .
    - ctest -V


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8)
project(webdriverxx)

if (NOT MSVC)
	include(CheckCXXCompilerFlag)
	CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
	CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
	if(COMPILER_SUPPORTS_CXX11)
		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
	elseif(COMPILER_SUPPORTS_CXX0X)
		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
	endif()
endif()

enable_testing()

include_directories(include)
add_subdirectory(test)

install(DIRECTORY
		${CMAKE_SOURCE_DIR}/include/
		DESTINATION /usr/local/include/${PROJECT_NAME}
		FILES_MATCHING PATTERN "*")



================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Sergey Kogan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================

# Webdriver++

A C++ client library for [Selenium Webdriver](http://www.seleniumhq.org/).
You can use this library in any C++ project.


## Install

```bash
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/

```cpp
#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

- Chainable commands.
- Value-like objects compatible with STL containers.
- Header-only.
- Lightweight dependencies:
    - [libcurl](http://curl.haxx.se/libcurl/),
    - [picojson](https://github.com/kazuho/picojson).
- Can be used with any testing framework.
- Linux, Mac and Windows.
- clang (3.4), GCC (4.6) and Visual Studio (2010).

## More examples

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

### Start browser

```cpp
#include <webdriverxx/browsers/firefox.h>

WebDriver ff = Start(Firefox());
```

```cpp
#include <webdriverxx/browsers/chrome.h>

WebDriver gc = Start(Chrome());
```

```cpp
#include <webdriverxx/browsers/ie.h>

WebDriver ie = Start(InternetExplorer());
```

### Use proxy

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

```cpp
WebDriver ff = Start(Firefox().SetProxy(DirectConnection()));
```

### Navigate browser

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

### Find elements

```cpp
// 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

```cpp
// 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

```cpp
// 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

```cpp
// 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](http://selenium-python.readthedocs.org/en/latest/waits.html) for asynchronous operations

```cpp
driver.SetImplicitTimeoutMs(5000);

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

### [Wait explicitly](http://selenium-python.readthedocs.org/en/latest/waits.html) for asynchronous operations

```cpp
#include <webdriverxx/wait.h>

auto find_element = [&]{ return driver.FindElement(ById("async_element")); };
Element element = WaitForValue(find_element);
```

```cpp
#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](https://code.google.com/p/googlemock/) for waiting

```cpp
#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](http://www.seleniumhq.org/download/)

```bash
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

- Webdriver++ objects are not thread safe. It is not safe to use
neither any single object nor different objects obtained from a single WebDriver
concurrently without synchronization. On the other side, Webdriver++ objects
don't use global variables so it is OK to use different instances of WebDriver
in different threads.

- The CURL library should be explicitly initialized if several WebDrivers are used from
multiple threads. Call `curl_global_init(CURL_GLOBAL_ALL);` from `<curl/curl.h>`
once per process before using this library.

### Use common capabilities for all browsers

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

### Use required capabilities

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

### Use custom URL for connecting to WebDriver

```cpp
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

```cpp
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 &copy; 2014 Sergey Kogan.
Licensed under [The MIT license](https://github.com/sekogan/webdriverxx/blob/master/LICENSE).


================================================
FILE: include/webdriverxx/browsers/chrome.h
================================================
#ifndef WEBDRIVERXX_BROWSERS_CHROME_H
#define WEBDRIVERXX_BROWSERS_CHROME_H

#include "../capabilities.h"

namespace webdriverxx {
namespace chrome {

struct PerfLoggingPrefs : JsonObject {
	WEBDRIVERXX_PROPERTIES_BEGIN(PerfLoggingPrefs)
	WEBDRIVERXX_PROPERTY(EnableNetwork, "enableNetwork", bool)
	WEBDRIVERXX_PROPERTY(enablePage, "enablePage", bool)
	WEBDRIVERXX_PROPERTY(enableTimeline, "enableTimeline", bool)
	WEBDRIVERXX_PROPERTY(tracingCategories, "tracingCategories", std::string)
	WEBDRIVERXX_PROPERTY(bufferUsageReportingInterval, "bufferUsageReportingInterval", int)
	WEBDRIVERXX_PROPERTIES_END()
};

} // namespace chrome

struct Chrome : Capabilities { // copyable
	Chrome(const Capabilities& defaults = Capabilities())
		: Capabilities(defaults) {
		SetBrowserName(browser::Chrome);
		SetVersion("");
		SetPlatform(platform::Any);
	}

	// See https://sites.google.com/a/chromium.org/chromedriver/capabilities for details
	WEBDRIVERXX_PROPERTIES_BEGIN(Chrome)
	WEBDRIVERXX_PROPERTY(LoggingPrefs, "loggingPrefs", LoggingPrefs)
	WEBDRIVERXX_PROPERTY(Args, "args", std::vector<std::string>)
	WEBDRIVERXX_PROPERTY(Binary, "binary", std::string)
	// Each extension is a base64-encoded .crx file
	WEBDRIVERXX_PROPERTY(Extensions, "extensions", std::vector<std::string>)
	WEBDRIVERXX_PROPERTY(LocalState, "localState", JsonObject)
	WEBDRIVERXX_PROPERTY(Prefs, "prefs", JsonObject)
	WEBDRIVERXX_PROPERTY(Detach, "detach", bool)
	WEBDRIVERXX_PROPERTY(DebuggerAddress, "debuggerAddress", std::string)
	WEBDRIVERXX_PROPERTY(ExcludeSwitches, "excludeSwitches", std::vector<std::string>)
	WEBDRIVERXX_PROPERTY(MinidumpPath, "minidumpPath", std::string)
	WEBDRIVERXX_PROPERTY(MobileEmulation, "mobileEmulation", JsonObject)
	WEBDRIVERXX_PROPERTY(PerfLoggingPrefs, "perfLoggingPrefs", chrome::PerfLoggingPrefs)
	WEBDRIVERXX_PROPERTIES_END()
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/browsers/firefox.h
================================================
#ifndef WEBDRIVERXX_BROWSERS_FIREFOX_H
#define WEBDRIVERXX_BROWSERS_FIREFOX_H

#include "../capabilities.h"

namespace webdriverxx {

struct Firefox : Capabilities { // copyable
	Firefox(const Capabilities& defaults = Capabilities())
		: Capabilities(defaults) {
		SetBrowserName(browser::Firefox);
		SetVersion("");
		SetPlatform(platform::Any);
	}

	WEBDRIVERXX_PROPERTIES_BEGIN(Firefox)
	// Profile is a profile folder, zipped and base64 encoded.
	// TODO: add FirefoxProfile 
	WEBDRIVERXX_PROPERTY(Profile, "firefox_profile", std::string)
	WEBDRIVERXX_PROPERTY(LoggingPrefs, "loggingPrefs", LoggingPrefs)
	WEBDRIVERXX_PROPERTY(FirefoxBinary, "firefox_binary", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/browsers/ie.h
================================================
#ifndef WEBDRIVERXX_BROWSERS_IE_H
#define WEBDRIVERXX_BROWSERS_IE_H

#include "../capabilities.h"

namespace webdriverxx {
namespace ie {

namespace log_level {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Trace = "TRACE";
ConstValue Debug = "DEBUG";
ConstValue Info = "INFO";
ConstValue Warning = "WARN";
ConstValue Error = "ERROR";
ConstValue Fatal = "FATAL";
} // namespace log_level

} // namespace ie

struct InternetExplorer : Capabilities { // copyable
	InternetExplorer(const Capabilities& defaults = Capabilities())
		: Capabilities(defaults) {
		SetBrowserName(browser::InternetExplorer);
		SetVersion("");
		SetPlatform(platform::Any);
	}

	WEBDRIVERXX_PROPERTIES_BEGIN(InternetExplorer)
	WEBDRIVERXX_PROPERTY(SkipProtectedModeCheck, "ignoreProtectedModeSettings", bool)
	WEBDRIVERXX_PROPERTY(IgnoreZoomSetting, "ignoreZoomSetting", bool)
	WEBDRIVERXX_PROPERTY(InitialUrl, "initialBrowserUrl", std::string)
	WEBDRIVERXX_PROPERTY(EnablePersistentHover, "enablePersistentHover", bool)
	WEBDRIVERXX_PROPERTY(EnableElementCacheCleanup, "enableElementCacheCleanup", bool)
	WEBDRIVERXX_PROPERTY(RequireWindowFocus, "requireWindowFocus", bool)
	WEBDRIVERXX_PROPERTY(BrowserAttachTimeoutMs, "browserAttachTimeout", int)
	WEBDRIVERXX_PROPERTY(ForceCreateProcessApi, "ie.forceCreateProcessApi", bool)
	WEBDRIVERXX_PROPERTY(CommandLineSwitches, "ie.browserCommandLineSwitches", std::string)
	WEBDRIVERXX_PROPERTY(UsePerProcessProxy, "ie.usePerProcessProxy", bool)
	WEBDRIVERXX_PROPERTY(EnsureCleanSession, "ie.ensureCleanSession", bool)
	WEBDRIVERXX_PROPERTY(LogFile, "logFile", std::string)
	WEBDRIVERXX_PROPERTY(LogLevel, "logLevel", ie::log_level::Value)
	WEBDRIVERXX_PROPERTY(Host, "host", std::string)
	WEBDRIVERXX_PROPERTY(ExtractPath, "extractPath", std::string)
	WEBDRIVERXX_PROPERTY(Silent, "silent", bool)
	WEBDRIVERXX_PROPERTY(ProxyByServer, "ie.setProxyByServer", bool)
	WEBDRIVERXX_PROPERTIES_END()
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/browsers/phantom.h
================================================
#ifndef WEBDRIVERXX_BROWSERS_PHANTOM_H
#define WEBDRIVERXX_BROWSERS_PHANTOM_H

#include "../capabilities.h"

namespace webdriverxx {

struct Phantom : Capabilities { // copyable
    Phantom(const Capabilities& defaults = Capabilities())
		: Capabilities(defaults) {
		SetBrowserName(browser::Phantom);
		SetVersion("");
		SetPlatform(platform::Any);
	}

	WEBDRIVERXX_PROPERTIES_BEGIN(Phantom)
	// Profile is a profile folder, zipped and base64 encoded.
	WEBDRIVERXX_PROPERTY(LoggingPrefs, "loggingPrefs", LoggingPrefs)
	WEBDRIVERXX_PROPERTIES_END()
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/by.h
================================================
#ifndef WEBDRIVERXX_BY_H
#define WEBDRIVERXX_BY_H

#include <string>

namespace webdriverxx {

class By { // copyable
public:
	By(const std::string& strategy, const std::string& value)
		: strategy_(strategy)
		, value_(value)
	{}

	const std::string& GetStrategy() const {
		return strategy_;
	}

	const std::string& GetValue() const {
		return value_;
	}

private:
	std::string strategy_;
	std::string value_;
};

inline By ByClass(const std::string& value) {
	return By("class name", value);
}

inline By ByCss(const std::string& value) {
	return By("css selector", value);
}

inline By ById(const std::string& value) {
	return By("id", value);
}

inline By ByName(const std::string& value) {
	return By("name", value);
}

inline By ByLinkText(const std::string& value) {
	return By("link text", value);
}

inline By ByPartialLinkText(const std::string& value) {
	return By("partial link text", value);
}

inline By ByTag(const std::string& value) {
	return By("tag name", value);
}

inline By ByXPath(const std::string& value) {
	return By("xpath", value);
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/capabilities.h
================================================
#ifndef WEBDRIVERXX_CAPABILITIES_H
#define WEBDRIVERXX_CAPABILITIES_H

#include "conversions.h"
#include "picojson.h"
#include <string>

namespace webdriverxx {

namespace browser {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Android = "android";
ConstValue Chrome = "chrome";
ConstValue Firefox = "firefox";
ConstValue HtmlUnit = "htmlunit";
ConstValue InternetExplorer = "internet explorer";
ConstValue IPhone = "iPhone";
ConstValue IPad = "iPad";
ConstValue Mock = "mock";
ConstValue Opera = "opera";
ConstValue Safari = "safari";
ConstValue Phantom = "phantomjs";
} // namespace browser

namespace platform {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Any = "ANY";
ConstValue Windows = "WINDOWS";
ConstValue Xp = "XP";
ConstValue Vista = "VISTA";
ConstValue Mac = "MAC";
ConstValue Linux = "LINUX";
ConstValue Unix = "UNIX";
ConstValue Android = "ANDROID";
} // namespace platform

namespace unexpected_alert_behaviour {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Accept = "accept";
ConstValue Dismiss = "dismiss";
ConstValue Ignore = "ignore";
} // namespace unexpected_alert_behaviour

namespace proxy_type {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Direct = "direct";
ConstValue Manual = "manual"; // Manual proxy settings configured, e.g. setting a proxy for HTTP, a proxy for FTP
ConstValue Pac = "pac"; // Proxy autoconfiguration from a URL
ConstValue Autodetect = "autodetect"; // Proxy autodetection, probably with WPAD
ConstValue System = "system"; // Use system settings
} // namespace proxy_type

#define WEBDRIVERXX_PROPERTIES_BEGIN(this_class)	typedef this_class This;
#define WEBDRIVERXX_PROPERTIES_END()

#define WEBDRIVERXX_PROPERTY_RONLY(name, id, type) \
	type Get##name() const { return GetOptional<type>(id); } \
	bool Has##name() { return Has(id); }

#define WEBDRIVERXX_PROPERTY(name, id, type) \
	WEBDRIVERXX_PROPERTY_RONLY(name, id, type) \
	This& Set##name(const type& value) { Set(id, value); return *this; }

struct Proxy : JsonObject { // copyable
	WEBDRIVERXX_PROPERTIES_BEGIN(Proxy)
	WEBDRIVERXX_PROPERTY(ProxyType, "proxyType", proxy_type::Value)
	WEBDRIVERXX_PROPERTIES_END()
};

struct DirectConnection : Proxy { // copyable
	DirectConnection() { SetProxyType(proxy_type::Direct); }
};

struct AutodetectProxy : Proxy { // copyable
	AutodetectProxy() { SetProxyType(proxy_type::Autodetect); }
};

struct SystemProxy : Proxy { // copyable
	SystemProxy() { SetProxyType(proxy_type::System); }
};

struct AutomaticProxyFromUrl : Proxy { // copyable
	explicit AutomaticProxyFromUrl(const std::string& url) {
		SetProxyType(proxy_type::Pac);
		SetAutoconfigUrl(url);
	}

	WEBDRIVERXX_PROPERTIES_BEGIN(AutomaticProxyFromUrl)
	WEBDRIVERXX_PROPERTY(AutoconfigUrl, "proxyAutoconfigUrl", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

struct ManualProxy : Proxy { // copyable
	ManualProxy() { SetProxyType(proxy_type::Manual); }

	WEBDRIVERXX_PROPERTIES_BEGIN(ManualProxy)
	WEBDRIVERXX_PROPERTY(NoProxyFor, "noProxy", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

struct FtpProxy : ManualProxy { // copyable
	explicit FtpProxy(const std::string& address) { SetProxyAddress(address); }

	WEBDRIVERXX_PROPERTIES_BEGIN(FtpProxy)
	WEBDRIVERXX_PROPERTY(ProxyAddress, "ftpProxy", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

struct HttpProxy : ManualProxy { // copyable
	explicit HttpProxy(const std::string& address) { SetProxyAddress(address); }

	WEBDRIVERXX_PROPERTIES_BEGIN(HttpProxy)
	WEBDRIVERXX_PROPERTY(ProxyAddress, "httpProxy", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

struct SslProxy : ManualProxy { // copyable
	explicit SslProxy(const std::string& address) { SetProxyAddress(address); }

	WEBDRIVERXX_PROPERTIES_BEGIN(SslProxy)
	WEBDRIVERXX_PROPERTY(ProxyAddress, "sslProxy", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

struct SocksProxy : ManualProxy { // copyable
	explicit SocksProxy(const std::string& address) { SetProxyAddress(address); }

	WEBDRIVERXX_PROPERTIES_BEGIN(SocksProxy)
	WEBDRIVERXX_PROPERTY(ProxyAddress, "socksProxy", std::string)
	WEBDRIVERXX_PROPERTY(Username, "socksUsername", std::string)
	WEBDRIVERXX_PROPERTY(Password, "socksPassword", std::string)
	WEBDRIVERXX_PROPERTIES_END()
};

namespace log_level {
typedef std::string Value;
typedef const char* const ConstValue;
ConstValue Off = "OFF";
ConstValue Severe = "SEVERE";
ConstValue Warning = "WARNING";
ConstValue Info = "INFO";
ConstValue Config = "CONFIG";
ConstValue Fine = "FINE";
ConstValue Finer = "FINER";
ConstValue Finest = "FINEST";
ConstValue All = "ALL";
} // namespace log_level

struct LoggingPrefs : JsonObject {
	WEBDRIVERXX_PROPERTIES_BEGIN(LoggingPrefs)
	WEBDRIVERXX_PROPERTY(Level, "driver", log_level::Value)
	WEBDRIVERXX_PROPERTIES_END()
};

// List of keys and values indicating features that server can or should provide.
struct Capabilities : JsonObject { // copyable
	Capabilities() {}
	explicit Capabilities(const picojson::object& object) : JsonObject(object) {}

	// Hardcoded capabilities are here just to add some sugar.
	// If a capability is not listed below use Get/Set/Has public members.

	WEBDRIVERXX_PROPERTIES_BEGIN(Capabilities)
	WEBDRIVERXX_PROPERTY(BrowserName, "browserName", browser::Value)
	WEBDRIVERXX_PROPERTY(Version, "version", std::string)
	WEBDRIVERXX_PROPERTY(Platform, "platform", platform::Value)

	WEBDRIVERXX_PROPERTY_RONLY(TakesScreenshot, "takesScreenshot", bool)
	WEBDRIVERXX_PROPERTY_RONLY(HandlesAlerts, "handlesAlerts", bool)
	WEBDRIVERXX_PROPERTY_RONLY(CssSelectorsEnabled, "cssSelectorsEnabled", bool)

	WEBDRIVERXX_PROPERTY(JavascriptEnabled, "javascriptEnabled", bool)
	WEBDRIVERXX_PROPERTY(DatabaseEnabled, "databaseEnabled", bool)
	WEBDRIVERXX_PROPERTY(LocationContextEnabled, "locationContextEnabled", bool)
	WEBDRIVERXX_PROPERTY(ApplicationCacheEnabled, "applicationCacheEnabled", bool)
	WEBDRIVERXX_PROPERTY(BrowserConnectionEnabled, "browserConnectionEnabled", bool)
	WEBDRIVERXX_PROPERTY(WebStorageEnabled, "webStorageEnabled", bool)
	WEBDRIVERXX_PROPERTY(AcceptSslCerts, "acceptSslCerts", bool)
	WEBDRIVERXX_PROPERTY(Rotatable, "rotatable", bool)
	WEBDRIVERXX_PROPERTY(NativeEvents, "nativeEvents", bool)
	WEBDRIVERXX_PROPERTY(Proxy, "proxy", Proxy)
	WEBDRIVERXX_PROPERTY(UnexpectedAlertBehaviour, "unexpectedAlertBehaviour", unexpected_alert_behaviour::Value)
	WEBDRIVERXX_PROPERTY(ElementScrollBehavior, "elementScrollBehavior", int)

	WEBDRIVERXX_PROPERTY_RONLY(SessionId, "webdriver.remote.sessionid", std::string)
	WEBDRIVERXX_PROPERTY(QuietExceptions, "webdriver.remote.quietExceptions", bool)
	WEBDRIVERXX_PROPERTIES_END()
};

inline
void CustomFromJson(const picojson::value& value, Capabilities& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Capabilities is not an object");
	result = Capabilities(value.get<picojson::object>());
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/client.h
================================================
#ifndef WEBDRIVERXX_CLIENT_H
#define WEBDRIVERXX_CLIENT_H

#include "session.h"
#include "capabilities.h"
#include "detail/resource.h"
#include "detail/http_connection.h"
#include "picojson.h"
#include <string>
#include <vector>

namespace webdriverxx {

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

// Gives low level access to server's resources. You normally should not use it. 
class Client { // copyable
public:
	explicit Client(const std::string& url = kDefaultWebDriverUrl);
	virtual ~Client() {}

	picojson::object GetStatus() const;

	// Returns existing sessions.
	std::vector<Session> GetSessions() const;

	// Creates new session.
	Session CreateSession(
		const Capabilities& desired,
		const Capabilities& required
		) const;

private:
	Session MakeSession(
		const std::string& id,
		detail::Resource::Ownership mode
		) const;

private:
	detail::Shared<detail::Resource> resource_;
};

} // namespace webdriverxx

#include "client.inl"

#endif


================================================
FILE: include/webdriverxx/client.inl
================================================
#include "conversions.h"
#include "detail/shared.h"
#include "detail/error_handling.h"
#include "detail/types.h"
#include <algorithm>

namespace webdriverxx {

inline
Client::Client(const std::string& url)
	: resource_(new detail::RootResource(
		url,
		detail::Shared<detail::IHttpClient>(new detail::HttpConnection)
		))
{}

inline
picojson::object Client::GetStatus() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	const auto value = resource_->Get("status").get("value");
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Value is not an object");
	return value.get<picojson::object>();
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
std::vector<Session> Client::GetSessions() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	const auto sessions =
		FromJson<std::vector<detail::SessionRef>>(
			resource_->Get("sessions").get("value")
			);
	std::vector<Session> result;
	result.reserve(sessions.size());
	std::transform(sessions.begin(), sessions.end(), std::back_inserter(result),
		[this](const detail::SessionRef& session_ref) {
			return MakeSession(session_ref.id, detail::Resource::IsObserver);
		});
	return result;
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
Session Client::CreateSession(
	const Capabilities& desired,
	const Capabilities& required
	) const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	const auto response = resource_->Post("session",
		JsonObject()
			.Set("desiredCapabilities", static_cast<picojson::value>(desired))
			.Set("requiredCapabilities", static_cast<picojson::value>(required))
			);

	WEBDRIVERXX_CHECK(response.get("sessionId").is<std::string>(), "Session ID is not a string");
	WEBDRIVERXX_CHECK(response.get("value").is<picojson::object>(), "Capabilities is not an object");
	
	const auto sessionId = response.get("sessionId").to_str();
	
	return MakeSession(sessionId, detail::Resource::IsOwner);
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
Session Client::MakeSession(
	const std::string& id,
	detail::Resource::Ownership mode
	) const {
	return Session(detail::MakeSubResource(resource_, "session", id, mode));
}

} // namespace webdriverxx


================================================
FILE: include/webdriverxx/conversions.h
================================================
#ifndef WEBDRIVERXX_CONVERSIONS_H
#define WEBDRIVERXX_CONVERSIONS_H

#include "types.h"
#include "detail/error_handling.h"
#include "detail/meta_tools.h"
#include "picojson.h"
#include <algorithm>

namespace webdriverxx {

template<typename T>
picojson::value ToJson(const T& value);

template<typename T>
T FromJson(const picojson::value& value);

class JsonObject { // copyable
public:
	JsonObject() : value_(picojson::object()) {}
	explicit JsonObject(const picojson::object& object) : value_(object) {}

	template<typename T>
	T Get(const std::string& name) const {
		const auto& map = value_.get<picojson::object>();
		const auto it = map.find(name);
		WEBDRIVERXX_CHECK(it != map.end(), detail::Fmt() << "No \"" << name << "\" in JsonObject");
		return FromJson<T>(it->second);
	}

	template<typename T>
	T GetOptional(const std::string& name, const T& default_value = T()) const {
		const auto& map = value_.get<picojson::object>();
		const auto it = map.find(name);
		return it != map.end() ? FromJson<T>(it->second) : default_value;
	}

	template<typename T>
	JsonObject& Set(const std::string& name, const T& value) {
		value_.get<picojson::object>()[name] = ToJson(value);
		return *this;
	}

	bool Has(const std::string& name) const {
		const auto& map = value_.get<picojson::object>();
		return map.find(name) != map.end();
	}

	operator const picojson::value& () const {
		return value_;
	}

private:
	picojson::value value_;
};

namespace conversions_detail {

struct DefaultTag {};
struct IterableTag {};

using namespace detail;

template<typename T>
struct Tag :
	if_<is_iterable<T>, type_is<IterableTag>,
	type_is<DefaultTag>
	> {};

template<typename T>
picojson::value ToJsonImpl(const T& value, DefaultTag) {
	// Compile error here usually indicates
	// that compiler doesn't know how to convert the type T
	// to the picojson::value. Define CustomToJson
	// function (see examples below) in the T's namespace
	// to resolve the issue.
	return picojson::value(value);
}

template<typename T>
picojson::value ToJsonImpl(const T& value, IterableTag) {
	typedef typename std::iterator_traits<decltype(std::begin(value))>::value_type Item;
	picojson::value result = picojson::value(picojson::array());
	picojson::array& dst = result.get<picojson::array>();
	std::transform(std::begin(value), std::end(value), std::back_inserter(dst), [](const Item& item) {
		return ToJson(item);
	});
	return result;
}

} // conversions_detail

inline
picojson::value CustomToJson(const char* value) {
	return picojson::value(value);
}

inline
picojson::value CustomToJson(const std::string& value) {
	return ToJson(value.c_str());
}

inline
picojson::value CustomToJson(const picojson::value& value) {
	return value;
}

inline
picojson::value CustomToJson(const picojson::object& value) {
	return picojson::value(value);
}

inline
picojson::value CustomToJson(const JsonObject& value) {
	return static_cast<picojson::value>(value);
}

inline
picojson::value CustomToJson(int value) {
	return picojson::value(static_cast<double>(value));
}

template<typename T>
picojson::value CustomToJson(const T& value) {
	using conversions_detail::ToJsonImpl;
	using conversions_detail::Tag;
	return ToJsonImpl(value, typename Tag<T>::type());
}

template<typename T>
picojson::value ToJson(const T& value) {
	return CustomToJson(value);
}

///////////////////////////////////////////////////////////////////

namespace conversions_detail {

template<typename T>
void FromJsonImpl(const picojson::value& value, T& result, DefaultTag) {
	// Compile error here usually indicates
	// that compiler doesn't know how to convert the picojson::value
	// to the type T. Define CustomFromJson function (see examples below)
	// in the T's namespace to resolve the issue.
	result = value.get<T>();
}

template<typename T>
void FromJsonImpl(const picojson::value& value, T& result, IterableTag) {
	WEBDRIVERXX_CHECK(value.is<picojson::array>(), "Value is not an array");
	const picojson::array& array = value.get<picojson::array>();
	typedef typename std::iterator_traits<decltype(std::begin(result))>::value_type Item;
	std::transform(array.begin(), array.end(), std::back_inserter(result), FromJson<Item>);
}

} // conversions_detail

inline
void CustomFromJson(const picojson::value& value, std::string& result) {
	result = value.to_str();
}

inline
void CustomFromJson(const picojson::value& value, bool& result) {
	result = value.evaluate_as_boolean();
}

inline
void CustomFromJson(const picojson::value& value, int& result) {
	WEBDRIVERXX_CHECK(value.is<double>(), "Value is not a number");
	result = static_cast<int>(value.get<double>());
}

inline
void CustomFromJson(const picojson::value& value, unsigned& result) {
	WEBDRIVERXX_CHECK(value.is<double>(), "Value is not a number");
	result = static_cast<unsigned>(value.get<double>());
}

inline
void CustomFromJson(const picojson::value& value, picojson::value& result) {
	result = value;
}

inline
void CustomFromJson(const picojson::value& value, picojson::object& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Value is not an object");
	result = value.get<picojson::object>();
}

inline
void CustomFromJson(const picojson::value& value, JsonObject& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Value is not an object");
	result = JsonObject(value.get<picojson::object>());
}

template<typename T>
void CustomFromJson(const picojson::value& value, T& result) {
	using conversions_detail::FromJsonImpl;
	using conversions_detail::Tag;
	return FromJsonImpl(value, result, typename Tag<T>::type());
}

template<typename T>
T FromJson(const picojson::value& value) {
	T result;
	CustomFromJson(value, result);
	return result;
}

template<typename T>
T OptionalFromJson(const picojson::value& value, const T& default_value = T()) {
	return value.is<picojson::null>() ? default_value : FromJson<T>(value);
}

///////////////////////////////////////////////////////////////////

inline
picojson::value CustomToJson(const Size& size) {
	return JsonObject()
		.Set("width", size.width)
		.Set("height", size.height)
		;
}

inline
void CustomFromJson(const picojson::value& value, Size& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Size is not an object");
	result.width = FromJson<int>(value.get("width"));
	result.height = FromJson<int>(value.get("height"));
}

inline
picojson::value CustomToJson(const Point& position) {
	return JsonObject()
		.Set("x", position.x)
		.Set("y", position.y)
		;
}

inline
void CustomFromJson(const picojson::value& value, Point& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Point is not an object");
	result.x = FromJson<int>(value.get("x"));
	result.y = FromJson<int>(value.get("y"));
}

inline
picojson::value CustomToJson(const Cookie& cookie) {
	JsonObject result;
	result.Set("name", cookie.name);
	result.Set("value", cookie.value);
	if (!cookie.path.empty()) result.Set("path", cookie.path);
	if (!cookie.domain.empty()) result.Set("domain", cookie.domain);
	if (cookie.secure) result.Set("secure", true);
	if (cookie.http_only) result.Set("httpOnly", true);
	if (cookie.expiry != Cookie::NoExpiry) result.Set("expiry", cookie.expiry);
	return result;
}

inline
void CustomFromJson(const picojson::value& value, Cookie& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Cookie is not an object");
	result.name = FromJson<std::string>(value.get("name"));
	result.value = FromJson<std::string>(value.get("value"));
	result.path = OptionalFromJson<std::string>(value.get("path"));
	result.domain = OptionalFromJson<std::string>(value.get("domain"));
	result.secure = OptionalFromJson<bool>(value.get("secure"), false);
	result.http_only = OptionalFromJson<bool>(value.get("httpOnly"), false);
	result.expiry = OptionalFromJson<int>(value.get("expiry"), Cookie::NoExpiry);
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/error_handling.h
================================================
#ifndef WEBDRIVERXX_DETAIL_ERROR_HANDLING_H
#define WEBDRIVERXX_DETAIL_ERROR_HANDLING_H

#include "../errors.h"
#include <string>
#include <sstream>

namespace webdriverxx {
namespace detail {

class Fmt {
public:
	template<typename T>
	Fmt& operator << (const T& value) {
		stream_ << value;
		return *this;
	}

	operator std::string() const {
		return stream_.str();
	}

private:
	std::ostringstream stream_;
};

template<typename T>
bool BoolCast(T value) {
	return !!value;
}

} // namespace detail
} // namespace webdriverxx

#if __cplusplus >= 201103L
	#define WEBDRIVERXX_CURRENT_FUNCTION __func__
#else
	#define WEBDRIVERXX_CURRENT_FUNCTION __FUNCTION__
#endif

#define WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN() \
	try {

#define WEBDRIVERXX_FUNCTION_CONTEXT_END() \
	} catch (const std::exception& e) { \
		throw ::webdriverxx::WebDriverException(std::string(e.what()) \
			+ " called from " + WEBDRIVERXX_CURRENT_FUNCTION); \
	}

#define WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(details) \
	} catch (const std::exception& e) { \
		throw ::webdriverxx::WebDriverException(std::string(e.what()) \
			+ " called from " + WEBDRIVERXX_CURRENT_FUNCTION \
			+ " (" + std::string(details) + ")"); \
	}

#define WEBDRIVERXX_THROW(message) \
	throw ::webdriverxx::WebDriverException(::webdriverxx::detail::Fmt() \
		<< std::string(message) \
		<< " at line " << __LINE__ \
		<< ", file " << __FILE__ \
		)

#define WEBDRIVERXX_CHECK(pred, message) \
	for (;!detail::BoolCast(pred);) \
		WEBDRIVERXX_THROW(message)

#endif


================================================
FILE: include/webdriverxx/detail/factories.h
================================================
#ifndef WEBDRIVERXX_DETAIL_FACTORIES_H
#define WEBDRIVERXX_DETAIL_FACTORIES_H

#include "shared.h"
#include <string>

namespace webdriverxx {

class Element;

namespace detail {

class Finder;
class Resource;

struct IFinderFactory {
	virtual Finder MakeFinder(const Shared<Resource>& context) = 0;
	virtual ~IFinderFactory() {}

};

struct IElementFactory {
	virtual Element MakeElement(const std::string& id) = 0;
	virtual ~IElementFactory() {}
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/factories_impl.h
================================================
#ifndef WEBDRIVERXX_DETAIL_FACTORIES_IMPL_H
#define WEBDRIVERXX_DETAIL_FACTORIES_IMPL_H

#include "factories.h"
#include "resource.h"
#include "finder.h"
#include "shared.h"
#include "../element.h"

namespace webdriverxx {
namespace detail {

class SessionFactory // noncopyable
	: public IElementFactory
	, public IFinderFactory
	, public SharedObjectBase
{
public:
	explicit SessionFactory(const Shared<Resource>& session_resource)
		: session_resource_(session_resource)
	{}

	virtual Element MakeElement(const std::string& id) {
		return Element(
			id,
			detail::MakeSubResource(session_resource_, "element", id),
			Shared<IFinderFactory>(this)
			);
	}

	virtual Finder MakeFinder(const Shared<Resource>& context) {
		return Finder(context, Shared<IElementFactory>(this));
	}

private:
	const Shared<Resource> session_resource_;
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/finder.h
================================================
#ifndef WEBDRIVERXX_DETAIL_FINDER_H
#define WEBDRIVERXX_DETAIL_FINDER_H

#include "shared.h"
#include "resource.h"
#include "factories.h"
#include "../by.h"
#include <vector>

namespace webdriverxx {

class Element;

namespace detail {

class Finder { // copyable
public:
	Finder(
		const Shared<Resource>& context,
		const Shared<IElementFactory>& factory
		);

	Element FindElement(const By& by) const;
	std::vector<Element> FindElements(const By& by) const;

private:
	Shared<Resource> context_;
	Shared<IElementFactory> factory_;
};

} // namespace detail
} // namespace webdriverxx

#include "finder.inl"

#endif


================================================
FILE: include/webdriverxx/detail/finder.inl
================================================
#include "../conversions.h"
#include "../element.h"
#include "types.h"
#include <algorithm>

namespace webdriverxx {
namespace detail {

inline
Finder::Finder(
	const Shared<Resource>& context,
	const Shared<IElementFactory>& factory
	)
	: context_(context)
	, factory_(factory)
{}

inline
Element Finder::FindElement(const By& by) const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	return factory_->MakeElement(FromJson<ElementRef>(
		context_->Post("element", JsonObject()
			.Set("using", by.GetStrategy())
			.Set("value", by.GetValue())
		)).ref);
	WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(Fmt()
		<< "context: " << context_->GetUrl()
		<< ", strategy: " << by.GetStrategy()
		<< ", value: " << by.GetValue()
		)
}

inline
std::vector<Element> Finder::FindElements(const By& by) const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	const auto ids =
		FromJson<std::vector<ElementRef>>(
			context_->Post("elements", JsonObject()
				.Set("using", by.GetStrategy())
				.Set("value", by.GetValue())
			));
	std::vector<Element> result;
	result.reserve(ids.size());
	std::transform(ids.begin(), ids.end(), std::back_inserter(result),
		[this](const ElementRef& element_ref) {
			return factory_->MakeElement(element_ref.ref);
		});
	return result;
	WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(Fmt()
		<< "context: " << context_->GetUrl()
		<< ", strategy: " << by.GetStrategy()
		<< ", value: " << by.GetValue()
		)
}

} // namespace detail
} // namespace webdriverxx


================================================
FILE: include/webdriverxx/detail/http_client.h
================================================
#ifndef WEBDRIVERXX_DETAIL_HTTP_CLIENT_H
#define WEBDRIVERXX_DETAIL_HTTP_CLIENT_H

#include <string>

namespace webdriverxx {
namespace detail {

struct HttpResponse {
	long http_code;
	std::string body;

	HttpResponse()
		: http_code(0)
	{}
};

struct IHttpClient {
	virtual HttpResponse Get(const std::string& url) const = 0;
	virtual HttpResponse Delete(const std::string& url) const = 0;
	virtual HttpResponse Post(const std::string& url, const std::string& data) const = 0;
	virtual ~IHttpClient() {}
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/http_connection.h
================================================
#ifndef WEBDRIVERXX_DETAIL_HTTP_CONNECTION_H
#define WEBDRIVERXX_DETAIL_HTTP_CONNECTION_H

#include "http_client.h"
#include "http_request.h"
#include "error_handling.h"
#include "shared.h"
#include <curl/curl.h>

namespace webdriverxx {
namespace detail {

class HttpConnection // noncopyable
	: public IHttpClient
	, public SharedObjectBase
{
public:
	HttpConnection()
		: connection_(InitCurl())
	{}

	~HttpConnection() {
		curl_easy_cleanup(connection_);
	}

	HttpResponse Get(const std::string& url) const {
		return HttpGetRequest(connection_, url).Execute();
	}
	
	HttpResponse Delete(const std::string& url) const {
		return HttpDeleteRequest(connection_, url).Execute();
	}
	
	HttpResponse Post(
		const std::string& url,
		const std::string& upload_data
		) const {
		return HttpPostRequest(connection_, url, upload_data).Execute();
	}

private:
	static
	CURL* InitCurl() {
		CURL *const result = curl_easy_init();
		WEBDRIVERXX_CHECK(result, "Cannot initialize CURL");
		return result;
	}

private:
	CURL *const connection_;
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/http_request.h
================================================
#ifndef WEBDRIVERXX_DETAIL_HTTP_REQUEST_H
#define WEBDRIVERXX_DETAIL_HTTP_REQUEST_H

#include "error_handling.h"
#include <curl/curl.h>
#include <string>
#include <algorithm>

namespace webdriverxx {
namespace detail {

const char *const kContentTypeJson = "application/json;charset=UTF-8";

class HttpHeaders {
public:
	HttpHeaders() : head_(nullptr) {}

	~HttpHeaders() {
		curl_slist_free_all(head_);
	}

	void Add(const std::string& name, const std::string& value) {
		head_ = curl_slist_append(head_, (name + ": " + value).c_str());
		WEBDRIVERXX_CHECK(head_, "Cannot add HTTP header");
	}

	curl_slist* Get() const {
		return head_;
	}

private:
	curl_slist* head_;
};

class HttpRequest {
public:
	HttpRequest(
		CURL* http_connection,
		const std::string& url
		)
		: http_connection_(http_connection)
		, url_(url)
	{}

	virtual ~HttpRequest() {}

	HttpResponse Execute() {
		curl_easy_reset(http_connection_);
		SetOption(CURLOPT_URL, url_.c_str());
		HttpResponse response;
		SetOption(CURLOPT_WRITEFUNCTION, &WriteCallback);
		SetOption(CURLOPT_WRITEDATA, &response.body);
		char error_message[CURL_ERROR_SIZE];
  		SetOption(CURLOPT_ERRORBUFFER, &error_message);
		AddHeader("Accept", kContentTypeJson);
		
		SetCustomRequestOptions();
		
		SetOption(CURLOPT_HTTPHEADER, headers_.Get());

		const CURLcode result = curl_easy_perform(http_connection_);
		WEBDRIVERXX_CHECK(result == CURLE_OK, Fmt()
			<< "Cannot perform HTTP request ("
			<< "result: " << result
			<< ", message: " << error_message
			<< ")"
			);

		response.http_code = GetHttpCode();
		return response;
	}

protected:
	virtual void SetCustomRequestOptions() {}

	template<typename T>
	void SetOption(CURLoption option, const T& value) const {
		const auto result = curl_easy_setopt(http_connection_, option, value);
		WEBDRIVERXX_CHECK(result == CURLE_OK, Fmt()
			<< "Cannot set HTTP session option ("
			<< "option: " << option
			<< ", message: \"" << curl_easy_strerror(result) << "\""
			<< ")"
			);
	}

	void AddHeader(const std::string& name, const std::string& value) {
		headers_.Add(name, value);
	}

private:
	long GetHttpCode() const {
		long http_code = 0;
		const auto result = curl_easy_getinfo(http_connection_, CURLINFO_RESPONSE_CODE, &http_code);
		WEBDRIVERXX_CHECK(result == CURLE_OK, Fmt()
			<< "Cannot get HTTP code (" << curl_easy_strerror(result) << ")"
			);
		return http_code;
	}

	static
	size_t WriteCallback(void* buffer, size_t size, size_t nmemb, void* userdata) {
		std::string* data_received = reinterpret_cast<std::string*>(userdata);
		const auto buffer_size = size * nmemb;
		data_received->append(reinterpret_cast<const char*>(buffer), buffer_size);
		return buffer_size;
	}

private:
	HttpRequest(HttpRequest&);
	HttpRequest& operator=(HttpRequest&);

private:
	CURL *const http_connection_;
	const std::string url_;
	HttpHeaders headers_;
};

typedef HttpRequest HttpGetRequest;

class HttpDeleteRequest : public HttpRequest {
public:
	HttpDeleteRequest(CURL* http_connection, const std::string& url)
		: HttpRequest(http_connection, url)
	{}

private:
	void SetCustomRequestOptions() {
		SetOption(CURLOPT_CUSTOMREQUEST, "DELETE");
	}
};

class HttpPostRequest : public HttpRequest {
public:
	HttpPostRequest(
		CURL* http_connection,
		const std::string& url,
		const std::string& upload_data
		)
		: HttpRequest(http_connection, url)
		, upload_data_(upload_data)
		, unsent_ptr_(upload_data.c_str())
		, unsent_length_(upload_data.size())
	{}

protected:
	void SetCustomRequestOptions() {
		SetOption(CURLOPT_POST, 1L);
		SetOption(CURLOPT_POSTFIELDSIZE, upload_data_.length());
		AddHeader("Content-Type", kContentTypeJson);
		SetOption(CURLOPT_READFUNCTION, ReadCallback);
		SetOption(CURLOPT_READDATA, this);
	}

private:
	static
	size_t ReadCallback(void* buffer, size_t size, size_t nmemb, void* userdata) {
		HttpPostRequest* that = reinterpret_cast<HttpPostRequest*>(userdata);
		auto buffer_size = size * nmemb;
		auto copy_size = that->unsent_length_ < buffer_size ? that->unsent_length_ : buffer_size;
		std::copy(that->unsent_ptr_, that->unsent_ptr_ + copy_size,
			reinterpret_cast<char*>(buffer));
		that->unsent_length_ -= copy_size;
		that->unsent_ptr_ += copy_size;
		return copy_size;
	}

private:
	const std::string& upload_data_;
	const char* unsent_ptr_;
	size_t unsent_length_;
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/keyboard.h
================================================
#ifndef WEBDRIVERXX_DETAIL_KEYBOARD_H
#define WEBDRIVERXX_DETAIL_KEYBOARD_H

#include "resource.h"
#include "../conversions.h"
#include "../keys.h"

namespace webdriverxx {
namespace detail {

class Keyboard { // copyable
public:
	Keyboard(const Shared<Resource>& resource, const std::string& command)
		: resource_(resource)
		, command_(command)
	{}

	const Keyboard& SendKeys(const std::string& keys) const {
		return SendKeys(Shortcut() << keys);
	}

	const Keyboard& SendKeys(const Shortcut& shortcut) const {
		resource_->Post(command_, JsonObject()
			.Set("value", ToJson(shortcut.keys_)));
		return *this;
	}

private:
	Shared<Resource> resource_;
	std::string command_;
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/meta_tools.h
================================================
#ifndef WEBDRIVERXX_DETAIL_META_TOOLS_H
#define WEBDRIVERXX_DETAIL_META_TOOLS_H

#include <type_traits>
#include <utility>
#include <iterator>
#include <string>

namespace webdriverxx {
namespace detail {

template<class T>
struct type_is { typedef T type; };

template<class T>
T& value_ref(); // MSVC2010 doesn't have std::declval

template<class T>
class is_iterable {
	template<class U>
	static std::false_type test(...);
	template<class U>
	static decltype(std::begin(value_ref<U>()), std::end(value_ref<U>()),
		std::true_type()) test(int);

	typedef decltype(test<T>(0)) verdict;

public:
	static const bool value = verdict::value;
};

template<class T>
struct is_string : std::is_convertible<const T&, std::string> {};

template<class C, class T1, class T2>
struct if_ : std::conditional<C::value, typename T1::type, typename T2::type> {};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/resource.h
================================================
#ifndef WEBDRIVERXX_DETAIL_RESOURCE_H
#define WEBDRIVERXX_DETAIL_RESOURCE_H

#include "error_handling.h"
#include "http_client.h"
#include "shared.h"
#include "../conversions.h"
#include "../response_status_code.h"
#include "../picojson.h"

namespace webdriverxx {
namespace detail {

class Resource : public SharedObjectBase { // noncopyable
public:
	enum Ownership { IsOwner, IsObserver };

	Resource(
		const std::string& url,
		const Shared<IHttpClient>& http_client,
		Ownership mode = IsObserver
		)
		: http_client_(http_client)
		, url_(url)
		, ownership_(mode)
	{}

	Resource(
		const Shared<Resource>& parent,
		const std::string& name,
		Ownership mode = IsObserver
		)
		: http_client_(parent->http_client_)
		, parent_(parent)
		, url_(ConcatUrl(parent->url_, name))
		, ownership_(mode)
	{}

	virtual ~Resource() {
		try {
			if (ownership_ == IsOwner)
				DeleteResource();
		} catch (const std::exception&) {}
	}

	const std::string& GetUrl() const {
		return url_;
	}

	picojson::value Get(const std::string& command = std::string()) const {
		return Download(command, &IHttpClient::Get, "GET");
	}

	template<typename T>
	T GetValue(const std::string& command) const {
		WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
		return FromJson<T>(Get(command));
		WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(detail::Fmt() <<
			"command: " << command
			)
	}

	std::string GetString(const std::string& command) const {
		return GetValue<std::string>(command);
	}

	bool GetBool(const std::string& command) const {
		return GetValue<bool>(command);
	}

	picojson::value Delete(const std::string& command = std::string()) const {
		return Download(command, &IHttpClient::Delete, "DELETE");
	}

	picojson::value Post(
		const std::string& command = std::string(),
		const picojson::value& upload_data = picojson::value()
		) const {
		return Upload(command, upload_data, &IHttpClient::Post, "POST");
	}

	template<typename T>
	void Post(
		const std::string& command,
		const std::string& arg_name,
		const T& arg_value
		) const {
		Post(command, JsonObject().Set(arg_name, arg_value));
	}	

	template<typename T>
	void PostValue(const std::string& command, const T& value) const {
		WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
		Post(command, ToJson(value));
		WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(detail::Fmt() <<
			"command: " << command
			)
	}	

protected:
	virtual picojson::value TransformResponse(picojson::value& response) const {
		picojson::value result;
		response.get("value").swap(result);
		return result;
	}

	virtual void DeleteResource() {
		Delete();
	}

private:
	picojson::value Download(
		const std::string& command, 
		HttpResponse (IHttpClient::* member)(const std::string& url) const,
		const char* request_type
		) const {
		WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
		return ProcessResponse((http_client_->*member)(
			ConcatUrl(url_, command)
			));
		WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(Fmt()
			<< "request: " << request_type
			<< ", command: " << command
			<< ", resource: " << url_
			)
	}

	static std::string ToUploadData(const picojson::value& upload_data)
	{
		return upload_data.is<picojson::null>() ?
			std::string() : upload_data.serialize();
	}

	picojson::value Upload(
		const std::string& command, 
		const picojson::value& upload_data,
		HttpResponse (IHttpClient::* member)(const std::string& url, const std::string& upload_data) const,
		const char* request_type
		) const {
		WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
		return ProcessResponse((http_client_->*member)(
			ConcatUrl(url_, command),
			ToUploadData(upload_data)
			));
		WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(Fmt()
			<< "request: " << request_type
			<< ", command: " << command
			<< ", resource: " << url_
			<< ", data: " << ToUploadData(upload_data)
			)
	}

	picojson::value ProcessResponse(
		const HttpResponse& http_response
		) const {
		WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
		WEBDRIVERXX_CHECK(
			http_response.http_code / 100 != 4 &&
			http_response.http_code != 501,
			"HTTP code indicates that request is invalid");

		picojson::value response;
		std::string error_message;
		picojson::parse(response, http_response.body.begin(), http_response.body.end(), &error_message);

		WEBDRIVERXX_CHECK(error_message.empty(),
			Fmt() << "JSON parser error (" << error_message << ")"
			);

		WEBDRIVERXX_CHECK(response.is<picojson::object>(), "Server response is not an object");
		WEBDRIVERXX_CHECK(response.contains("status"), "Server response has no member \"status\"");
		WEBDRIVERXX_CHECK(response.get("status").is<double>(), "Response status code is not a number");
		const auto status =
			static_cast<response_status_code::Value>(static_cast<int>(response.get("status").get<double>()));
		WEBDRIVERXX_CHECK(response.contains("value"), "Server response has no member \"value\"");
		const auto& value = response.get("value");

		if (http_response.http_code == 500) { // Internal server error
			WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Server returned HTTP code 500 and \"response.value\" is not an object");
			WEBDRIVERXX_CHECK(value.contains("message"), "Server response has no member \"value.message\"");
			WEBDRIVERXX_CHECK(value.get("message").is<std::string>(), "\"value.message\" is not a string");
			WEBDRIVERXX_THROW(Fmt() << "Server failed to execute command ("
				<< "message: " << value.get("message").to_str()
				<< ", status: " << response_status_code::ToString(status)
				<< ", status_code: " << status
				<< ")"
				);
		}
		WEBDRIVERXX_CHECK(status == response_status_code::kSuccess, "Non-zero response status code");
		WEBDRIVERXX_CHECK(http_response.http_code == 200, "Unsupported HTTP code");

		return TransformResponse(response);
		WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(Fmt()
			<< "HTTP code: " << http_response.http_code
			<< ", body: " << http_response.body
			)
	}

	static
	std::string ConcatUrl(const std::string& a, const std::string& b, const char delim = '/') {
		auto result = a.empty() ? b : a;
		if (!a.empty() && !b.empty()) {
			if (result[result.length()-1] != delim)
				result += delim;
			result.append(b[0] == delim ? b.begin() + 1 : b.begin(), b.end());
		}
		return result;
	}

private:
	const Shared<IHttpClient> http_client_;
	const Shared<Resource> parent_;
	const std::string url_;
	const Ownership ownership_;
};

class RootResource : public Resource { // noncopyable
public:
	RootResource(
		const std::string& url,
		const Shared<IHttpClient>& http_client
		)
		: Resource(url, http_client, IsObserver)
	{}

private:
	virtual picojson::value TransformResponse(picojson::value& response) const {
		picojson::value result;
		response.swap(result);
		return result;
	}
};

inline
Shared<Resource> MakeSubResource(
	const Shared<Resource>& parent,
	const std::string& subpath,
	Resource::Ownership mode = Resource::IsObserver
	) {
	return Shared<Resource>(new Resource(parent, subpath, mode));
}

inline
Shared<Resource> MakeSubResource(
	const Shared<Resource>& parent,
	const std::string& subpath1,
	const std::string& subpath2,
	Resource::Ownership mode = Resource::IsObserver
	) {
	return Shared<Resource>(new Resource(parent, subpath1 + "/" + subpath2, mode));
}

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/shared.h
================================================
#ifndef WEBDRIVERXX_DETAIL_SHARED_H
#define WEBDRIVERXX_DETAIL_SHARED_H

#include <algorithm>

namespace webdriverxx {
namespace detail {

class SharedObjectBase { // noncopyable
public:
	SharedObjectBase() : ref_(0) {}
	virtual ~SharedObjectBase() {}

	virtual void AddRef() {
		++ref_;
	}

	virtual void Release() {
		if (--ref_ == 0)
			delete this;
	}

private:
	SharedObjectBase(SharedObjectBase&);
	SharedObjectBase& operator = (SharedObjectBase&);

private:
	unsigned ref_;
};

// Copyable, not thread safe
template<typename T>
class Shared {
public:
	Shared()
		: ptr_(nullptr)
		, ref_(nullptr)
	{}

	template<typename T2>
	Shared(T2* ptr)
		: ptr_(ptr)
		, ref_(ptr)
	{
		if (ref_) ref_->AddRef();
	}

	Shared(const Shared& other)
		: ptr_(other.ptr_)
		, ref_(other.ref_)
	{
		if (ref_) ref_->AddRef();
	}

	template<typename T2>
	Shared(const Shared<T2>& other) 
		: ptr_(other.ptr_)
		, ref_(other.ref_)
	{
		if (ref_) ref_->AddRef();
	}

	~Shared() {
		if (ref_) ref_->Release();
	}

	Shared& operator = (const Shared& other) {
		if (&other != this)
			Shared<T>(other).Swap(*this);
		return *this;
	}

	void Swap(Shared& other) {
		std::swap(ptr_, other.ptr_);
		std::swap(ref_, other.ref_);
	}

	T& operator * () const {
		return *ptr_;
	}

	T* operator -> () const {
		return ptr_;
	}

	T* Get() const {
		return ptr_;
	}

	operator T* () const {
		return ptr_;
	}

private:
	template<typename T2>
	friend class Shared;

	T* ptr_;
	SharedObjectBase* ref_;
};

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/time.h
================================================
#ifndef WEBDRIVERXX_DETAIL_TIME_H
#define WEBDRIVERXX_DETAIL_TIME_H

#include "error_handling.h"
#include "../types.h"

#ifdef _WIN32

#include <windows.h>

#else

#include <time.h>
#include <sys/time.h>

#endif

namespace webdriverxx {
namespace detail {

inline
TimePoint Now() {
	#ifdef _WIN32
		FILETIME time;
		::GetSystemTimeAsFileTime(&time);
		return ((static_cast<TimePoint>(time.dwHighDateTime) << 32)
			+ time.dwLowDateTime)/10000;
	#else
		timeval time = {};
		WEBDRIVERXX_CHECK(0 == gettimeofday(&time, nullptr), "gettimeofday failure");
		return static_cast<TimePoint>(time.tv_sec)*1000 + time.tv_usec/1000;
	#endif
}

inline
void Sleep(Duration milliseconds) {
	#ifdef _WIN32
		::Sleep(static_cast<DWORD>(milliseconds));
	#else
		timespec time = { static_cast<time_t>(milliseconds/1000),
			static_cast<long>(milliseconds%1000)*1000000 };
		while (nanosleep(&time, &time)) {}
	#endif
}

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/to_string.h
================================================
#ifndef WEBDRIVERXX_TO_STRING_H
#define WEBDRIVERXX_TO_STRING_H

#include "meta_tools.h"
#include <string>
#include <sstream>

namespace webdriverxx {
namespace detail {

template<typename T>
void ToStream(const T& value, std::ostream& stream);

namespace to_string_impl {

template<typename T>
void WriteNonStreamableValue(const T&, std::ostream& stream) {
	stream << "<non-printable>";
}

} // namespace to_string_impl
} // detail
} // webdriverxx

namespace webdriverxx_to_string_impl {

template<typename T>
std::ostream& operator << (std::ostream& stream, const T& value) {
	webdriverxx::detail::to_string_impl::WriteNonStreamableValue(value, stream);
	return stream;
}

} // namespace webdriverxx_to_string_impl

namespace webdriverxx {
namespace detail {
namespace to_string_impl {

struct DefaultTag {};
struct IterableTag {};
struct StringTag {};

template<typename T>
struct Tag :
	if_<is_string<T>, type_is<StringTag>,
	if_<is_iterable<T>, type_is<IterableTag>,
	type_is<DefaultTag>
	>> {};

template<typename T>
void ToStreamImpl(const T& value, std::ostream& stream, DefaultTag) {
	using namespace webdriverxx_to_string_impl;
	stream << value;
}

template<typename T>
void ToStreamImpl(T* value, std::ostream& stream, DefaultTag) {
	stream << '*';
	ToStream(*value, stream);
}

inline
void ToStreamImpl(const char value, std::ostream& stream, DefaultTag) {
	stream << "'" << value << "'";
}

inline
void ToStreamImpl(const char* value, std::ostream& stream, StringTag) {
	stream << '"' << value << '"';
}

inline
void ToStreamImpl(const std::string& value, std::ostream& stream, StringTag) {
	ToStream(value.c_str(), stream);
}

template<typename T>
void ToStreamImpl(const T& value, std::ostream& stream, IterableTag) {
	auto it = std::begin(value);
	const auto end = std::end(value);
	int limit = 20;
	stream << "[";
	if (it != end) {
		ToStream(*it, stream);
		while (++it != end && --limit > 0) {
			stream << ", ";
			ToStream(*it, stream);
		}
	}
	stream << "]";
}

} // namespace to_string_impl

template<typename T>
void PrintTo(const T& value, std::ostream* stream) {
	using to_string_impl::ToStreamImpl;
	using to_string_impl::Tag;
	ToStreamImpl(value, *stream, typename Tag<T>::type());
}

template<typename T>
void ToStream(const T& value, std::ostream& stream)
{
	PrintTo(value, &stream); // for compatibility with Google Test's values printers
}

template<typename T>
std::string ToString(const T& value) {
	std::ostringstream s;
	ToStream(value, s);
	return s.str();
}

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/detail/types.h
================================================
#ifndef WEBDRIVERXX_DETAIL_TYPES_H
#define WEBDRIVERXX_DETAIL_TYPES_H

#include "../conversions.h"
#include "../capabilities.h"
#include "../picojson.h"
#include <string>

namespace webdriverxx {
namespace detail {

struct SessionRef {
	std::string id;
	Capabilities capabilities;
};

struct ElementRef {
	std::string ref;
};

inline
picojson::value CustomToJson(const ElementRef& ref) {
	return JsonObject()
		.Set("ELEMENT", ref.ref)
		;
}

inline
void CustomFromJson(const picojson::value& value, ElementRef& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "ElementRef is not an object");
	result.ref = FromJson<std::string>(value.get("ELEMENT"));
	if (result.ref == "null") {
		std::stringstream element(value.serialize());
		std::string segment;
		std::vector<std::string> strArr;
		while (std::getline(element, segment, ':')) {
			segment.erase(std::remove(segment.begin(), segment.end(), '{'), segment.end());
			segment.erase(std::remove(segment.begin(), segment.end(), '"'), segment.end());
			segment.erase(std::remove(segment.begin(), segment.end(), '}'), segment.end());
			strArr.push_back(segment);
		}
		if (strArr.size() >= 1) {
			std::stringstream ss;
			ss << "{\"ELEMENT\":" << "\"" << strArr[1] << "\"}";
			picojson::value jSon;
			picojson::parse(jSon, ss);
			result.ref = FromJson<std::string>(jSon.get("ELEMENT"));
		}
	}
}

inline
void CustomFromJson(const picojson::value& value, SessionRef& result) {
	WEBDRIVERXX_CHECK(value.is<picojson::object>(), "Session information is not an object");
	result.id = value.get("sessionId").to_str();
	if (value.get("capabilities").is<picojson::object>())
		result.capabilities = Capabilities(value.get("capabilities").get<picojson::object>());
}

} // namespace detail
} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/element.h
================================================
#ifndef WEBDRIVERXX_ELEMENT_H
#define WEBDRIVERXX_ELEMENT_H

#include "by.h"
#include "types.h"
#include "keys.h"
#include "detail/shared.h"
#include "detail/keyboard.h"
#include "detail/resource.h"
#include "detail/factories.h"
#include <string>
#include <vector>

namespace webdriverxx {

// An element from DOM
class Element { // copyable
public:
	Element();
	
	Element(
		const std::string& ref,
		const detail::Shared<detail::Resource>& resource,
		const detail::Shared<detail::IFinderFactory>& factory
		);

	std::string GetRef() const; // Returns ID that is used by Webdriver to identify elements

	bool IsDisplayed() const;
	bool IsEnabled() const;
	bool IsSelected() const;
	Point GetLocation() const;
	Point GetLocationInView() const;
	Size GetSize() const;
	std::string GetAttribute(const std::string& name) const;
	std::string GetCssProperty(const std::string& name) const;
	std::string GetTagName() const;
	std::string GetText() const;

	Element FindElement(const By& by) const;
	std::vector<Element> FindElements(const By& by) const;

	const Element& Clear() const;
	const Element& Click() const;
	const Element& Submit() const;

	const Element& SendKeys(const std::string& keys) const;
	const Element& SendKeys(const Shortcut& shortcut) const;

	bool Equals(const Element& other) const;
	bool operator != (const Element& other) const;
	bool operator == (const Element& other) const;
	bool operator < (const Element& other) const;

private:
	detail::Resource& GetResource() const;
	detail::Keyboard GetKeyboard() const;

private:
	std::string ref_;
	detail::Shared<detail::Resource> resource_;
	detail::Shared<detail::IFinderFactory> factory_;
};

} // namespace webdriverxx

#include "element.inl"

#endif


================================================
FILE: include/webdriverxx/element.inl
================================================
#include "conversions.h"
#include "detail/finder.h"
#include "detail/error_handling.h"

namespace webdriverxx {

inline
picojson::value CustomToJson(const Element& element) {
	detail::ElementRef ref = { element.GetRef() };
	return ToJson(ref);
}

inline
Element::Element() {}

inline
Element::Element(
	const std::string& ref,
	const detail::Shared<detail::Resource>& resource,
	const detail::Shared<detail::IFinderFactory>& factory
	)
	: ref_(ref)
	, resource_(resource)
	, factory_(factory)
{}

inline
std::string Element::GetRef() const {
	return ref_;
}

inline
bool Element::IsDisplayed() const {
	return GetResource().GetBool("displayed");
}

inline
bool Element::IsEnabled() const {
	return GetResource().GetBool("enabled");
}

inline
bool Element::IsSelected() const {
	return GetResource().GetBool("selected");
}

inline
Point Element::GetLocation() const {
	return GetResource().GetValue<Point>("location");
}

inline
Point Element::GetLocationInView() const {
	return GetResource().GetValue<Point>("location_in_view");
}

inline
Size Element::GetSize() const {
	return GetResource().GetValue<Size>("size");
}

inline
std::string Element::GetAttribute(const std::string& name) const {
	return GetResource().GetString(std::string("attribute/") + name);
}

inline
std::string Element::GetCssProperty(const std::string& name) const {
	return GetResource().GetString(std::string("css/") + name);
}

inline
std::string Element::GetTagName() const {
	return GetResource().GetString("name");
}
inline
std::string Element::GetText() const {
	return GetResource().GetString("text");
}

inline
Element Element::FindElement(const By& by) const {
	return factory_->MakeFinder(&GetResource()).FindElement(by);
}

inline
std::vector<Element> Element::FindElements(const By& by) const {
	return factory_->MakeFinder(&GetResource()).FindElements(by);
}

inline
const Element& Element::Clear() const {
	GetResource().Post("clear");
	return *this;
}

inline
const Element& Element::Click() const {
	GetResource().Post("click");
	return *this;
}

inline
const Element& Element::Submit() const {
	GetResource().Post("submit");
	return *this;
}

inline
const Element& Element::SendKeys(const std::string& keys) const {
	GetKeyboard().SendKeys(keys);
	return *this;
}

inline
const Element& Element::SendKeys(const Shortcut& shortcut) const {
	GetKeyboard().SendKeys(shortcut);
	return *this;
}

inline
bool Element::Equals(const Element& other) const {
	return GetResource().GetBool(std::string("equals/") + other.ref_);
}

inline
bool Element::operator != (const Element& other) const {
	return ref_ != other.ref_;
}

inline
bool Element::operator == (const Element& other) const {
	return ref_ == other.ref_;
}

inline
bool Element::operator < (const Element& other) const {
	return ref_ < other.ref_;
}

inline
detail::Resource& Element::GetResource() const {
	WEBDRIVERXX_CHECK(resource_, "Attempt to use empty element");
	return *resource_;
}

inline
detail::Keyboard Element::GetKeyboard() const
{
	return detail::Keyboard(&GetResource(), "value");
}

} // namespace webdriverxx


================================================
FILE: include/webdriverxx/errors.h
================================================
#ifndef WEBDRIVERXX_ERRORS_H
#define WEBDRIVERXX_ERRORS_H

#include <stdexcept>
#include <string>

namespace webdriverxx {

struct WebDriverException : std::runtime_error {
	explicit WebDriverException(const std::string& message)
		: std::runtime_error(message) {}
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/js_args.h
================================================
#ifndef WEBDRIVERXX_JS_ARGS_H
#define WEBDRIVERXX_JS_ARGS_H 

#include "conversions.h"
#include "picojson.h"

namespace webdriverxx {

class Session;

class JsArgs // copyable
{
public:
	JsArgs() : args_(picojson::array()) {}

	// This member works out of the box for scalar values, Elements and std::vector
	// with scalar values or Elements. Additional code is needed to use it
	// with custom objects and custom containers. Take a look
	// to the TestJsExecutor/CanPassArray, TestJsExecutor/CanPassCustomArray
	// and TestJsExecutor/CanPassCustomObjects tests to get details.
	template<typename T>
	JsArgs& operator << (const T& arg) {
		args_.get<picojson::array>().push_back(ToJson(arg));
		return *this;
	}

	// Alternative backdoor for passing custom data structures as arguments.
	JsArgs& operator << (const picojson::value& arg) {
		args_.get<picojson::array>().push_back(arg);
		return *this;
	}

private:
	friend class Session;
	picojson::value args_;
};

} // namespace webdriverxx 

#endif


================================================
FILE: include/webdriverxx/keys.h
================================================
#ifndef WEBDRIVERXX_KEYS_H
#define WEBDRIVERXX_KEYS_H 

#include <string>
#include <vector>

namespace webdriverxx {
namespace keys { 

const char *const Null = "\xee\x80\x80";
const char *const Cancel = "\xee\x80\x81";
const char *const Help = "\xee\x80\x82";
const char *const Backspace = "\xee\x80\x83";
const char *const Tab = "\xee\x80\x84";
const char *const Clear = "\xee\x80\x85";
const char *const Return = "\xee\x80\x86";
const char *const Enter = "\xee\x80\x87";
const char *const Shift = "\xee\x80\x88";
const char *const Control = "\xee\x80\x89";
const char *const Alt = "\xee\x80\x8a";
const char *const Pause = "\xee\x80\x8b";
const char *const Escape = "\xee\x80\x8c";
const char *const Space = "\xee\x80\x8d";
const char *const PageUp = "\xee\x80\x8e";
const char *const PageDown = "\xee\x80\x8f";
const char *const End = "\xee\x80\x90";
const char *const Home = "\xee\x80\x91";
const char *const Left = "\xee\x80\x92";
const char *const Up = "\xee\x80\x93";
const char *const Right = "\xee\x80\x94";
const char *const Down = "\xee\x80\x95";
const char *const Insert = "\xee\x80\x96";
const char *const Delete = "\xee\x80\x97";
const char *const Semicolon = "\xee\x80\x98";
const char *const Equals = "\xee\x80\x99";
const char *const Numpad0 = "\xee\x80\x9a";
const char *const Numpad1 = "\xee\x80\x9b";
const char *const Numpad2 = "\xee\x80\x9c";
const char *const Numpad3 = "\xee\x80\x9d";
const char *const Numpad4 = "\xee\x80\x9e";
const char *const Numpad5 = "\xee\x80\x9f";
const char *const Numpad6 = "\xee\x80\xa0";
const char *const Numpad7 = "\xee\x80\xa1";
const char *const Numpad8 = "\xee\x80\xa2";
const char *const Numpad9 = "\xee\x80\xa3";
const char *const Multiply = "\xee\x80\xa4";
const char *const Add = "\xee\x80\xa5";
const char *const Separator = "\xee\x80\xa6";
const char *const Subtract = "\xee\x80\xa7";
const char *const Decimal = "\xee\x80\xa8";
const char *const Divide = "\xee\x80\xa9";
const char *const F1 = "\xee\x80\xb1";
const char *const F2 = "\xee\x80\xb2";
const char *const F3 = "\xee\x80\xb3";
const char *const F4 = "\xee\x80\xb4";
const char *const F5 = "\xee\x80\xb5";
const char *const F6 = "\xee\x80\xb6";
const char *const F7 = "\xee\x80\xb7";
const char *const F8 = "\xee\x80\xb8";
const char *const F9 = "\xee\x80\xb9";
const char *const F10 = "\xee\x80\xba";
const char *const F11 = "\xee\x80\xbb";
const char *const F12 = "\xee\x80\xbc";
const char *const Command = "\xee\x80\xbd";
const char *const Meta = Command;

} // namespace keys 

namespace detail {
class Keyboard;
} // namespace detail

class Shortcut // copyable
{
public:
	Shortcut& operator << (const std::string& key) {
		keys_.push_back(key);
		return *this;
	}

	Shortcut& operator << (const char* key) {
		keys_.push_back(key);
		return *this;
	}

private:
	friend class detail::Keyboard;
	std::vector<std::string> keys_;
};

} // namespace webdriverxx 

#endif


================================================
FILE: include/webdriverxx/picojson.h
================================================
/*
 * Copyright 2009-2010 Cybozu Labs, Inc.
 * Copyright 2011-2014 Kazuho Oku
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef picojson_h
#define picojson_h

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>

// for isnan/isinf
#if __cplusplus>=201103L
# include <cmath>
#else
extern "C" {
# ifdef _MSC_VER
#  include <float.h>
# elif defined(__INTEL_COMPILER)
#  include <mathimf.h>
# else
#  include <math.h>
# endif
}
#endif

// experimental support for int64_t (see README.mkdn for detail)
#ifdef PICOJSON_USE_INT64
# define __STDC_FORMAT_MACROS
# include <errno.h>
# include <inttypes.h>
#endif

// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
#ifndef PICOJSON_USE_LOCALE
# define PICOJSON_USE_LOCALE 1
#endif
#if PICOJSON_USE_LOCALE
extern "C" {
# include <locale.h>
}
#endif

#ifndef PICOJSON_ASSERT
# define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0)
#endif

#ifdef _MSC_VER
    #define SNPRINTF _snprintf_s
    #pragma warning(push)
    #pragma warning(disable : 4244) // conversion from int to char
    #pragma warning(disable : 4127) // conditional expression is constant
    #pragma warning(disable : 4702) // unreachable code
#else
    #define SNPRINTF snprintf
#endif

namespace picojson {
  
  enum {
    null_type,
    boolean_type,
    number_type,
    string_type,
    array_type,
    object_type
#ifdef PICOJSON_USE_INT64
    , int64_type
#endif
  };
  
  enum {
    INDENT_WIDTH = 2
  };

  struct null {};
  
  class value {
  public:
    typedef std::vector<value> array;
    typedef std::map<std::string, value> object;
    union _storage {
      bool boolean_;
      double number_;
#ifdef PICOJSON_USE_INT64
      int64_t int64_;
#endif
      std::string* string_;
      array* array_;
      object* object_;
    };
  protected:
    int type_;
    _storage u_;
  public:
    value();
    value(int type, bool);
    explicit value(bool b);
#ifdef PICOJSON_USE_INT64
    explicit value(int64_t i);
#endif
    explicit value(double n);
    explicit value(const std::string& s);
    explicit value(const array& a);
    explicit value(const object& o);
    explicit value(const char* s);
    value(const char* s, size_t len);
    ~value();
    value(const value& x);
    value& operator=(const value& x);
    void swap(value& x);
    template <typename T> bool is() const;
    template <typename T> const T& get() const;
    template <typename T> T& get();
    bool evaluate_as_boolean() const;
    const value& get(size_t idx) const;
    const value& get(const std::string& key) const;
    value& get(size_t idx);
    value& get(const std::string& key);

    bool contains(size_t idx) const;
    bool contains(const std::string& key) const;
    std::string to_str() const;
    template <typename Iter> void serialize(Iter os, bool prettify = false) const;
    std::string serialize(bool prettify = false) const;
  private:
    template <typename T> value(const T*); // intentionally defined to block implicit conversion of pointer to bool
    template <typename Iter> static void _indent(Iter os, int indent);
    template <typename Iter> void _serialize(Iter os, int indent) const;
    std::string _serialize(int indent) const;
  };
  
  typedef value::array array;
  typedef value::object object;
  
  inline value::value() : type_(null_type) {}
  
  inline value::value(int type, bool) : type_(type) {
    switch (type) {
#define INIT(p, v) case p##type: u_.p = v; break
      INIT(boolean_, false);
      INIT(number_, 0.0);
#ifdef PICOJSON_USE_INT64
      INIT(int64_, 0);
#endif
      INIT(string_, new std::string());
      INIT(array_, new array());
      INIT(object_, new object());
#undef INIT
    default: break;
    }
  }
  
  inline value::value(bool b) : type_(boolean_type) {
    u_.boolean_ = b;
  }

#ifdef PICOJSON_USE_INT64
  inline value::value(int64_t i) : type_(int64_type) {
    u_.int64_ = i;
  }
#endif

  inline value::value(double n) : type_(number_type) {
    if (
#ifdef _MSC_VER
        ! _finite(n)
#elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf))
		std::isnan(n) || std::isinf(n)
#else
        isnan(n) || isinf(n)
#endif
        ) {
      throw std::overflow_error("");
    }
    u_.number_ = n;
  }
  
  inline value::value(const std::string& s) : type_(string_type) {
    u_.string_ = new std::string(s);
  }
  
  inline value::value(const array& a) : type_(array_type) {
    u_.array_ = new array(a);
  }
  
  inline value::value(const object& o) : type_(object_type) {
    u_.object_ = new object(o);
  }
  
  inline value::value(const char* s) : type_(string_type) {
    u_.string_ = new std::string(s);
  }
  
  inline value::value(const char* s, size_t len) : type_(string_type) {
    u_.string_ = new std::string(s, len);
  }
  
  inline value::~value() {
    switch (type_) {
#define DEINIT(p) case p##type: delete u_.p; break
      DEINIT(string_);
      DEINIT(array_);
      DEINIT(object_);
#undef DEINIT
    default: break;
    }
  }
  
  inline value::value(const value& x) : type_(x.type_) {
    switch (type_) {
#define INIT(p, v) case p##type: u_.p = v; break
      INIT(string_, new std::string(*x.u_.string_));
      INIT(array_, new array(*x.u_.array_));
      INIT(object_, new object(*x.u_.object_));
#undef INIT
    default:
      u_ = x.u_;
      break;
    }
  }
  
  inline value& value::operator=(const value& x) {
    if (this != &x) {
      this->~value();
      new (this) value(x);
    }
    return *this;
  }
  
  inline void value::swap(value& x) {
    std::swap(type_, x.type_);
    std::swap(u_, x.u_);
  }
  
#define IS(ctype, jtype)			     \
  template <> inline bool value::is<ctype>() const { \
    return type_ == jtype##_type;		     \
  }
  IS(null, null)
  IS(bool, boolean)
#ifdef PICOJSON_USE_INT64
  IS(int64_t, int64)
#endif
  IS(std::string, string)
  IS(array, array)
  IS(object, object)
#undef IS
  template <> inline bool value::is<double>() const {
    return type_ == number_type
#ifdef PICOJSON_USE_INT64
      || type_ == int64_type
#endif
      ;
  }
  
#define GET(ctype, var)						\
  template <> inline const ctype& value::get<ctype>() const {	\
    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \
	   && is<ctype>());				        \
    return var;							\
  }								\
  template <> inline ctype& value::get<ctype>() {		\
    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()"	\
	   && is<ctype>());					\
    return var;							\
  }
  GET(bool, u_.boolean_)
  GET(std::string, *u_.string_)
  GET(array, *u_.array_)
  GET(object, *u_.object_)
#ifdef PICOJSON_USE_INT64
  GET(double, (type_ == int64_type && (const_cast<value*>(this)->type_ = number_type, const_cast<value*>(this)->u_.number_ = u_.int64_), u_.number_))
  GET(int64_t, u_.int64_)
#else
  GET(double, u_.number_)
#endif
#undef GET
  
  inline bool value::evaluate_as_boolean() const {
    switch (type_) {
    case null_type:
      return false;
    case boolean_type:
      return u_.boolean_;
    case number_type:
      return u_.number_ != 0;
    case string_type:
      return ! u_.string_->empty();
    default:
      return true;
    }
  }
  
  inline const value& value::get(size_t idx) const {
    static value s_null;
    PICOJSON_ASSERT(is<array>());
    return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
  }

  inline value& value::get(size_t idx) {
    static value s_null;
    PICOJSON_ASSERT(is<array>());
    return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
  }

  inline const value& value::get(const std::string& key) const {
    static value s_null;
    PICOJSON_ASSERT(is<object>());
    object::const_iterator i = u_.object_->find(key);
    return i != u_.object_->end() ? i->second : s_null;
  }

  inline value& value::get(const std::string& key) {
    static value s_null;
    PICOJSON_ASSERT(is<object>());
    object::iterator i = u_.object_->find(key);
    return i != u_.object_->end() ? i->second : s_null;
  }

  inline bool value::contains(size_t idx) const {
    PICOJSON_ASSERT(is<array>());
    return idx < u_.array_->size();
  }

  inline bool value::contains(const std::string& key) const {
    PICOJSON_ASSERT(is<object>());
    object::const_iterator i = u_.object_->find(key);
    return i != u_.object_->end();
  }
  
  inline std::string value::to_str() const {
    switch (type_) {
    case null_type:      return "null";
    case boolean_type:   return u_.boolean_ ? "true" : "false";
#ifdef PICOJSON_USE_INT64
    case int64_type: {
      char buf[sizeof("-9223372036854775808")];
      SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
      return buf;
    }
#endif
    case number_type:    {
      char buf[256];
      double tmp;
      SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_);
#if PICOJSON_USE_LOCALE
      char *decimal_point = localeconv()->decimal_point;
      if (strcmp(decimal_point, ".") != 0) {
        size_t decimal_point_len = strlen(decimal_point);
        for (char *p = buf; *p != '\0'; ++p) {
          if (strncmp(p, decimal_point, decimal_point_len) == 0) {
            return std::string(buf, p) + "." + (p + decimal_point_len);
          }
        }
      }
#endif
      return buf;
    }
    case string_type:    return *u_.string_;
    case array_type:     return "array";
    case object_type:    return "object";
    default:             PICOJSON_ASSERT(0);
#ifdef _MSC_VER
      __assume(0);
#endif
    }
    return std::string();
  }
  
  template <typename Iter> void copy(const std::string& s, Iter oi) {
    std::copy(s.begin(), s.end(), oi);
  }
  
  template <typename Iter> void serialize_str(const std::string& s, Iter oi) {
    *oi++ = '"';
    for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
      switch (*i) {
#define MAP(val, sym) case val: copy(sym, oi); break
	MAP('"', "\\\"");
	MAP('\\', "\\\\");
	MAP('/', "\\/");
	MAP('\b', "\\b");
	MAP('\f', "\\f");
	MAP('\n', "\\n");
	MAP('\r', "\\r");
	MAP('\t', "\\t");
#undef MAP
      default:
	if (static_cast<unsigned char>(*i) < 0x20 || *i == 0x7f) {
	  char buf[7];
	  SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff);
	  copy(buf, buf + 6, oi);
	  } else {
	  *oi++ = *i;
	}
	break;
      }
    }
    *oi++ = '"';
  }

  template <typename Iter> void value::serialize(Iter oi, bool prettify) const {
    return _serialize(oi, prettify ? 0 : -1);
  }
  
  inline std::string value::serialize(bool prettify) const {
    return _serialize(prettify ? 0 : -1);
  }

  template <typename Iter> void value::_indent(Iter oi, int indent) {
    *oi++ = '\n';
    for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
      *oi++ = ' ';
    }
  }

  template <typename Iter> void value::_serialize(Iter oi, int indent) const {
    switch (type_) {
    case string_type:
      serialize_str(*u_.string_, oi);
      break;
    case array_type: {
      *oi++ = '[';
      if (indent != -1) {
        ++indent;
      }
      for (array::const_iterator i = u_.array_->begin();
           i != u_.array_->end();
           ++i) {
	if (i != u_.array_->begin()) {
	  *oi++ = ',';
	}
        if (indent != -1) {
          _indent(oi, indent);
        }
	i->_serialize(oi, indent);
      }
      if (indent != -1) {
        --indent;
        if (! u_.array_->empty()) {
          _indent(oi, indent);
        }
      }
      *oi++ = ']';
      break;
    }
    case object_type: {
      *oi++ = '{';
      if (indent != -1) {
        ++indent;
      }
      for (object::const_iterator i = u_.object_->begin();
	   i != u_.object_->end();
	   ++i) {
	if (i != u_.object_->begin()) {
	  *oi++ = ',';
	}
        if (indent != -1) {
          _indent(oi, indent);
        }
	serialize_str(i->first, oi);
	*oi++ = ':';
        if (indent != -1) {
          *oi++ = ' ';
        }
        i->second._serialize(oi, indent);
      }
      if (indent != -1) {
        --indent;
        if (! u_.object_->empty()) {
          _indent(oi, indent);
        }
      }
      *oi++ = '}';
      break;
    }
    default:
      copy(to_str(), oi);
      break;
    }
    if (indent == 0) {
      *oi++ = '\n';
    }
  }
  
  inline std::string value::_serialize(int indent) const {
    std::string s;
    _serialize(std::back_inserter(s), indent);
    return s;
  }
  
  template <typename Iter> class input {
  protected:
    Iter cur_, end_;
    int last_ch_;
    bool ungot_;
    int line_;
  public:
    input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {}
    int getc() {
      if (ungot_) {
	ungot_ = false;
	return last_ch_;
      }
      if (cur_ == end_) {
	last_ch_ = -1;
	return -1;
      }
      if (last_ch_ == '\n') {
	line_++;
      }
      last_ch_ = *cur_ & 0xff;
      ++cur_;
      return last_ch_;
    }
    void ungetc() {
      if (last_ch_ != -1) {
	PICOJSON_ASSERT(! ungot_);
	ungot_ = true;
      }
    }
    Iter cur() const { return cur_; }
    int line() const { return line_; }
    void skip_ws() {
      while (1) {
	int ch = getc();
	if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
	  ungetc();
	  break;
	}
      }
    }
    bool expect(int expect) {
      skip_ws();
      if (getc() != expect) {
	ungetc();
	return false;
      }
      return true;
    }
    bool match(const std::string& pattern) {
      for (std::string::const_iterator pi(pattern.begin());
	   pi != pattern.end();
	   ++pi) {
	if (getc() != *pi) {
	  ungetc();
	  return false;
	}
      }
      return true;
    }
  };
  
  template<typename Iter> inline int _parse_quadhex(input<Iter> &in) {
    int uni_ch = 0, hex;
    for (int i = 0; i < 4; i++) {
      if ((hex = in.getc()) == -1) {
	return -1;
      }
      if ('0' <= hex && hex <= '9') {
	hex -= '0';
      } else if ('A' <= hex && hex <= 'F') {
	hex -= 'A' - 0xa;
      } else if ('a' <= hex && hex <= 'f') {
	hex -= 'a' - 0xa;
      } else {
	in.ungetc();
	return -1;
      }
      uni_ch = uni_ch * 16 + hex;
    }
    return uni_ch;
  }
  
  template<typename String, typename Iter> inline bool _parse_codepoint(String& out, input<Iter>& in) {
    int uni_ch;
    if ((uni_ch = _parse_quadhex(in)) == -1) {
      return false;
    }
    if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
      if (0xdc00 <= uni_ch) {
	// a second 16-bit of a surrogate pair appeared
	return false;
      }
      // first 16-bit of surrogate pair, get the next one
      if (in.getc() != '\\' || in.getc() != 'u') {
	in.ungetc();
	return false;
      }
      int second = _parse_quadhex(in);
      if (! (0xdc00 <= second && second <= 0xdfff)) {
	return false;
      }
      uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
      uni_ch += 0x10000;
    }
    if (uni_ch < 0x80) {
      out.push_back(uni_ch);
    } else {
      if (uni_ch < 0x800) {
	out.push_back(0xc0 | (uni_ch >> 6));
      } else {
	if (uni_ch < 0x10000) {
	  out.push_back(0xe0 | (uni_ch >> 12));
	} else {
	  out.push_back(0xf0 | (uni_ch >> 18));
	  out.push_back(0x80 | ((uni_ch >> 12) & 0x3f));
	}
	out.push_back(0x80 | ((uni_ch >> 6) & 0x3f));
      }
      out.push_back(0x80 | (uni_ch & 0x3f));
    }
    return true;
  }
  
  template<typename String, typename Iter> inline bool _parse_string(String& out, input<Iter>& in) {
    while (1) {
      int ch = in.getc();
      if (ch < ' ') {
	in.ungetc();
	return false;
      } else if (ch == '"') {
	return true;
      } else if (ch == '\\') {
	if ((ch = in.getc()) == -1) {
	  return false;
	}
	switch (ch) {
#define MAP(sym, val) case sym: out.push_back(val); break
	  MAP('"', '\"');
	  MAP('\\', '\\');
	  MAP('/', '/');
	  MAP('b', '\b');
	  MAP('f', '\f');
	  MAP('n', '\n');
	  MAP('r', '\r');
	  MAP('t', '\t');
#undef MAP
	case 'u':
	  if (! _parse_codepoint(out, in)) {
	    return false;
	  }
	  break;
	default:
	  return false;
	}
      } else {
	out.push_back(ch);
      }
    }
    return false;
  }
  
  template <typename Context, typename Iter> inline bool _parse_array(Context& ctx, input<Iter>& in) {
    if (! ctx.parse_array_start()) {
      return false;
    }
    size_t idx = 0;
    if (in.expect(']')) {
      return ctx.parse_array_stop(idx);
    }
    do {
      if (! ctx.parse_array_item(in, idx)) {
	return false;
      }
      idx++;
    } while (in.expect(','));
    return in.expect(']') && ctx.parse_array_stop(idx);
  }
  
  template <typename Context, typename Iter> inline bool _parse_object(Context& ctx, input<Iter>& in) {
    if (! ctx.parse_object_start()) {
      return false;
    }
    if (in.expect('}')) {
      return true;
    }
    do {
      std::string key;
      if (! in.expect('"')
	  || ! _parse_string(key, in)
	  || ! in.expect(':')) {
	return false;
      }
      if (! ctx.parse_object_item(in, key)) {
	return false;
      }
    } while (in.expect(','));
    return in.expect('}');
  }
  
  template <typename Iter> inline std::string _parse_number(input<Iter>& in) {
    std::string num_str;
    while (1) {
      int ch = in.getc();
      if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-'
          || ch == 'e' || ch == 'E') {
        num_str.push_back(ch);
      } else if (ch == '.') {
#if PICOJSON_USE_LOCALE
        num_str += localeconv()->decimal_point;
#else
        num_str.push_back('.');
#endif
      } else {
	in.ungetc();
	break;
      }
    }
    return num_str;
  }
  
  template <typename Context, typename Iter> inline bool _parse(Context& ctx, input<Iter>& in) {
    in.skip_ws();
    int ch = in.getc();
    switch (ch) {
#define IS(ch, text, op) case ch: \
      if (in.match(text) && op) { \
	return true; \
      } else { \
	return false; \
      }
      IS('n', "ull", ctx.set_null());
      IS('f', "alse", ctx.set_bool(false));
      IS('t', "rue", ctx.set_bool(true));
#undef IS
    case '"':
      return ctx.parse_string(in);
    case '[':
      return _parse_array(ctx, in);
    case '{':
      return _parse_object(ctx, in);
    default:
      if (('0' <= ch && ch <= '9') || ch == '-') {
        double f;
        char *endp;
	in.ungetc();
        std::string num_str = _parse_number(in);
        if (num_str.empty()) {
          return false;
        }
#ifdef PICOJSON_USE_INT64
        {
          errno = 0;
          intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
          if (errno == 0
              && std::numeric_limits<int64_t>::min() <= ival
              && ival <= std::numeric_limits<int64_t>::max()
              && endp == num_str.c_str() + num_str.size()) {
            ctx.set_int64(ival);
            return true;
          }
        }
#endif
        f = strtod(num_str.c_str(), &endp);
        if (endp == num_str.c_str() + num_str.size()) {
          ctx.set_number(f);
          return true;
        }
        return false;
      }
      break;
    }
    in.ungetc();
    return false;
  }
  
  class deny_parse_context {
  public:
    bool set_null() { return false; }
    bool set_bool(bool) { return false; }
#ifdef PICOJSON_USE_INT64
    bool set_int64(int64_t) { return false; }
#endif
    bool set_number(double) { return false; }
    template <typename Iter> bool parse_string(input<Iter>&) { return false; }
    bool parse_array_start() { return false; }
    template <typename Iter> bool parse_array_item(input<Iter>&, size_t) {
      return false;
    }
    bool parse_array_stop(size_t) { return false; }
    bool parse_object_start() { return false; }
    template <typename Iter> bool parse_object_item(input<Iter>&, const std::string&) {
      return false;
    }
  };
  
  class default_parse_context {
  protected:
    value* out_;
  public:
    default_parse_context(value* out) : out_(out) {}
    bool set_null() {
      *out_ = value();
      return true;
    }
    bool set_bool(bool b) {
      *out_ = value(b);
      return true;
    }
#ifdef PICOJSON_USE_INT64
    bool set_int64(int64_t i) {
      *out_ = value(i);
      return true;
    }
#endif
    bool set_number(double f) {
      *out_ = value(f);
      return true;
    }
    template<typename Iter> bool parse_string(input<Iter>& in) {
      *out_ = value(string_type, false);
      return _parse_string(out_->get<std::string>(), in);
    }
    bool parse_array_start() {
      *out_ = value(array_type, false);
      return true;
    }
    template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
      array& a = out_->get<array>();
      a.push_back(value());
      default_parse_context ctx(&a.back());
      return _parse(ctx, in);
    }
    bool parse_array_stop(size_t) { return true; }
    bool parse_object_start() {
      *out_ = value(object_type, false);
      return true;
    }
    template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string& key) {
      object& o = out_->get<object>();
      default_parse_context ctx(&o[key]);
      return _parse(ctx, in);
    }
  private:
    default_parse_context(const default_parse_context&);
    default_parse_context& operator=(const default_parse_context&);
  };

  class null_parse_context {
  public:
    struct dummy_str {
      void push_back(int) {}
    };
  public:
    null_parse_context() {}
    bool set_null() { return true; }
    bool set_bool(bool) { return true; }
#ifdef PICOJSON_USE_INT64
    bool set_int64(int64_t) { return true; }
#endif
    bool set_number(double) { return true; }
    template <typename Iter> bool parse_string(input<Iter>& in) {
      dummy_str s;
      return _parse_string(s, in);
    }
    bool parse_array_start() { return true; }
    template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
      return _parse(*this, in);
    }
    bool parse_array_stop(size_t) { return true; }
    bool parse_object_start() { return true; }
    template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string&) {
      return _parse(*this, in);
    }
  private:
    null_parse_context(const null_parse_context&);
    null_parse_context& operator=(const null_parse_context&);
  };
  
  // obsolete, use the version below
  template <typename Iter> inline std::string parse(value& out, Iter& pos, const Iter& last) {
    std::string err;
    pos = parse(out, pos, last, &err);
    return err;
  }
  
  template <typename Context, typename Iter> inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) {
    input<Iter> in(first, last);
    if (! _parse(ctx, in) && err != NULL) {
      char buf[64];
      SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
      *err = buf;
      while (1) {
	int ch = in.getc();
	if (ch == -1 || ch == '\n') {
	  break;
	} else if (ch >= ' ') {
	  err->push_back(ch);
	}
      }
    }
    return in.cur();
  }
  
  template <typename Iter> inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) {
    default_parse_context ctx(&out);
    return _parse(ctx, first, last, err);
  }
  
  inline std::string parse(value& out, std::istream& is) {
    std::string err;
    parse(out, std::istreambuf_iterator<char>(is.rdbuf()),
	  std::istreambuf_iterator<char>(), &err);
    return err;
  }
  
  template <typename T> struct last_error_t {
    static std::string s;
  };
  template <typename T> std::string last_error_t<T>::s;
  
  inline void set_last_error(const std::string& s) {
    last_error_t<bool>::s = s;
  }
  
  inline const std::string& get_last_error() {
    return last_error_t<bool>::s;
  }

  inline bool operator==(const value& x, const value& y) {
    if (x.is<null>())
      return y.is<null>();
#define PICOJSON_CMP(type)					\
    if (x.is<type>())						\
      return y.is<type>() && x.get<type>() == y.get<type>()
    PICOJSON_CMP(bool);
    PICOJSON_CMP(double);
    PICOJSON_CMP(std::string);
    PICOJSON_CMP(array);
    PICOJSON_CMP(object);
#undef PICOJSON_CMP
    PICOJSON_ASSERT(0);
#ifdef _MSC_VER
    __assume(0);
#endif
    return false;
  }
  
  inline bool operator!=(const value& x, const value& y) {
    return ! (x == y);
  }
}

namespace std {
  template<> inline void swap(picojson::value& x, picojson::value& y)
    {
      x.swap(y);
    }
}

inline std::istream& operator>>(std::istream& is, picojson::value& x)
{
  picojson::set_last_error(std::string());
  std::string err = picojson::parse(x, is);
  if (! err.empty()) {
    picojson::set_last_error(err);
    is.setstate(std::ios::failbit);
  }
  return is;
}

inline std::ostream& operator<<(std::ostream& os, const picojson::value& x)
{
  x.serialize(std::ostream_iterator<char>(os));
  return os;
}
#ifdef _MSC_VER
    #pragma warning(pop)
#endif

#endif
#ifdef TEST_PICOJSON
#ifdef _MSC_VER
    #pragma warning(disable : 4127) // conditional expression is constant
#endif

using namespace std;
  
static bool success = true;
static int test_num = 0;

static void ok(bool b, const char* name = "")
{
  if (! b)
    success = false;
  printf("%s %d - %s\n", b ? "ok" : "ng", ++test_num, name);
}

static void done_testing()
{
  printf("1..%d\n", test_num);
}

template <typename T> void is(const T& x, const T& y, const char* name = "")
{
  if (x == y) {
    ok(true, name);
  } else {
    ok(false, name);
  }
}

#include <algorithm>
#include <sstream>
#include <float.h>
#include <limits.h>

int main(void)
{
#if PICOJSON_USE_LOCALE
  setlocale(LC_ALL, "");
#endif

  // constructors
#define TEST(expr, expected) \
    is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr)
  
  TEST( (true),  "true");
  TEST( (false), "false");
  TEST( (42.0),   "42");
  TEST( (string("hello")), "\"hello\"");
  TEST( ("hello"), "\"hello\"");
  TEST( ("hello", 4), "\"hell\"");

  {
    double a = 1;
    for (int i = 0; i < 1024; i++) {
      picojson::value vi(a);
      std::stringstream ss;
      ss << vi;
      picojson::value vo;
      ss >> vo;
      double b = vo.get<double>();
      if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) {
        printf("ng i=%d a=%.18e b=%.18e\n", i, a, b);
      }
      a *= 2;
    }
  }
  
#undef TEST
  
#define TEST(in, type, cmp, serialize_test) {				\
    picojson::value v;							\
    const char* s = in;							\
    string err = picojson::parse(v, s, s + strlen(s));			\
    ok(err.empty(), in " no error");					\
    ok(v.is<type>(), in " check type");					\
    is<type>(v.get<type>(), cmp, in " correct output");			\
    is(*s, '\0', in " read to eof");					\
    if (serialize_test) {						\
      is(v.serialize(), string(in), in " serialize");			\
    }									\
  }
  TEST("false", bool, false, true);
  TEST("true", bool, true, true);
  TEST("90.5", double, 90.5, false);
  TEST("1.7976931348623157e+308", double, DBL_MAX, false);
  TEST("\"hello\"", string, string("hello"), true);
  TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"),
       true);
  TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string,
       string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false);
  TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false);
#ifdef PICOJSON_USE_INT64
  TEST("0", int64_t, 0, true);
  TEST("-9223372036854775808", int64_t, std::numeric_limits<int64_t>::min(), true);
  TEST("9223372036854775807", int64_t, std::numeric_limits<int64_t>::max(), true);
#endif
#undef TEST

#define TEST(type, expr) {					       \
    picojson::value v;						       \
    const char *s = expr;					       \
    string err = picojson::parse(v, s, s + strlen(s));		       \
    ok(err.empty(), "empty " #type " no error");		       \
    ok(v.is<picojson::type>(), "empty " #type " check type");	       \
    ok(v.get<picojson::type>().empty(), "check " #type " array size"); \
  }
  TEST(array, "[]");
  TEST(object, "{}");
#undef TEST
  
  {
    picojson::value v;
    const char *s = "[1,true,\"hello\"]";
    string err = picojson::parse(v, s, s + strlen(s));
    ok(err.empty(), "array no error");
    ok(v.is<picojson::array>(), "array check type");
    is(v.get<picojson::array>().size(), size_t(3), "check array size");
    ok(v.contains(0), "check contains array[0]");
    ok(v.get(0).is<double>(), "check array[0] type");
    is(v.get(0).get<double>(), 1.0, "check array[0] value");
    ok(v.contains(1), "check contains array[1]");
    ok(v.get(1).is<bool>(), "check array[1] type");
    ok(v.get(1).get<bool>(), "check array[1] value");
    ok(v.contains(2), "check contains array[2]");
    ok(v.get(2).is<string>(), "check array[2] type");
    is(v.get(2).get<string>(), string("hello"), "check array[2] value");
    ok(!v.contains(3), "check not contains array[3]");
  }
  
  {
    picojson::value v;
    const char *s = "{ \"a\": true }";
    string err = picojson::parse(v, s, s + strlen(s));
    ok(err.empty(), "object no error");
    ok(v.is<picojson::object>(), "object check type");
    is(v.get<picojson::object>().size(), size_t(1), "check object size");
    ok(v.contains("a"), "check contains property");
    ok(v.get("a").is<bool>(), "check bool property exists");
    is(v.get("a").get<bool>(), true, "check bool property value");
    is(v.serialize(), string("{\"a\":true}"), "serialize object");
    ok(!v.contains("z"), "check not contains property");
  }

#define TEST(json, msg) do {				\
    picojson::value v;					\
    const char *s = json;				\
    string err = picojson::parse(v, s, s + strlen(s));	\
    is(err, string("syntax error at line " msg), msg);	\
  } while (0)
  TEST("falsoa", "1 near: oa");
  TEST("{]", "1 near: ]");
  TEST("\n\bbell", "2 near: bell");
  TEST("\"abc\nd\"", "1 near: ");
#undef TEST
  
  {
    picojson::value v1, v2;
    const char *s;
    string err;
    s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }";
    err = picojson::parse(v1, s, s + strlen(s));
    s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }";
    err = picojson::parse(v2, s, s + strlen(s));
    ok((v1 == v2), "check == operator in deep comparison");
  }

  {
    picojson::value v1, v2;
    const char *s;
    string err;
    s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }";
    err = picojson::parse(v1, s, s + strlen(s));
    s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }";
    err = picojson::parse(v2, s, s + strlen(s));
    ok((v1 != v2), "check != operator for array in deep comparison");
  }

  {
    picojson::value v1, v2;
    const char *s;
    string err;
    s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }";
    err = picojson::parse(v1, s, s + strlen(s));
    s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }";
    err = picojson::parse(v2, s, s + strlen(s));
    ok((v1 != v2), "check != operator for object in deep comparison");
  }

  {
    picojson::value v1, v2;
    const char *s;
    string err;
    s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }";
    err = picojson::parse(v1, s, s + strlen(s));
    picojson::object& o = v1.get<picojson::object>();
    o.erase("b");
    picojson::array& a = o["a"].get<picojson::array>();
    picojson::array::iterator i;
    i = std::remove(a.begin(), a.end(), picojson::value(std::string("three")));
    a.erase(i, a.end());
    s = "{ \"a\": [1,2], \"d\": 2 }";
    err = picojson::parse(v2, s, s + strlen(s));
    ok((v1 == v2), "check erase()");
  }

  ok(picojson::value(3.0).serialize() == "3",
     "integral number should be serialized as a integer");
  
  {
    const char* s = "{ \"a\": [1,2], \"d\": 2 }";
    picojson::null_parse_context ctx;
    string err;
    picojson::_parse(ctx, s, s + strlen(s), &err);
    ok(err.empty(), "null_parse_context");
  }
  
  {
    picojson::value v1, v2;
    v1 = picojson::value(true);
    swap(v1, v2);
    ok(v1.is<picojson::null>(), "swap (null)");
    ok(v2.get<bool>() == true, "swap (bool)");

    v1 = picojson::value("a");
    v2 = picojson::value(1.0);
    swap(v1, v2);
    ok(v1.get<double>() == 1.0, "swap (dobule)");
    ok(v2.get<string>() == "a", "swap (string)");

    v1 = picojson::value(picojson::object());
    v2 = picojson::value(picojson::array());
    swap(v1, v2);
    ok(v1.is<picojson::array>(), "swap (array)");
    ok(v2.is<picojson::object>(), "swap (object)");
  }
  
  {
    picojson::value v;
    const char *s = "{ \"a\": 1, \"b\": [ 2, { \"b1\": \"abc\" } ], \"c\": {}, \"d\": [] }";
    string err;
    err = picojson::parse(v, s, s + strlen(s));
    ok(err.empty(), "parse test data for prettifying output");
    ok(v.serialize() == "{\"a\":1,\"b\":[2,{\"b1\":\"abc\"}],\"c\":{},\"d\":[]}", "non-prettifying output");
    ok(v.serialize(true) == "{\n  \"a\": 1,\n  \"b\": [\n    2,\n    {\n      \"b1\": \"abc\"\n    }\n  ],\n  \"c\": {},\n  \"d\": []\n}\n", "prettifying output");
  }

  try {
    picojson::value v(std::numeric_limits<double>::quiet_NaN());
    ok(false, "should not accept NaN");
  } catch (std::overflow_error e) {
    ok(true, "should not accept NaN");
  }

  try {
    picojson::value v(std::numeric_limits<double>::infinity());
    ok(false, "should not accept infinity");
  } catch (std::overflow_error e) {
    ok(true, "should not accept infinity");
  }

  try {
    picojson::value v(123.);
    ok(! v.is<bool>(), "is<wrong_type>() should return false");
    v.get<bool>();
    ok(false, "get<wrong_type>() should raise an error");
  } catch (std::runtime_error e) {
    ok(true, "get<wrong_type>() should raise an error");
  }

#ifdef PICOJSON_USE_INT64
  {
    picojson::value v1((int64_t)123);
    ok(v1.is<int64_t>(), "is int64_t");
    ok(v1.is<double>(), "is double as well");
    ok(v1.serialize() == "123", "serialize the value");
    ok(v1.get<int64_t>() == 123, "value is correct as int64_t");
    ok(v1.get<double>(), "value is correct as double");

    ok(! v1.is<int64_t>(), "is no more int64_type once get<double>() is called");
    ok(v1.is<double>(), "and is still a double");

    const char *s = "-9223372036854775809";
    ok(picojson::parse(v1, s, s + strlen(s)).empty(), "parse underflowing int64_t");
    ok(! v1.is<int64_t>(), "underflowing int is not int64_t");
    ok(v1.is<double>(), "underflowing int is double");
    ok(v1.get<double>() + 9.22337203685478e+18 < 65536, "double value is somewhat correct");
  }
#endif

  done_testing();

  return success ? 0 : 1;
}

#endif


================================================
FILE: include/webdriverxx/response_status_code.h
================================================
#ifndef WEBDRIVERXX_RESPONSE_STATUS_CODE_H
#define WEBDRIVERXX_RESPONSE_STATUS_CODE_H 

namespace webdriverxx {
namespace response_status_code { 

enum Value {
	kSuccess = 0,
	kNoSuchDriver = 6,
	kNoSuchElement = 7,
	kNoSuchFrame = 8,
	kUnknownCommand = 9,
	kStaleElementReference = 10,
	kElementNotVisible = 11,
	kInvalidElementState = 12,
	kUnknownError = 13,
	kElementIsNotSelectable = 15,
	kJavaScriptError = 17,
	kXPathLookupError = 19,
	kTimeout = 21,
	kNoSuchWindow = 23,
	kInvalidCookieDomain = 24,
	kUnableToSetCookie = 25,
	kUnexpectedAlertOpen = 26,
	kNoAlertOpenError = 27,
	kScriptTimeout = 28,
	kInvalidElementCoordinates = 29,
	kIMENotAvailable = 30,
	kIMEEngineActivationFailed = 31,
	kInvalidSelector = 32,
	kSessionNotCreatedException = 33,
	kMoveTargetOutOfBounds = 34
}; 

inline
const char* ToString(Value code) {
	switch(code) {
	case kSuccess: return "The command executed successfully.";
	case kNoSuchDriver: return "A session is either terminated or not started"; 
	case kNoSuchElement: return "An element could not be located on the page using the given search parameters."; 
	case kNoSuchFrame: return "A request to switch to a frame could not be satisfied because the frame could not be found."; 
	case kUnknownCommand: return "The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource."; 
	case kStaleElementReference: return "An element command failed because the referenced element is no longer attached to the DOM."; 
	case kElementNotVisible: return "An element command could not be completed because the element is not visible on the page."; 
	case kInvalidElementState: return "An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element)."; 
	case kUnknownError: return "An unknown server-side error occurred while processing the command."; 
	case kElementIsNotSelectable: return "An attempt was made to select an element that cannot be selected."; 
	case kJavaScriptError: return "An error occurred while executing user supplied JavaScript."; 
	case kXPathLookupError: return "An error occurred while searching for an element by XPath."; 
	case kTimeout: return "An operation did not complete before its timeout expired."; 
	case kNoSuchWindow: return "A request to switch to a different window could not be satisfied because the window could not be found."; 
	case kInvalidCookieDomain: return "An illegal attempt was made to set a cookie under a different domain than the current page."; 
	case kUnableToSetCookie: return "A request to set a cookie's value could not be satisfied."; 
	case kUnexpectedAlertOpen: return "A modal dialog was open, blocking this operation"; 
	case kNoAlertOpenError: return "An attempt was made to operate on a modal dialog when one was not open."; 
	case kScriptTimeout: return "A script did not complete before its timeout expired."; 
	case kInvalidElementCoordinates: return "The coordinates provided to an interactions operation are invalid."; 
	case kIMENotAvailable: return "IME was not available."; 
	case kIMEEngineActivationFailed: return "An IME engine could not be started."; 
	case kInvalidSelector: return "Argument was an invalid selector (e.g. XPath/CSS).";
	case kSessionNotCreatedException: return "A new session could not be created.";
	case kMoveTargetOutOfBounds: return "Target provided for a move action is out of bounds.";
	}
	return "Unknown";
} 

} // namespace response_status_code 
} // namespace webdriverxx 

#endif


================================================
FILE: include/webdriverxx/session.h
================================================
#ifndef WEBDRIVERXX_SESSION_H
#define WEBDRIVERXX_SESSION_H

#include "element.h"
#include "window.h"
#include "by.h"
#include "capabilities.h"
#include "keys.h"
#include "js_args.h"
#include "detail/resource.h"
#include "detail/keyboard.h"
#include "detail/shared.h"
#include "detail/factories_impl.h"
#include "picojson.h"
#include <string>

namespace webdriverxx {

class Client;

class Session { // copyable
public:	
	Capabilities GetCapabilities() const;
	std::string GetSource() const;
	std::string GetTitle() const;
	std::string GetUrl() const;
	std::string GetScreenshot() const; // Base64 PNG

	const Session& Navigate(const std::string& url) const;
	const Session& Get(const std::string& url) const; // Same as Navigate
	const Session& Forward() const;
	const Session& Back() const;
	const Session& Refresh() const;

	const Session& Execute(const std::string& script, const JsArgs& args = JsArgs()) const;
	template<typename T>
	T Eval(const std::string& script, const JsArgs& args = JsArgs()) const;
	const Session& ExecuteAsync(const std::string& script, const JsArgs& args = JsArgs()) const;
	template<typename T>
	T EvalAsync(const std::string& script, const JsArgs& args = JsArgs()) const;

	const Session& SetFocusToFrame(const Element& frame) const;
	const Session& SetFocusToFrame(const std::string& id) const;
	const Session& SetFocusToFrame(int number) const;
	const Session& SetFocusToDefaultFrame() const;
	const Session& SetFocusToParentFrame() const;

	std::vector<Window> GetWindows() const;
	Window GetCurrentWindow() const;
	const Session& CloseCurrentWindow() const;
	const Session& SetFocusToWindow(const std::string& window_name) const;
	const Session& SetFocusToWindow(const Window& window) const;
	
	Element GetActiveElement() const;

	Element FindElement(const By& by) const;
	std::vector<Element> FindElements(const By& by) const;

	std::vector<Cookie> GetCookies() const;
	const Session& SetCookie(const Cookie& cookie) const;
	const Session& DeleteCookies() const;
	const Session& DeleteCookie(const std::string& name) const;

	std::string GetAlertText() const;
	const Session& SendKeysToAlert(const std::string& text) const;
	const Session& AcceptAlert() const;
	const Session& DismissAlert() const;

	const Session& SendKeys(const std::string& keys) const;
	const Session& SendKeys(const Shortcut& shortcut) const;

	const Session& MoveToTopLeftOf(const Element&, const Offset& = Offset()) const;
	const Session& MoveToCenterOf(const Element&) const;
	const Session& MoveTo(const Offset&) const;
	const Session& Click(mouse::Button = mouse::LeftButton) const;
	const Session& DoubleClick() const;
	const Session& ButtonDown(mouse::Button = mouse::LeftButton) const;
	const Session& ButtonUp(mouse::Button = mouse::LeftButton) const;

	const Session& SetTimeoutMs(timeout::Type type, int milliseconds);
	const Session& SetImplicitTimeoutMs(int milliseconds);
	const Session& SetAsyncScriptTimeoutMs(int milliseconds);

	void DeleteSession() const; // No need to delete sessions created by WebDriver or Client
	virtual ~Session() {}

private:
	friend class Client; // Only Client can create Sessions

	explicit Session(const detail::Shared<detail::Resource>& resource);

	Window MakeWindow(const std::string& handle) const;
	detail::Keyboard GetKeyboard() const;
	template<typename T>
	void InternalEval(const std::string& webdriver_command,
		const std::string& script, const JsArgs& args,
		T& result) const;
	void InternalEval(const std::string& webdriver_command,
		const std::string& script, const JsArgs& args,
		Element& result) const;
	picojson::value InternalEvalJsonValue(const std::string& command,
		const std::string& script, const JsArgs& args) const;
	const Session& InternalSetFocusToFrame(const picojson::value& id) const;
	const Session& InternalMoveTo(const Element*, const Offset*) const;
	const Session& InternalMouseButtonCommand(const char* command, mouse::Button button) const;

private:
	detail::Shared<detail::Resource> resource_;
	detail::Shared<detail::SessionFactory> factory_;
};

} // namespace webdriverxx

#include "session.inl"

#endif


================================================
FILE: include/webdriverxx/session.inl
================================================
#include "conversions.h"
#include "detail/error_handling.h"
#include "detail/types.h"
#include <algorithm>

namespace webdriverxx {

inline
Session::Session(const detail::Shared<detail::Resource>& resource)
	: resource_(resource)
	, factory_(new detail::SessionFactory(resource))
{}

inline
void Session::DeleteSession() const {
	resource_->Delete();
}

inline
Capabilities Session::GetCapabilities() const {
	return Capabilities(resource_->Get().get<picojson::object>());
}

inline
std::string Session::GetSource() const {
	return resource_->GetString("source");
}

inline
std::string Session::GetTitle() const {
	return resource_->GetString("title");
}

inline
std::string Session::GetUrl() const {
	return resource_->GetString("url");
}

inline
std::string Session::GetScreenshot() const {
	return resource_->GetString("screenshot");
}

inline
const Session& Session::SetTimeoutMs(timeout::Type type, int milliseconds) {
	resource_->Post("timeouts",
		JsonObject()
			.Set("type", type)
			.Set("ms", milliseconds)
		);
	return *this;
}

inline
const Session& Session::SetImplicitTimeoutMs(int milliseconds) {
	resource_->Post("timeouts/implicit_wait",
		JsonObject().Set("ms", milliseconds));
	return *this;
}

inline
const Session& Session::SetAsyncScriptTimeoutMs(int milliseconds) {
	resource_->Post("timeouts/async_script",
		JsonObject().Set("ms", milliseconds));
	return *this;
}

inline
Window Session::GetCurrentWindow() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	return MakeWindow(resource_->GetString("window_handle"));
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
const Session& Session::CloseCurrentWindow() const {
	resource_->Delete("window");
	return *this;
}

inline
const Session& Session::Navigate(const std::string& url) const {
	resource_->Post("url", "url", url);
	return *this;
}

inline
const Session& Session::Get(const std::string& url) const {
	return Navigate(url);
}

inline
const Session& Session::Forward() const {
	resource_->Post("forward");
	return *this;
}

inline
const Session& Session::Back() const {
	resource_->Post("back");
	return *this;
}

inline
const Session& Session::Refresh() const {
	resource_->Post("refresh");
	return *this;
}

inline
const Session& Session::Execute(const std::string& script, const JsArgs& args) const {
	InternalEvalJsonValue("execute", script, args);
	return *this;
}

template<typename T>
T Session::Eval(const std::string& script, const JsArgs& args) const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	T result = T();
	InternalEval("execute", script, args, result);
	return result;
	WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(detail::Fmt()
		<< "script: " << script
		)
}

inline
const Session& Session::ExecuteAsync(const std::string& script, const JsArgs& args) const {
	InternalEvalJsonValue("execute_async", script, args);
	return *this;
}

template<typename T>
T Session::EvalAsync(const std::string& script, const JsArgs& args) const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	T result;
	InternalEval("execute_async", script, args, result);
	return result;
	WEBDRIVERXX_FUNCTION_CONTEXT_END_EX(detail::Fmt()
		<< "script: " << script
		)
}

inline
const Session& Session::SetFocusToWindow(const std::string& window_name_or_handle) const {
	resource_->Post("window", "name", window_name_or_handle);
	return *this;
}

inline
const Session& Session::SetFocusToWindow(const Window& window) const {
	SetFocusToWindow(window.GetHandle());
	return *this;
}

inline
const Session& Session::SetFocusToFrame(const Element& frame) const {
	return InternalSetFocusToFrame(ToJson(frame));
}

inline
const Session& Session::SetFocusToFrame(const std::string& id) const {
	return InternalSetFocusToFrame(ToJson(id));
}

inline
const Session& Session::SetFocusToFrame(int number) const {
	return InternalSetFocusToFrame(ToJson(number));
}

inline
const Session& Session::SetFocusToDefaultFrame() const {
	return InternalSetFocusToFrame(picojson::value());
}

inline
const Session& Session::SetFocusToParentFrame() const {
	resource_->Post("frame/parent");
	return *this;
}

inline
const Session& Session::InternalSetFocusToFrame(const picojson::value& id) const {
	resource_->Post("frame", JsonObject().Set("id", id));
	return *this;
}

inline
std::vector<Window> Session::GetWindows() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	const auto handles =
		FromJson<std::vector<std::string>>(
			resource_->Get("window_handles")
			);
	std::vector<Window> result;
	result.reserve(handles.size());
	std::transform(handles.begin(), handles.end(), std::back_inserter(result),
		[this](const std::string& window_handle){
			return MakeWindow(window_handle);
		});
	return result;
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
Element Session::GetActiveElement() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	return factory_->MakeElement(FromJson<detail::ElementRef>(resource_->Post("element/active")).ref);
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
Element Session::FindElement(const By& by) const {
	return factory_->MakeFinder(resource_).FindElement(by);
}

inline
std::vector<Element> Session::FindElements(const By& by) const {
	return factory_->MakeFinder(resource_).FindElements(by);
}

inline
std::vector<Cookie> Session::GetCookies() const {
	WEBDRIVERXX_FUNCTION_CONTEXT_BEGIN()
	return FromJson<std::vector<Cookie>>(resource_->Get("cookie"));
	WEBDRIVERXX_FUNCTION_CONTEXT_END()
}

inline
const Session& Session::SetCookie(const Cookie& cookie) const {
	resource_->Post("cookie", JsonObject()
		.Set("cookie", ToJson(cookie)));
	return *this;
}

inline
const Session& Session::DeleteCookies() const {
	resource_->Delete("cookie");
	return *this;
}

inline
const Session& Session::DeleteCookie(const std::string& name) const {
	resource_->Delete(std::string("cookie/") + name);
	return *this;
}

inline
std::string Session::GetAlertText() const {
	return resource_->GetString("alert_text");
}

inline
const Session& Session::SendKeysToAlert(const std::string& text) const {
	resource_->Post("alert_text", "text", text);
	return *this;
}

inline
const Session& Session::AcceptAlert() const {
	resource_->Post("accept_alert");
	return *this;
}

inline
const Session& Session::DismissAlert() const {
	resource_->Post("dismiss_alert");
	return *this;
}

inline
const Session& Session::SendKeys(const std::string& keys) const {
	GetKeyboard().SendKeys(keys);
	return *this;
}

inline
const Session& Session::SendKeys(const Shortcut& shortcut) const {
	GetKeyboard().SendKeys(shortcut);
	return *this;
}

inline
const Session& Session::MoveToTopLeftOf(const Element& element, const Offset& offset) const {
	return InternalMoveTo(&element, &offset);
}

inline
const Session& Session::MoveToCenterOf(const Element& element) const {
	return InternalMoveTo(&element, nullptr);
}

inline
const Session& Session::MoveTo(const Offset& offset) const {
	return InternalMoveTo(nullptr, &offset);
}

inline
const Session& Session::InternalMoveTo(
	const Element* element,
	const Offset* offset
	) const {
	JsonObject args;
	if (element)
		args.Set("element", element->GetRef());
	if (offset) {
		args.Set("xoffset", offset->x);
		args.Set("yoffset", offset->y);
	}
	resource_->Post("moveto", args);
	return *this;
}

inline
const Session& Session::Click(mouse::Button button) const {
	return InternalMouseButtonCommand("click", button);
}

inline
const Session& Session::DoubleClick() const {
	resource_->Post("doubleclick");
	return *this;
}

inline
const Session& Session::ButtonDown(mouse::Button button) const {
	return InternalMouseButtonCommand("buttondown", button);
}

inline
const Session& Session::ButtonUp(mouse::Button button) const {
	return InternalMouseButtonCommand("buttonup", button);
}

inline
const Session& Session::InternalMouseButtonCommand(const char* command, mouse::Button button) const {
	resource_->Post(command, "button", static_cast<int>(button));
	return *this;
}

inline
Window Session::MakeWindow(const std::string& handle) const {
	return Window(handle,
		detail::MakeSubResource(resource_, "window", handle)
		);
}

inline
detail::Keyboard Session::GetKeyboard() const
{
	return detail::Keyboard(resource_, "keys");
}

template<typename T>
void Session::InternalEval(const std::string& webdriver_command,
	const std::string& script, const JsArgs& args,
	T& result) const {
	result = FromJson<T>(InternalEvalJsonValue(webdriver_command, script, args));
}

inline
void Session::InternalEval(const std::string& webdriver_command,
	const std::string& script, const JsArgs& args,
	Element& result) const {
	detail::ElementRef element_ref;
	InternalEval(webdriver_command, script, args, element_ref);
	result = factory_->MakeElement(element_ref.ref);
}

inline
picojson::value Session::InternalEvalJsonValue(
	const std::string& webdriver_command,
	const std::string& script,
	const JsArgs& args
	) const {
	return resource_->Post(webdriver_command,
		JsonObject()
			.Set("script", script)
			.Set("args", args.args_)
		);
}

} // namespace webdriverxx


================================================
FILE: include/webdriverxx/types.h
================================================
#ifndef WEBDRIVERXX_TYPES_H
#define WEBDRIVERXX_TYPES_H

#include <string>

namespace webdriverxx {

typedef unsigned long long TimePoint;
typedef unsigned Duration;

struct Size {
	int width;
	int height;
	Size() : width(0), height(0) {}
};

struct Point {
	int x;
	int y;
	Point() : x(0), y(0) {}
	Point(int x, int y) : x(x), y(y) {}
};

typedef Point Offset;

struct Cookie {
	enum {
		NoExpiry = 0
	};

	std::string name;
	std::string value;
	std::string path;
	std::string domain;
	bool secure;
	bool http_only;
	int expiry; // seconds since midnight, January 1, 1970 UTC

	Cookie() : secure(false), http_only(false), expiry(NoExpiry) {}
	Cookie(
		const std::string& name,
		const std::string& value,
		const std::string& path = std::string(),
		const std::string& domain = std::string(),
		bool secure = false,
		bool http_only = false,
		int expiry = NoExpiry
		)
		: name(name)
		, value(value)
		, path(path)
		, domain(domain)
		, secure(secure)
		, http_only(http_only)
		, expiry(expiry)
	{}

	bool operator == (const Cookie& c) const {
		return name == c.name
			&& value == c.value
			&& path == c.path
			&& domain == c.domain
			&& secure == c.secure
			&& http_only == c.http_only
			&& expiry == c.expiry
			;
	}
};

namespace timeout {

typedef const char* Type;

Type const Implicit = "implicit";
Type const PageLoad = "page load";
Type const Script = "script";

} // namespace timeout

namespace mouse {
enum Button {
	LeftButton = 0,
	MiddleButton = 1,
	RightButton = 2
};
} // namespace mouse

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/wait.h
================================================
#ifndef WEBDRIVERXX_WAIT_H
#define WEBDRIVERXX_WAIT_H

#include "detail/error_handling.h"
#include "detail/time.h"
#include "detail/to_string.h"
#include <string>
#include <memory>

namespace webdriverxx {
namespace detail {

template<typename Value, typename DescriptiveGetter>
Value Wait(
	DescriptiveGetter getter,
	Duration timeoutMs = 5000,
	Duration intervalMs = 50
	) {
	const TimePoint timeout = detail::Now() + timeoutMs;
	for (;;) {
		const auto value_ptr = getter(nullptr);
		if (value_ptr)
			return *value_ptr;
		if (detail::Now() >= timeout) {
			std::string description;
			const auto value_ptr = getter(&description);
			if (value_ptr)
				return *value_ptr;
			throw WebDriverException(detail::Fmt()
				<< "Timeout after " << timeoutMs << "ms of waiting, last attempt returned: "
				<< description
				);
		}
		detail::Sleep(intervalMs);
	}
}

template<typename Value, typename Getter>
std::unique_ptr<Value> TryToCallGetter(Getter getter, std::string* description) {
	std::unique_ptr<Value> value_ptr;
	try {
		value_ptr.reset(new Value(getter()));
	} catch (const std::exception& e) {
		if (description)
			*description = e.what();
	}
	return value_ptr;
}

} // namespace detail

// Waits for a value returned by a supplied getter.
// Returns that value or throws exception on timeout.
// Getter is a function or function-like object that returns some copyable value.
template<typename Getter>
auto WaitForValue(
	Getter getter,
	Duration timeoutMs = 5000,
	Duration intervalMs = 50
	) -> decltype(getter()) {
	typedef decltype(getter()) Value;
	return detail::Wait<Value>(
		[&getter](std::string* description) {
			return detail::TryToCallGetter<Value>(getter, description);
		},
		timeoutMs, intervalMs);
}

// Waits until a truthy value is returned by a supplied getter.
// Returns that value or throws exception on timeout.
// Getter is a function or function-like object that returns some copyable value.
template<typename Getter>
auto WaitUntil(
	Getter getter,
	Duration timeoutMs = 5000,
	Duration intervalMs = 50
	) -> decltype(getter()) {
	typedef decltype(getter()) Value;
	return detail::Wait<Value>(
		[&getter](std::string* description) -> std::unique_ptr<Value> {
			auto value_ptr = detail::TryToCallGetter<Value>(getter, description);
			if (!value_ptr || !!*value_ptr)
				return value_ptr;
			if (description)
				*description = "Value is falsy";
			value_ptr.reset();
			return value_ptr;
		}, timeoutMs, intervalMs);
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/wait_match.h
================================================
#ifndef WEBDRIVERXX_WAIT_MATCH_H
#define WEBDRIVERXX_WAIT_MATCH_H

#include "wait.h"
#include "detail/to_string.h"
#include <type_traits>

#ifdef WEBDRIVERXX_ENABLE_GMOCK_MATCHERS

#include <gmock/gmock-matchers.h>
#include <sstream>

namespace webdriverxx {
namespace detail {

template<typename T, typename M>
class GMockMatcherAdapter {
public:
	explicit GMockMatcherAdapter(::testing::Matcher<T> matcher) : matcher_(matcher) {}

	bool Apply(const T& value) const {
		return matcher_.Matches(value);
	}

	std::string DescribeMismatch(const T& value) const {
		std::ostringstream s;
		s << "Expected: ";
		matcher_.DescribeTo(&s);
		s << ", actual: ";
		ToStream(value, s);
		const auto mismatch_details = GetMismatchDetails(value);
		if (!mismatch_details.empty())
			 s << ", " << mismatch_details;
		return s.str();
	}

private:
	std::string GetMismatchDetails(const T& value) const {
		std::ostringstream s;
		matcher_.ExplainMatchResultTo(value, &s);
		return s.str();
	}

private:
	::testing::Matcher<T> matcher_;
};

} // detail

template<class T, class M>
detail::GMockMatcherAdapter<T,M> MakeMatcherAdapter(const M& matcher, typename std::enable_if<std::is_convertible<M,::testing::Matcher<T>>::value>::type* = nullptr) {
	return detail::GMockMatcherAdapter<T,M>(matcher);
};

} // namespace webdriverxx

#endif // WEBDRIVERXX_ENABLE_GMOCK_MATCHERS

namespace webdriverxx {
namespace detail {

template<typename T, typename P>
class PredicateMatcherAdapter {
public:
	explicit PredicateMatcherAdapter(P& predicate) : predicate_(&predicate) {}

	bool Apply(const T& value) const {
		return (*predicate_)(value);
	}

	std::string DescribeMismatch(const T& value) const {
		return detail::Fmt() << "Value " << ToString(value) << " does not match predicate";
	}

private:
	P* predicate_;
};

} // detail

template<typename T>
void MakeMatcherAdapter(...);

namespace detail {

template<typename T, typename M>
PredicateMatcherAdapter<T,M> SelectMakeMatcherAdapter(M& matcher, std::true_type /*no_custom_adapters*/) {
	return PredicateMatcherAdapter<T,M>(matcher);
}

template<typename T, typename M>
auto SelectMakeMatcherAdapter(const M& matcher, std::false_type /*no_custom_adapters*/) -> decltype(MakeMatcherAdapter<T>(matcher)) {
	return MakeMatcherAdapter<T>(matcher);
}

} // detail

// Waits until a value returned by a getter satisfies a supplied matcher.
// Returns that value or throws exception on timeout.
// Getter is a function or function-like object that returns some copyable value.
// Matcher can be a predicate or a Google Mock matcher (if Google Mock matchers are enabled).
template<typename Getter, typename Matcher>
auto WaitForMatch(
	Getter getter,
	Matcher matcher,
	Duration timeoutMs = 5000,
	Duration intervalMs = 50
	) -> decltype(getter()) {
	typedef decltype(getter()) Value;
	const auto& adapter = detail::SelectMakeMatcherAdapter<Value>(matcher,
		typename std::is_same<void,decltype(MakeMatcherAdapter<Value>(matcher))>::type());
	return detail::Wait<Value>([&getter, &adapter](std::string* description) -> std::unique_ptr<Value> {
			auto value_ptr = detail::TryToCallGetter<Value>(getter, description);
			if (value_ptr && !adapter.Apply(*value_ptr)) {
				if (description)
					*description = adapter.DescribeMismatch(*value_ptr);
				value_ptr.reset();
			}
			return value_ptr;
		}, timeoutMs, intervalMs);
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/webdriver.h
================================================
#ifndef WEBDRIVERXX_WEBDRIVER_H
#define WEBDRIVERXX_WEBDRIVER_H

#include "client.h"
#include "session.h"

namespace webdriverxx {

// The main class for interactions with a server. Automatically connects to a server,
// creates and deletes a session and gives access to session's API.
class WebDriver // copyable
	: public Client
	, public Session {
public:
	explicit WebDriver(
		const Capabilities& desired = Capabilities(),
		const Capabilities& required = Capabilities(),
		const std::string& url = kDefaultWebDriverUrl
		)
		: Client(url)
		, Session(CreateSession(desired, required))
	{}
};

inline
WebDriver Start(
	const Capabilities& desired, 
	const Capabilities& required = Capabilities(),
	const std::string& url = kDefaultWebDriverUrl
	)
{
	return WebDriver(desired, required, url);
}

inline
WebDriver Start(const Capabilities& desired, const std::string& url)
{
	return Start(desired, Capabilities(), url);
}

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx/window.h
================================================
#ifndef WEBDRIVERXX_WINDOW_H
#define WEBDRIVERXX_WINDOW_H

#include "types.h"
#include "conversions.h"
#include "detail/resource.h"
#include <string>

namespace webdriverxx {

class Window { // copyable
public:
	Window(const std::string& handle, const detail::Shared<detail::Resource>& resource)
		: handle_(handle)
		, resource_(resource)
	{}

	std::string GetHandle() const {
		return handle_;
	}

	Size GetSize() const {
		return resource_->GetValue<Size>("size");
	}

	const Window& SetSize(const Size& size) const {
		resource_->PostValue("size", size);
		return *this;
	}

	Point GetPosition() const {
		return resource_->GetValue<Point>("position");
	}

	const Window& SetPosition(const Point& position) const {
		resource_->PostValue("position", position);
		return *this;
	}

	const Window& Maximize() const {
		resource_->Post("maximize");
		return *this;
	}

private:
	std::string handle_;
	detail::Shared<detail::Resource> resource_;
};

} // namespace webdriverxx

#endif


================================================
FILE: include/webdriverxx.h
================================================
#ifndef WEBDRIVERXX_H
#define WEBDRIVERXX_H

#include "webdriverxx/webdriver.h"
#include "webdriverxx/browsers/chrome.h"
#include "webdriverxx/browsers/firefox.h"
#include "webdriverxx/browsers/ie.h"
#include "webdriverxx/browsers/phantom.h"
#include "webdriverxx/wait.h"
#include "webdriverxx/wait_match.h"

#endif


================================================
FILE: test/CMakeLists.txt
================================================
set(HEADER_FILES
	../include/webdriverxx.h 
	../include/webdriverxx/by.h 
	../include/webdriverxx/capabilities.h 
	../include/webdriverxx/client.h 
	../include/webdriverxx/client.inl 
	../include/webdriverxx/conversions.h 
	../include/webdriverxx/element.h 
	../include/webdriverxx/element.inl 
	../include/webdriverxx/errors.h 
	../include/webdriverxx/js_args.h 
	../include/webdriverxx/keys.h 
	../include/webdriverxx/response_status_code.h 
	../include/webdriverxx/session.h 
	../include/webdriverxx/session.inl 
	../include/webdriverxx/types.h 
	../include/webdriverxx/wait.h 
	../include/webdriverxx/wait_match.h 
	../include/webdriverxx/webdriver.h 
	../include/webdriverxx/window.h 
	../include/webdriverxx/picojson.h
	../include/webdriverxx/browsers/chrome.h
	../include/webdriverxx/browsers/firefox.h 
	../include/webdriverxx/browsers/ie.h 
	../include/webdriverxx/browsers/phantom.h
	../include/webdriverxx/detail/error_handling.h
	../include/webdriverxx/detail/factories.h 
	../include/webdriverxx/detail/factories_impl.h 
	../include/webdriverxx/detail/finder.h 
	../include/webdriverxx/detail/finder.inl 
	../include/webdriverxx/detail/http_client.h 
	../include/webdriverxx/detail/http_connection.h 
	../include/webdriverxx/detail/http_request.h 
	../include/webdriverxx/detail/keyboard.h 
	../include/webdriverxx/detail/meta_tools.h 
	../include/webdriverxx/detail/resource.h 
	../include/webdriverxx/detail/shared.h 
	../include/webdriverxx/detail/time.h 
	../include/webdriverxx/detail/to_string.h 
	../include/webdriverxx/detail/types.h 
	)

set(SOURCE_FILES
	alerts_test.cpp
	browsers_test.cpp
	capabilities_test.cpp
	conversions_test.cpp
	client_test.cpp
	element_test.cpp
	environment.h
	examples_test.cpp
	finder_test.cpp
	frames_test.cpp
	http_connection_test.cpp
	js_test.cpp
	keyboard_test.cpp
	main.cpp
	mouse_test.cpp
	resource_test.cpp
	session_test.cpp
	shared_test.cpp
	to_string_test.cpp
	wait_match_test.cpp
	wait_test.cpp
	webdriver_test.cpp
	)

file(COPY pages DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

add_definitions(-DWEBDRIVERXX_ENABLE_GMOCK_MATCHERS)

if (MSVC)
	add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX")
elseif (
	"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
	"${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"
	)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
	if (NOT ${APPLE})
		set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fsanitize=address -g -O1 -fno-omit-frame-pointer")
	endif()
endif()

# Coverage
if (UNIX)
	set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} Coverage)
	set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
		"Debug" "Release" "MinSizeRel" "RelWithDebInfo" "Coverage")
	set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG} --coverage")
	set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_DEBUG} --coverage")

	find_program(LCOV_EXECUTABLE lcov)
	find_program(GENHTML_EXECUTABLE genhtml)

	add_custom_target(coverage
		${LCOV_EXECUTABLE} --directory=. --zerocounters
		COMMAND ${CMAKE_CTEST_COMMAND} -V
		COMMAND ${LCOV_EXECUTABLE} --capture --directory=. --output-file coverage.info
		COMMAND ${GENHTML_EXECUTABLE} coverage.info --output-directory coverage
		DEPENDS ${PROJECT_NAME}
		)
endif()

include(ExternalProject)

# CURL
if (WIN32)
	externalproject_add(curl_project
		URL http://curl.haxx.se/download/curl-7.55.0.tar.gz
		PREFIX curl
		CMAKE_ARGS -DCURL_STATICLIB=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DBUILD_CURL_TESTS=OFF -DCMAKE_USE_OPENSSL=OFF -DCURL_ZLIB=OFF -DHTTP_ONLY=ON
		INSTALL_COMMAND ""
		UPDATE_COMMAND ""
	)
	externalproject_get_property(curl_project source_dir binary_dir)
	include_directories("${source_dir}/include")
	link_directories("${binary_dir}/lib")
	list(APPEND LIBS libcurl wsock32 ws2_32)
	list(APPEND DEPS curl_project)
	add_definitions(-DCURL_STATICLIB)
else()
	find_package(CURL REQUIRED)
	include_directories(${CURL_INCLUDE_DIR})
	list(APPEND LIBS ${CURL_LIBRARIES})
endif()

# Google test
externalproject_add(googletest
		PREFIX googletest
		GIT_REPOSITORY https://github.com/google/googletest
		UPDATE_COMMAND ""
)
externalproject_get_property(googletest source_dir)
include_directories("${source_dir}")
list(APPEND DEPS googletest)
list(APPEND LIBS gtest gmock)

# pthread
if (UNIX)
	set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
	find_package(Threads REQUIRED)
	list(APPEND LIBS ${CMAKE_THREAD_LIBS_INIT})
endif()

add_library(${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES})
add_dependencies(${PROJECT_NAME} ${DEPS})
target_link_libraries(${PROJECT_NAME} ${LIBS})

add_executable(${PROJECT_NAME}_test ${SOURCE_FILES} ${HEADER_FILES})
add_dependencies(${PROJECT_NAME}_test ${DEPS})
target_link_libraries(${PROJECT_NAME}_test ${LIBS})
add_test(${PROJECT_NAME}_test ${PROJECT_NAME})

if (APPLE)
	set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} SUFFIX ".dylib")
endif()

if (UNIX AND NOT APPLE)
	set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} SUFFIX ".so")
endif()

if (WIN32)
	set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} SUFFIX ".dll")
endif()

install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/lib)


================================================
FILE: test/alerts_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestAlerts : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		GetDriver().Navigate(GetTestPageUrl("alerts.html"));
	}

	TestAlerts() : driver(GetDriver()) {}

	WebDriver driver;
};

TEST_F(TestAlerts, AcceptsAlert) {
	if (IsPhantom()) return;
	driver.Execute("alert('abc')");
	driver.AcceptAlert();
}

TEST_F(TestAlerts, DismissesAlert) {
	if (IsPhantom()) return;
	driver.Execute("alert('abc')");
	driver.DismissAlert();
}

TEST_F(TestAlerts, GetsAlertText) {
	if (IsPhantom()) return;
	driver.Execute("alert('abc')");
	ASSERT_EQ("abc", driver.GetAlertText());
	driver.DismissAlert();
}

TEST_F(TestAlerts, SendsKeysToAlert) {
	if (IsPhantom()) return;
	driver.Execute("result = prompt('abc')");
	driver.SendKeysToAlert("def");
	driver.AcceptAlert();
	ASSERT_EQ("def", driver.Eval<std::string>("return result"));
}

TEST_F(TestAlerts, DismissesSendedKeys) {
	if (IsPhantom()) return;
	driver.Execute("result = prompt('abc')");
	driver.SendKeysToAlert("def");
	driver.DismissAlert();
	ASSERT_FALSE(driver.Eval<bool>("return result"));
}

} // namespace test


================================================
FILE: test/browsers_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/browsers/chrome.h>
#include <webdriverxx/browsers/firefox.h>
#include <webdriverxx/browsers/ie.h>
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

TEST(Firefox, WithTheSimplestSyntax) {
	if (!TestRealBrowsers()) return;
	auto ff = Start(Firefox());
}

TEST(Firefox, WithCustomUrl) {
	if (!TestRealBrowsers()) return;
	auto ff = Start(Firefox(), kDefaultWebDriverUrl);
}

TEST(Firefox, WithDefaultCapabilities) {
	if (!TestRealBrowsers()) return;
	auto defaults = Capabilities().SetProxy(DirectConnection());
	auto ff = Start(Firefox(defaults));
}

TEST(Firefox, HasCapabilitiesProperties) {
	if (!TestRealBrowsers()) return;
	auto ff = Start(Firefox().SetProxy(DirectConnection()));
}

TEST(Firefox, ConvertsToJson) {
	auto ff = Firefox()
		.SetLoggingPrefs(LoggingPrefs().SetLevel(log_level::Warning))
		.SetFirefoxBinary("abc");
	const auto json = ToJson(ff);
	const auto c = FromJson<Capabilities>(json);
	const auto logging = c.Get<JsonObject>("loggingPrefs");
	ASSERT_EQ(browser::Firefox, c.GetBrowserName());
	ASSERT_EQ("WARNING", logging.Get<std::string>("driver"));
	ASSERT_EQ("abc", c.Get<std::string>("firefox_binary"));
}

TEST(InternetExplorer, ConvertsToJson) {
	auto ie = InternetExplorer();
	const auto json = ToJson(ie);
	const auto c = FromJson<Capabilities>(json);
	ASSERT_EQ(browser::InternetExplorer, c.GetBrowserName());
}

TEST(Chrome, ConvertsToJson) {
	auto gc = Chrome();
	const auto json = ToJson(gc);
	const auto c = FromJson<Capabilities>(json);
	ASSERT_EQ(browser::Chrome, c.GetBrowserName());
}

} // namespace test


================================================
FILE: test/capabilities_test.cpp
================================================
#include <webdriverxx/capabilities.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

TEST(Capabilities, AllowsToSetAndGetCustomValues) {
	Capabilities c;
	c.Set("int", 123);
	c.Set("double", 456.7);
	c.Set("string", "abc");
	c.Set("bool", true);
	ASSERT_EQ(123, c.Get<int>("int"));
	ASSERT_EQ(456.7, c.Get<double>("double"));
	ASSERT_EQ("abc", c.Get<std::string>("string"));
	ASSERT_EQ(true, c.Get<bool>("bool"));
}

TEST(Capabilities, ConvertibleToJson) {
	Capabilities c;
	c.Set("int", 123);
	c.Set("string", "abc");
	const auto json = ToJson(c);
	const auto c_copy = FromJson<Capabilities>(json);
	ASSERT_EQ(123, c_copy.Get<int>("int"));
	ASSERT_EQ("abc", c_copy.Get<std::string>("string"));
}

TEST(Capabilities, AllowsToSetProxy) {
	Capabilities().SetProxy(DirectConnection());
	Capabilities().SetProxy(AutodetectProxy());
	Capabilities().SetProxy(SystemProxy());
	Capabilities().SetProxy(FtpProxy("127.0.0.1:3128").SetNoProxyFor("custom.host"));
	Capabilities().SetProxy(HttpProxy("127.0.0.1:3128").SetNoProxyFor("custom.host"));
	Capabilities().SetProxy(SslProxy("127.0.0.1:3128").SetNoProxyFor("custom.host"));
	Capabilities().SetProxy(SocksProxy("127.0.0.1:3128")
		.SetUsername("user").SetPassword("12345").SetNoProxyFor("custom.host")
		);
	Capabilities().SetProxy(AutomaticProxyFromUrl("http://some.url"));
}

TEST(Capabilities, ConvertsProxyToJson) {
	Capabilities c;
	c.SetProxy(SocksProxy("127.0.0.1:3128").SetUsername("user")
		.SetPassword("12345").SetNoProxyFor("custom.host"));
	const auto json = ToJson(c);
	const auto c_copy = FromJson<Capabilities>(json);
	const auto proxy = FromJson<JsonObject>(c_copy.Get<picojson::value>("proxy"));
	ASSERT_EQ("manual", proxy.Get<std::string>("proxyType"));
	ASSERT_EQ("127.0.0.1:3128", proxy.Get<std::string>("socksProxy"));
	ASSERT_EQ("user", proxy.Get<std::string>("socksUsername"));
	ASSERT_EQ("12345", proxy.Get<std::string>("socksPassword"));
	ASSERT_EQ("custom.host", proxy.Get<std::string>("noProxy"));
}

} // namespace test


================================================
FILE: test/client_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/client.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestClient : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		client = new Client(GetWebDriverUrl());
	}

	static void TearDownTestCase() {
		delete client;
		client = 0;
	}

	static Client* client;
};

Client* TestClient::client = 0;

TEST_F(TestClient, GetsStatus) {
	picojson::object status = client->GetStatus();
	ASSERT_TRUE(status["build"].is<picojson::object>());
	ASSERT_TRUE(status["os"].is<picojson::object>());
}

TEST_F(TestClient, GetsSessions) {
	client->GetSessions();
}

TEST_F(TestClient, CreatesSession) {
 	Parameters params = GetParameters();
	client->CreateSession(params.desired, params.required);
}

} // namespace test


================================================
FILE: test/conversions_test.cpp
================================================
#include <webdriverxx/conversions.h>
#include <gtest/gtest.h>
#include <vector>
#include <list>

namespace test {

using namespace webdriverxx;

TEST(ToJson, ConvertsIntegralTypes) {
	ASSERT_EQ(123, ToJson(123).get<double>());
	int i = 123;
	ASSERT_EQ(123, ToJson(i).get<double>());
	ASSERT_EQ(123, ToJson(static_cast<const int>(123)).get<double>());
	ASSERT_EQ(123.5, ToJson(123.5).get<double>());
	ASSERT_TRUE(ToJson(true).get<bool>());
	ASSERT_FALSE(ToJson(false).get<bool>());
}

TEST(ToJson, ConvertsStrings) {
	ASSERT_EQ("abc", ToJson("abc").get<std::string>());
	std::string s("abc");
	ASSERT_EQ("abc", ToJson(s).get<std::string>());
	ASSERT_EQ("abc", ToJson(s.c_str()).get<std::string>());
	ASSERT_EQ("abc", ToJson(const_cast<char*>(s.c_str())).get<std::string>());
	char a[] = "abc";
	ASSERT_EQ("abc", ToJson(a).get<std::string>());
}

TEST(ToJson, ConvertsIterables) {
	int i[] = { 123, 456, 789 };
	const auto ji = ToJson(i);
	ASSERT_TRUE(ji.is<picojson::array>());
	ASSERT_EQ(3u, ji.get<picojson::array>().size());
	ASSERT_EQ(789, ji.get<picojson::array>()[2].get<double>());

	const char* s[] = { "abc", "def", "ghi" };
	const auto js = ToJson(s);
	ASSERT_TRUE(js.is<picojson::array>());
	ASSERT_EQ(3u, js.get<picojson::array>().size());
	ASSERT_EQ("ghi", js.get<picojson::array>()[2].get<std::string>());

	std::vector<int> v(std::begin(i), std::end(i));
	const auto jv = ToJson(v);
	ASSERT_TRUE(jv.is<picojson::array>());
	ASSERT_EQ(3u, jv.get<picojson::array>().size());
	ASSERT_EQ(789, jv.get<picojson::array>()[2].get<double>());

	std::list<int> l(std::begin(i), std::end(i));
	const auto jl = ToJson(l);
	ASSERT_TRUE(jl.is<picojson::array>());
	ASSERT_EQ(3u, jl.get<picojson::array>().size());
	ASSERT_EQ(789, jl.get<picojson::array>()[2].get<double>());

	int ii[2][3] = { { 1, 2, 3 }, { 4, 5, 123 } };
	const auto jii = ToJson(ii);
	ASSERT_TRUE(jii.is<picojson::array>());
	ASSERT_EQ(2u, jii.get<picojson::array>().size());
	ASSERT_TRUE(jii.get<picojson::array>()[0].is<picojson::array>());
	ASSERT_TRUE(jii.get<picojson::array>()[1].is<picojson::array>());
	ASSERT_EQ(3u, jii.get<picojson::array>()[0].get<picojson::array>().size());
	ASSERT_EQ(3u, jii.get<picojson::array>()[1].get<picojson::array>().size());
	ASSERT_EQ(123, jii.get<picojson::array>()[1].get<picojson::array>()[2].get<double>());

	std::vector<int> e;
	const auto je = ToJson(e);
	ASSERT_TRUE(je.is<picojson::array>());
	ASSERT_EQ(0u, je.get<picojson::array>().size());
}

namespace {
namespace custom {

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

picojson::value CustomToJson(const Object& value) {
	return JsonObject()
		.Set("string", value.string)
		.Set("number", value.number);
}

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

} // namespace custom
} // namespace

TEST(ToJson, ConvertsCustomObjects) {
	custom::Object o = { "abc", 123 };
	const auto jo = ToJson(o);
	ASSERT_TRUE(jo.is<picojson::object>());
	ASSERT_EQ(123, jo.get("number").get<double>());
	ASSERT_EQ("abc", jo.get("string").get<std::string>());

	const custom::Object os[] = { { "abc", 123 }, { "def", 456 } };
	const auto jos = ToJson(os);
	ASSERT_TRUE(jos.is<picojson::array>());
	ASSERT_EQ(2u, jos.get<picojson::array>().size());
	ASSERT_TRUE(jos.get<picojson::array>()[0].is<picojson::object>());
	ASSERT_EQ(456, jos.get<picojson::array>()[1].get("number").get<double>());
}

picojson::value J(const std::string& json) {
	picojson::value result;
	picojson::parse(result, json.begin(), json.end(), nullptr);
	return result;
}

TEST(FromJson, ConvertsIntegralTypes) {
	ASSERT_EQ(123, FromJson<int>(J("123")));
	ASSERT_EQ(123.5, FromJson<double>(J("123.5")));
	ASSERT_EQ(123u, FromJson<unsigned>(J("123")));
	ASSERT_FALSE(FromJson<bool>(J("false")));
	ASSERT_TRUE(FromJson<bool>(J("true")));
}

TEST(FromJson, ConvertsStrings) {
	ASSERT_EQ("abc", FromJson<std::string>(J("\"abc\"")));
}

TEST(FromJson, ConvertsIterables) {
	const auto i = FromJson<std::vector<int>>(J("[ 123, 456, 789 ]"));
	ASSERT_EQ(3u, i.size());
	ASSERT_EQ(123, i[0]);
	ASSERT_EQ(789, i[2]);

	const auto s = FromJson<std::list<std::string>>(J("[ \"abc\", \"def\" ]"));
	ASSERT_EQ(2u, s.size());
	ASSERT_EQ("abc", s.front());
	ASSERT_EQ("def", s.back());

	const auto e = FromJson<std::vector<int>>(J("[]"));
	ASSERT_EQ(0u, e.size());

	const auto ii = FromJson<std::vector<std::list<int>>>(J("[ [ 123, 456 ], [ 789 ] ]"));
	ASSERT_EQ(2u, ii.size());
	ASSERT_EQ(2u, ii[0].size());
	ASSERT_EQ(1u, ii[1].size());
	ASSERT_EQ(456, ii[0].back());
	ASSERT_EQ(789, ii[1].front());
}

TEST(FromJson, ConvertsCustomObjects) {
	const auto o = FromJson<custom::Object>(J("{ \"number\": 123, \"string\": \"abc\" }"));
	ASSERT_EQ(123, o.number);
	ASSERT_EQ("abc", o.string);

	const auto os = FromJson<std::vector<custom::Object>>(J(
		"[ { \"number\": 123, \"string\": \"abc\" },"
		"{ \"number\": 456, \"string\": \"def\" } ]"
		));
	ASSERT_EQ(2u, os.size());
	ASSERT_EQ(123, os[0].number);
	ASSERT_EQ("abc", os[0].string);
	ASSERT_EQ(456, os[1].number);
	ASSERT_EQ("def", os[1].string);
}

} // namespace test


================================================
FILE: test/element_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestElement : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		GetDriver().Navigate(GetTestPageUrl("element.html"));
	}

	TestElement() : driver(GetDriver()) {}

	WebDriver driver;
};

TEST_F(TestElement, CanBeClicked) {
	driver.FindElement(ByTag("input")).Click();
}

// TODO: Submit

TEST_F(TestElement, GetsText) {
	ASSERT_EQ("Some text", driver.FindElement(ById("element_with_text")).GetText());
}

TEST_F(TestElement, CanBeCleared) {
	Element e = driver.FindElement(ByTag("input"));
	e.SendKeys("abc");
	ASSERT_NE("", e.GetAttribute("value"));
	e.Clear();
	ASSERT_EQ("", e.GetAttribute("value"));
}

TEST_F(TestElement, GetsTagName) {
	ASSERT_EQ("div", driver.FindElement(ByTag("div")).GetTagName());
}

// TODO: IsEnabled
// TODO: IsSelected

TEST_F(TestElement, GetsAttributes) {
	Element e = driver.FindElement(ById("div_with_attributes"));
	ASSERT_EQ("div_with_attributes", e.GetAttribute("id"));
	ASSERT_EQ("test value", e.GetAttribute("test"));
}

TEST_F(TestElement, IsEqualToOtherElement) {
	Element e = driver.FindElement(ById("first_div"));
	Element other = driver.FindElement(ById("first_div"));
	ASSERT_TRUE(e.Equals(other));
	ASSERT_TRUE(e == other);
}

TEST_F(TestElement, IsNotEqualToOtherElement) {
	Element e = driver.FindElement(ById("first_div"));
	Element other = driver.FindElement(ById("second_div"));
	ASSERT_TRUE(!e.Equals(other));
	ASSERT_TRUE(e != other);
}

TEST_F(TestElement, HasStrictWeakOrdering) {
	Element a = driver.FindElement(ById("first_div"));
	Element b = driver.FindElement(ById("second_div"));
	Element c = driver.FindElement(ById("third_div"));
	Element a2 = a;
	if (c < b) std::swap(b, c);
	if (b < a) std::swap(a, b);
	if (c < b) std::swap(b, c);
	ASSERT_FALSE(a < a2);
	ASSERT_FALSE(a2 < a);
	ASSERT_TRUE(a < b && !(b < a));
	ASSERT_TRUE(a < b && b < c && a < c);
}

TEST_F(TestElement, GetsIsDisplayed) {
	ASSERT_TRUE(driver.FindElement(ById("visible")).IsDisplayed());
	ASSERT_FALSE(driver.FindElement(ById("hidden")).IsDisplayed());
}

TEST_F(TestElement, GetsLocation) {
	driver.FindElement(ById("visible")).GetLocation();
	driver.FindElement(ById("visible")).GetLocationInView();
}

TEST_F(TestElement, GetsSize) {
	Size size = driver.FindElement(ById("visible")).GetSize();
	ASSERT_NE(0, size.width);
	ASSERT_NE(0, size.height);
}

TEST_F(TestElement, GetsCssProperty) {
	ASSERT_EQ("none", driver.FindElement(ById("hidden")).GetCssProperty("display"));
}

} // namespace test


================================================
FILE: test/environment.h
================================================
#ifndef WEBDRIVERXX_ENVIRONMENT_H
#define WEBDRIVERXX_ENVIRONMENT_H

#include <webdriverxx/webdriver.h>
#include <webdriverxx/capabilities.h>
#include <gtest/gtest.h>
#include <string>
#include <algorithm>

namespace test {

const char* const kDefaultTestWebDriverUrl = "http://localhost:7777/";
const char* const kDefaultTestPagesUrl = "http://localhost:8080/";

struct Parameters {
	std::string web_driver_url;
	webdriverxx::Capabilities required;
	webdriverxx::Capabilities desired;
	std::string test_pages_url;
	bool test_real_browsers;

	Parameters()
		: web_driver_url(kDefaultTestWebDriverUrl)
		, test_pages_url(kDefaultTestPagesUrl)
		, test_real_browsers(false)
	{}
};

class Environment : public ::testing::Environment {
public:
	static Environment& Instance() {
		return *instance_;
	}

	explicit Environment(const Parameters& parameters)
		: driver_(0)
		, parameters_(parameters)
	{}

	webdriverxx::WebDriver& GetDriver() {
		return driver_ ? *driver_ : GetFreshDriver();
	}

	webdriverxx::WebDriver& GetFreshDriver() {
		DeleteDriver();
		driver_ = new webdriverxx::WebDriver(CreateDriver());
		return *driver_;
	}

	webdriverxx::WebDriver CreateDriver() {
		return webdriverxx::WebDriver(
			parameters_.desired,
			parameters_.required,
			parameters_.web_driver_url
			);
	}

	std::string GetWebDriverUrl() const { return parameters_.web_driver_url; }

	Parameters GetParameters() const { return parameters_; }

	std::string GetTestPageUrl(const std::string& page_name) const {
		std::string url = parameters_.test_pages_url;
		if (!url.empty() && url[url.length() - 1] != '/')
			url += "/";
		url += page_name;
		return url;
	}

private:
	void SetUp() {
		instance_ = this;
	}

	void TearDown() {
		instance_ = 0;
		DeleteDriver();
	}

	void DeleteDriver() {
		delete driver_;
		driver_ = 0;
	}

private:
	static Environment* instance_;
	webdriverxx::WebDriver* driver_;
	Parameters parameters_;
};

inline Parameters GetParameters() { return Environment::Instance().GetParameters(); }
inline std::string GetWebDriverUrl() { return Environment::Instance().GetWebDriverUrl(); }
inline std::string GetTestPageUrl(const std::string& page_name) { return Environment::Instance().GetTestPageUrl(page_name); }
inline webdriverxx::WebDriver& GetDriver() { return Environment::Instance().GetDriver(); }
inline webdriverxx::WebDriver& GetFreshDriver() { return Environment::Instance().GetFreshDriver(); }
inline webdriverxx::WebDriver CreateDriver() { return Environment::Instance().CreateDriver(); }
inline bool TestRealBrowsers() { return GetParameters().test_real_browsers; }
inline std::string GetBrowserName() { return GetDriver().GetCapabilities().GetBrowserName(); }
inline bool IsFirefox() { return GetBrowserName() == webdriverxx::browser::Firefox; }
inline bool IsPhantom() { return GetBrowserName() == webdriverxx::browser::Phantom; }

} // namespace test

#endif


================================================
FILE: test/examples_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#ifndef WEBDRIVERXX_ENABLE_GMOCK_MATCHERS
#define WEBDRIVERXX_ENABLE_GMOCK_MATCHERS
#endif
#include <webdriverxx/wait_match.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestExamples : public ::testing::Test {
protected:
	TestExamples() : driver(GetDriver()) {}

	void StopNavigation() {
		WaitForMatch([this] {
			return driver.Navigate(GetTestPageUrl("non_existing.html")).GetUrl();
		}, ::testing::HasSubstr("non_existing"));
	}

	WebDriver driver;
};

TEST_F(TestExamples, QuickExample) {
	driver
		.Navigate("http://google.com")
		.FindElement(ByCss("input[name=q]"))
		.SendKeys("Hello, world!")
		.Submit();

	StopNavigation(); // Firefox doesn't perform navigation right after Submit.
}

TEST_F(TestExamples, ImplicitWait) {
	driver.SetImplicitTimeoutMs(0);
	try {
		Element element = driver.FindElement(ByName("akela"));
		FAIL();
	} catch (const std::exception&) {}
}

TEST_F(TestExamples, ExplicitWait1) {
	auto find_element = [&]{ return driver.FindElement(ById("async_loaded")); };
	try {
		int timeout = 0;
		Element element = WaitForValue(find_element, timeout);
		FAIL();
	} catch (const std::exception&) {}
}

TEST_F(TestExamples, ExplicitWait2) {
	auto element_is_selected = [&]{
		return driver.FindElement(ById("async_loaded")).IsSelected();
	};
	try {
		int timeout = 0;
		WaitUntil(element_is_selected, timeout);
		FAIL();
	} catch (const std::exception&) {}
}

TEST_F(TestExamples, UseGmockMatchers) {
	driver.Navigate(GetTestPageUrl("redirect.html"));
	auto url = [&]{ return driver.GetUrl(); };
	using namespace ::testing;
	WaitForMatch(url, HasSubstr("target"));
	StopNavigation();	
}

} // namespace test


================================================
FILE: test/finder_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestFinder : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		WebDriver& driver = GetDriver();
		driver.Navigate(GetTestPageUrl("finder.html"));
		driver.FindElement(ById("finder_loaded"));
		driver.SetImplicitTimeoutMs(0);
	}

	static void TearDownTestCase() {
		WebDriver& driver = GetDriver();
		driver.SetImplicitTimeoutMs(1000);
	}

	TestFinder() : driver(GetDriver()) {}

	WebDriver driver;
};

TEST_F(TestFinder, CanFindElement) {
	driver.FindElement(ById("test_id"));
}

TEST_F(TestFinder, ThrowsIfElementNotFound) {
	ASSERT_THROW(driver.FindElement(ById("non_existing")), WebDriverException);
}

TEST_F(TestFinder, CanFindMoreThanOneElement) {
	ASSERT_TRUE(0u < driver.FindElements(ByTag("div")).size());
}

TEST_F(TestFinder, ReturnsZeroIfElementsNotFound) {
	ASSERT_EQ(0u, driver.FindElements(ById("non_existing")).size());
}

TEST_F(TestFinder, FindsElementById) {
	driver.FindElement(ById("test_id"));
	ASSERT_EQ(0u, driver.FindElements(ById("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByClassName) {
	driver.FindElement(ByClass("test_class"));
	ASSERT_EQ(0u, driver.FindElements(ByClass("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByCssSelector) {
	driver.FindElement(ByCss("body div#css_selectable"));
	ASSERT_EQ(0u, driver.FindElements(ByCss("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByName) {
	driver.FindElement(ByName("test_name"));
	ASSERT_EQ(0u, driver.FindElements(ByName("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByLinkText) {
	driver.FindElement(ByLinkText("test link text"));
	ASSERT_EQ(0u, driver.FindElements(ByLinkText("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByPartialLinkText) {
	driver.FindElement(ByPartialLinkText("link text"));
	ASSERT_EQ(0u, driver.FindElements(ByPartialLinkText("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByTagName) {
	driver.FindElement(ByTag("body"));
	ASSERT_EQ(0u, driver.FindElements(ByTag("non_existing")).size());
}

TEST_F(TestFinder, FindsElementByXPath) {
	driver.FindElement(ByXPath("//div"));
	ASSERT_EQ(0u, driver.FindElements(ByXPath("//non_existing")).size());
}

TEST_F(TestFinder, OfElementFindsInnerElement) {
	Element outer = driver.FindElement(ById("outer"));
	outer.FindElement(ById("inner"));
}

TEST_F(TestFinder, OfElementDoesNotFindItself) {
	Element outer = driver.FindElement(ById("outer"));
	ASSERT_EQ(0u, outer.FindElements(ById("outer")).size());
}

TEST_F(TestFinder, OfElementDoesNotFindNonExistingInnerElements) {
	Element outer = driver.FindElement(ById("outer"));
	driver.FindElement(ById("next_after_outer"));
	ASSERT_THROW(outer.FindElement(ById("next_after_outer")), WebDriverException);
	ASSERT_EQ(0u, outer.FindElements(ById("next_after_outer")).size());
}

TEST_F(TestFinder, OfElementFindsMoreThanOneInnerElement) {
	ASSERT_EQ(2u, driver.FindElement(ById("outer")).FindElements(ByTag("div")).size());
}

} // namespace test


================================================
FILE: test/frames_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestFrames : public ::testing::Test {
protected:
	TestFrames()
		: driver(GetDriver())
		, url(GetTestPageUrl("frames.html"))
	{}

	void SetUp()
	{
		driver.Navigate(url);
	}

	WebDriver driver;
	std::string url;
};

TEST_F(TestFrames, OnTopFrameByDefault) {
	ASSERT_EQ("top_frame", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToFrameByNumber) {
	driver.SetFocusToFrame(1);
	ASSERT_EQ("frame3", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToFrameByName) {
	driver.SetFocusToFrame("frame3_name");
	ASSERT_EQ("frame3", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToFrameByElement) {
	std::vector<Element> frames = driver.FindElements(ByTag("iframe"));
	ASSERT_EQ(2u, frames.size());
	driver.SetFocusToFrame(frames[1]);
	ASSERT_EQ("frame3", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToDefaultFrame) {
	driver.SetFocusToFrame(1);
	driver.SetFocusToDefaultFrame();
	ASSERT_EQ("top_frame", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToDeepFrames) {
	driver.SetFocusToFrame(0).SetFocusToFrame(1);
	ASSERT_EQ("frame2", driver.FindElement(ById("tag")).GetAttribute("value"));
}

TEST_F(TestFrames, CanSwitchToParentFrame) {
	if (IsPhantom()) return; // Not supported in PhantomJS 1.9.7
	driver.SetFocusToFrame(0).SetFocusToFrame(1)
		.SetFocusToParentFrame().SetFocusToParentFrame();
	ASSERT_EQ("top_frame", driver.FindElement(ById("tag")).GetAttribute("value"));
}

} // namespace test


================================================
FILE: test/http_connection_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/detail/http_connection.h>
#include <gtest/gtest.h>
#include <string>

namespace test {

using namespace webdriverxx;
using namespace webdriverxx::detail;

TEST(HttpConnection, CanBeCreated) {
	HttpConnection connection;
}

TEST(HttpConnection, GetsPage) {
	HttpConnection connection;
	HttpResponse response = connection.Get(GetWebDriverUrl() + "status");
	ASSERT_EQ(200, response.http_code);
	ASSERT_TRUE(!response.body.empty());
}

TEST(HttpConnection, ThrowsExceptionIfPortIsClosed) {
	HttpConnection connection;
	const char *const kUrlWithClosedPort = "http://127.0.0.1:7778/";
	ASSERT_THROW(connection.Get(kUrlWithClosedPort), WebDriverException);
}

} // namespace test


================================================
FILE: test/js_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <gtest/gtest.h>
#include <list>

namespace test {

using namespace webdriverxx;

class TestJsExecutor : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		GetDriver().Navigate(GetTestPageUrl("js.html"));
	}

	TestJsExecutor() : driver(GetDriver()) {}

	WebDriver driver;
};

TEST_F(TestJsExecutor, ExecutesSimpleScript) {
	driver.Execute("document.title = 'abc'");
	ASSERT_EQ("abc", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassStringArgument) {
	driver.Execute("document.title = arguments[0]", JsArgs() << std::string("abc"));
	ASSERT_EQ("abc", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassStringLiteralArgument) {
	driver.Execute("document.title = arguments[0]", JsArgs() << "abc");
	ASSERT_EQ("abc", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassNumberArgument) {
	driver.Execute("document.title = String(arguments[0] + 21)", JsArgs() << 21);
	ASSERT_EQ("42", driver.GetTitle());
	driver.Execute("document.title = String(arguments[0] + 21)", JsArgs() << 21.5);
	ASSERT_EQ("42.5", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassBooleanArgument) {
	driver.Execute("a = arguments; document.title = [ typeof(a[0]), a[0], typeof(a[1]), a[1] ].join(',')",
		JsArgs() << true << false);
	ASSERT_EQ("boolean,true,boolean,false", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassMoreThanOneArgument) {
	driver.Execute("document.title = arguments[0] + ',' + arguments[1]",
		JsArgs() << "abc" << "def");
	ASSERT_EQ("abc,def", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassElement) {
	Element e = driver.FindElement(ByTag("input")).Clear();
	ASSERT_EQ("", e.GetAttribute("value"));
	driver.Execute("arguments[0].value = arguments[1]",
		JsArgs() << e << "abc");
	ASSERT_EQ("abc", e.GetAttribute("value"));
}

TEST_F(TestJsExecutor, CanPassArray) {
	std::vector<int> numbers;
	numbers.push_back(123);
	numbers.push_back(321);
	driver.Execute("document.title = arguments[0][0] + arguments[0][1]",
		JsArgs() << numbers);
	ASSERT_EQ("444", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassOtherContainers) {
	std::list<int> numbers;
	numbers.push_back(123);
	numbers.push_back(321);
	driver.Execute("document.title = arguments[0][0] + arguments[0][1]",
		JsArgs() << numbers);
	ASSERT_EQ("444", driver.GetTitle());
}

TEST_F(TestJsExecutor, CanPassCArray) {
	const char* colors[] = { "red", "green", "blue" };
	driver.Execute("document.title = arguments[0].reverse().join(', ')",
		JsArgs() << colors);
	ASSERT_EQ("blue, green, red", driver.GetTitle());
}

namespace {
namespace custom {

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

picojson::value CustomToJson(const Object& value) {
	return JsonObject()
		.Set("string", value.string)
		.Set("number", value.number);
}

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

} // namespace custom
} // namespace

TEST_F(TestJsExecutor, CanPassCustomObject) {
	custom::Object o = { "abc", 123 };
	driver.Execute("o = arguments[0]; document.title = (o.string + 'def') + (o.number + 1)",
		JsArgs() << o);
	ASSERT_EQ("abcdef124", driver.GetTitle());
}

///////////////////////////////////////////////////////////////////////////

TEST_F(TestJsExecutor, EvalsString) {
	ASSERT_EQ("abc", driver.Eval<std::string>("return 'abc'"));
}

TEST_F(TestJsExecutor, EvalsNumber) {
	ASSERT_EQ(123, driver.Eval<int>("return 123"));
	ASSERT_EQ(123.5, driver.Eval<double>("return 123.5"));
}

TEST_F(TestJsExecutor, EvalsBoolean) {
	ASSERT_TRUE(true == driver.Eval<bool>("return true"));
	ASSERT_TRUE(false == driver.Eval<bool>("return false"));
}

TEST_F(TestJsExecutor, EvalsElement) {
	Element e = driver.FindElement(ByTag("input"));
	ASSERT_EQ(e, driver.Eval<Element>("return document.getElementsByTagName('input')[0]"));
}

TEST_F(TestJsExecutor, EvalsCustomObject) {
	custom::Object o = driver.Eval<custom::Object>("return { string: 'abc', number: 123 }");
	ASSERT_EQ("abc", o.string);
	ASSERT_EQ(123, o.number);
}

TEST_F(TestJsExecutor, EvalsArrayOfStrings) {
	std::vector<std::string> v = driver.Eval<std::vector<std::string>>(
		"return [ 'abc', 'def' ]"
		);
	ASSERT_EQ(2u, v.size());
	ASSERT_EQ("abc", v[0]);
	ASSERT_EQ("def", v[1]);
}

TEST_F(TestJsExecutor, EvalsArrayOfNumbers) {
	std::vector<int> v = driver.Eval<std::vector<int>>(
		"return [ 123, 456 ]"
		);
	ASSERT_EQ(2u, v.size());
	ASSERT_EQ(123, v[0]);
	ASSERT_EQ(456, v[1]);
}

///////////////////////////////////////////////////////////////////////////

// Makes asynchronous script from synchronous one
std::string AsyncScript(const std::string& script) {
	return std::string()
		+ "var args = Array.prototype.slice.call(arguments, 0);"
		+ "var callback = args.pop();"
		+ "setTimeout(function(){"
			+ "var result = (function(){" + script + "}).apply(this, args);"
			+ "callback(result)"
		+ "}, 0);"
		;
}

TEST_F(TestJsExecutor, ExecutesSimpleAsyncScript) {
	if (IsPhantom()) return; // Crashes PhantomJS 1.9.7
	driver.ExecuteAsync(AsyncScript("document.title = 'abc'"));
	ASSERT_EQ("abc", driver.GetTitle());
}

TEST_F(TestJsExecutor, PassesArgumentsToAsyncScript) {
	if (IsPhantom()) return; // Crashes PhantomJS 1.9.7
	driver.ExecuteAsync(AsyncScript("document.title = JSON.stringify(Array.prototype.slice.call(arguments, 0))"),
		JsArgs() << std::string("abc") << 123 << true);
	ASSERT_EQ("[\"abc\",123,true]", driver.GetTitle());
}

TEST_F(TestJsExecutor, ReturnsValueFromAsyncScript) {
	if (IsPhantom()) return; // Crashes PhantomJS 1.9.7
	ASSERT_EQ(123, driver.EvalAsync<int>(AsyncScript("return 123")));
}

TEST_F(TestJsExecutor, ReturnsElementFromAsyncScript) {
	if (IsPhantom()) return; // Crashes PhantomJS 1.9.7
	Element e = driver.FindElement(ByTag("input"));
	ASSERT_EQ(e, driver.EvalAsync<Element>(AsyncScript(
		"return document.getElementsByTagName('input')[0]")));
}

} // namespace test


================================================
FILE: test/keyboard_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/webdriver.h>
#include <webdriverxx/keys.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestKeyboard : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		GetDriver().Navigate(GetTestPageUrl("keyboard.html"));
	}

	TestKeyboard() : driver(GetDriver()) {}

	WebDriver driver;
};

TEST_F(TestKeyboard, SendsKeysToElement) {
	Element e = driver.FindElement(ByName("first"));
	e.Clear()
		.SendKeys("abc")
		.SendKeys(keys::Left).SendKeys(keys::Left).SendKeys(keys::Left)
		.SendKeys("def")
		;
	ASSERT_EQ("abcdef", e.GetAttribute("value"));
}

TEST_F(TestKeyboard, SendsShortcuts) {
	Element e = driver.FindElement(ByName("first"));
	e.Clear()
		.SendKeys(Shortcut() << keys::Shift << "a" << "bc")
		.SendKeys("def")
		;
	ASSERT_EQ("ABCdef", e.GetAttribute("value"));
}

TEST_F(TestKeyboard, SendsKeysToActiveElement) {
	Element first = driver.FindElement(ByName("first"));
	Element second = driver.FindElement(ByName("second"));
	first.Click().Clear();
	driver.SendKeys("abc");
	second.Click().Clear();
	driver.SendKeys("def");
	ASSERT_EQ("abc", first.GetAttribute("value"));
	ASSERT_EQ("def", second.GetAttribute("value"));
}

} // namespace test


================================================
FILE: test/main.cpp
================================================
#include "environment.h"
#include <gtest/gtest.h>
#include <string>

namespace test {

Environment* Environment::instance_ = 0;

bool IsCommandLineArgument(const std::string& arg, const char* name) {
	return arg.find(std::string("--") + name) == 0;
}

std::string GetCommandLineArgumentValue(const std::string& arg) {
	const size_t pos = arg.find('=');
	return pos == std::string::npos ? std::string() : arg.substr(pos + 1);
}

Parameters ParseParameters(int argc, char **argv) {
	Parameters result;
	for(int i = 1; i < argc; ++i) {
		const std::string arg = argv[i];
		if (IsCommandLineArgument(arg, "browser")) {
			result.web_driver_url = webdriverxx::kDefaultWebDriverUrl;
			const std::string browser_name = GetCommandLineArgumentValue(arg);
			result.desired.Set("browserName", browser_name);
		} else if (IsCommandLineArgument(arg, "pages")) {
			result.test_pages_url = GetCommandLineArgumentValue(arg);
		} else if (IsCommandLineArgument(arg, "webdriver")) {
			result.web_driver_url = GetCommandLineArgumentValue(arg);
		} else if (IsCommandLineArgument(arg, "test_real_browsers")) {
			result.test_real_browsers = true;
		}
	}
	return result;
}

} // namespace test

int main(int argc, char **argv) {
	::testing::InitGoogleTest(&argc, argv);
	::testing::AddGlobalTestEnvironment(
		new test::Environment(test::ParseParameters(argc, argv))
		);
	return RUN_ALL_TESTS();
}


================================================
FILE: test/mouse_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/session.h>
#include <gtest/gtest.h>

namespace test {

using namespace webdriverxx;

class TestMouse : public ::testing::Test {
protected:
	static void SetUpTestCase() {
		GetDriver().Navigate(GetTestPageUrl("mouse.html"));
	}

	TestMouse() : driver(GetDriver()) {}

	void SetUp() {
		target = driver.FindElement(ById("target"));
		click_type = driver.FindElement(ById("click_type")).Clear();
		updown_type = driver.FindElement(ById("updown_type")).Clear();
		updown_button = driver.FindElement(ById("updown_button")).Clear();
	}

	std::string GetValue(const Element& element) {
		return element.GetAttribute("value");
	}

	WebDriver driver;
	Element target;
	Element click_type;
	Element updown_type;
	Element updown_button;
};

TEST_F(TestMouse, SendsClick) {
	driver.MoveToCenterOf(target).Click();
	ASSERT_EQ("click", GetValue(click_type));
}

TEST_F(TestMouse, MovesPointerInsideTarget) {
	driver.MoveToCenterOf(target).MoveTo(Offset(-45,-45)).Click();
	ASSERT_EQ("click", GetValue(click_type));
}

TEST_F(TestMouse, MovesPointerOutsideTarget) {
	if (IsFirefox()) return;
	driver.MoveToCenterOf(target).MoveTo(Offset(55,55)).Click();
	driver.MoveToCenterOf(target).MoveTo(Offset(-55,55)).Click();
	driver.MoveToCenterOf(target).MoveTo(Offset(-55,-55)).Click();
	driver.MoveToCenterOf(target).MoveTo(Offset(55,-55)).Click();
	ASSERT_EQ("", GetValue(click_type));
}

TEST_F(TestMouse, MovesPointerToTopLeftCorner) {
	driver.MoveToTopLeftOf(target).Click();
	ASSERT_EQ("click", GetValue(click_type));
}

TEST_F(TestMouse, MovesPointerToTopLeftCornerWithOffset) {
	driver.MoveToTopLeftOf(target, Offset(95,95)).Click();
	ASSERT_EQ("click", GetValue(click_type));
}

TEST_F(TestMouse, MovesPointerToTopLeftCornerWithOffset2) {
	if (IsFirefox()) return;
	driver.MoveToTopLeftOf(target, Offset(-5,-5)).Click();
	driver.MoveToTopLeftOf(target, Offset(105,105)).Click();
	ASSERT_EQ("", GetValue(click_type));
}

TEST_F(TestMouse, SendsDoubleclicks) {
	driver.MoveToCenterOf(target).DoubleClick();
	ASSERT_EQ("dblclick", GetValue(click_type));
}

TEST_F(TestMouse, SendsButtonDown) {
	driver.MoveToCenterOf(target).ButtonDown();
	ASSERT_EQ("mousedown", GetValue(updown_type));
	driver.ButtonUp();
}

TEST_F(TestMouse, SendsButtonUp) {
	driver.MoveToCenterOf(target).ButtonDown().ButtonUp();
	ASSERT_EQ("mouseup", GetValue(updown_type));
}

TEST_F(TestMouse, SendsDifferentButtons) {
	if (IsFirefox()) return;
	driver.MoveToCenterOf(target).ButtonDown(mouse::LeftButton).ButtonUp(mouse::LeftButton);
	const auto left_button = GetValue(updown_button);
	driver.ButtonDown(mouse::RightButton).ButtonUp(mouse::RightButton);
	const auto right_button = GetValue(updown_button);
	driver.ButtonDown(mouse::MiddleButton).ButtonUp(mouse::MiddleButton);
	const auto middle_button = GetValue(updown_button);
	ASSERT_NE(left_button, right_button);
	ASSERT_NE(left_button, middle_button);
	ASSERT_NE(right_button, middle_button);	
}

} // namespace test


================================================
FILE: test/pages/alerts.html
================================================
<html>
<body>
</body>
</html>

================================================
FILE: test/pages/element.html
================================================
<html>
<body>
<div id="first_div"></div>
<div id="second_div"></div>
<div id="third_div"></div>
<div id="element_with_text">Some text</div>
<div id="div_with_attributes" test="test value"></div>
<input>
<div id="visible">visible text</div>
<div id="hidden" style="display:none">hidden text</div>
</body>
</html>

================================================
FILE: test/pages/finder.html
================================================
<html>
<body>
<div id="test_id"></div>
<div class="test_class"></div>
<div id="css_selectable"></div>
<input name="test_name">
<a href="">test link text</a>
<div id="outer">
	<div id="inner"></div>
	<div></div>
</div>
<div id="next_after_outer"></div>
<div id="finder_loaded"></div>
</body>
</html>

================================================
FILE: test/pages/frame1.html
================================================
<html>
<body>
<input id="tag" value="frame1">
</body>
</html>

================================================
FILE: test/pages/frame2.html
================================================
<html>
<body>
<input id="tag" value="frame2">
</body>
</html>

================================================
FILE: test/pages/frame3.html
================================================
<html>
<body>
<input id="tag" value="frame3">
</body>
</html>

================================================
FILE: test/pages/frames.html
================================================
<html>
<body>
<input id="tag" value="top_frame">
<iframe src="frameset.html" name="frameset_name"></iframe>
<iframe src="frame3.html" name="frame3_name"></iframe>
</body>
</html>

================================================
FILE: test/pages/frameset.html
================================================
<html>
<frameset rows="*" cols="50,*">
<frame src="frame1.html" name="frame1_name">
<frame src="frame2.html" name="frame2_name">
</frameset>
</html>

================================================
FILE: test/pages/js.html
================================================
<html>
<body>
<input>
</body>
</html>

================================================
FILE: test/pages/keyboard.html
================================================
<html>
<body>
<input name="first">
<input name="second">
</body>
</html>

================================================
FILE: test/pages/mouse.html
================================================
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
	function report_click(event) {
		document.getElementById('click_type').value = event.type;
		document.getElementById('click_button').value = event.button;
	}

	function report_updown(event) {
		document.getElementById('updown_type').value = event.type;
		document.getElementById('updown_button').value = event.button;
	}

</script>
<style type="text/css">
	* { border: none; padding: 0; margin: 0; }
	#target { width: 100px; height: 100px; margin: 50px; background-color: red; }
</style>
</head>
<body>
<div id="target"
onclick="report_click(event)"
ondblclick="report_click(event)"
onmousedown="report_updown(event)"
onmouseup="report_updown(event)"
></div>
<div><input id="click_type"></div>
<div><input id="click_button"></div>
<div><input id="updown_type"></div>
<div><input id="updown_button"></div>
</body>
</html>

================================================
FILE: test/pages/navigation1.html
================================================
<html>
<body>
Navigation 1
<input>
</body>
</html>

================================================
FILE: test/pages/navigation2.html
================================================
<html>
<body>
Navigation 2
</body>
</html>

================================================
FILE: test/pages/redirect.html
================================================
<html>
<body>
<script type="text/javascript">location = "target.html"</script>
</body>
</html>

================================================
FILE: test/pages/session.html
================================================
<html>
<head>
<title>Test title</title>
</head>
<body>
<input>
</body>
</html>

================================================
FILE: test/pages/webdriver.html
================================================
<html>
<body>
<input>
</body>
</html>

================================================
FILE: test/resource_test.cpp
================================================
#include <webdriverxx/detail/resource.h>
#include <webdriverxx/detail/http_client.h>
#include <webdriverxx/response_status_code.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

namespace test {

using namespace webdriverxx;
using namespace webdriverxx::detail;

const char *const kTestUrl = "http://test/";

struct MockHttpClient : IHttpClient, SharedObjectBase {
	MOCK_CONST_METHOD1(Get, HttpResponse(const std::string& url));
	MOCK_CONST_METHOD2(Post, HttpResponse(const std::string& url, const std::string& data));
	MOCK_CONST_METHOD1(Delete, HttpResponse(const std::string& url));
};

using namespace ::testing;

struct TestResource : Test {
	void SetUp() {
		http_response.http_code = 200;
		http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":12345}";
	
		http_client = Shared<MockHttpClient>(new MockHttpClient);
		DefaultValue<HttpResponse>::Set(HttpResponse());
		ON_CALL(*http_client, Get(_)).WillByDefault(ReturnPointee(&http_response));
		ON_CALL(*http_client, Post(_,_)).WillByDefault(ReturnPointee(&http_response));
		ON_CALL(*http_client, Delete(_)).WillByDefault(ReturnPointee(&http_response));
		EXPECT_CALL(*http_client, Get(_)).Times(AnyNumber());
		EXPECT_CALL(*http_client, Post(_,_)).Times(AnyNumber());
		EXPECT_CALL(*http_client, Delete(_)).Times(AnyNumber());
	}

	Shared<MockHttpClient> http_client;
	HttpResponse http_response;
};

// Positive tests

TEST_F(TestResource, CanBeCreated) {
	Resource resource(kTestUrl, http_client);
}

TEST_F(TestResource, ReturnsUrl) {
	Resource resource(kTestUrl, http_client);
	ASSERT_EQ(kTestUrl, resource.GetUrl());
}

TEST_F(TestResource, CanBeUsedToMakeSubResource) {
	Shared<Resource> a(new Resource("a", http_client));
	ASSERT_EQ("a/c", MakeSubResource(a, "c")->GetUrl());
	ASSERT_EQ("a/c",MakeSubResource(a, "/c")->GetUrl());
	Shared<Resource> b(new Resource("b/", http_client));
	ASSERT_EQ("b/c", MakeSubResource(b, "c")->GetUrl());
	ASSERT_EQ("b/c", MakeSubResource(b, "/c")->GetUrl());
}

TEST_F(TestResource, DoesNotDeleteResourceByDefault)
{
	EXPECT_CALL(*http_client, Delete(_)).Times(0);
	Resource resource(kTestUrl, http_client);
}

TEST_F(TestResource, DeletesResourceIfOwnershipIsEnabled)
{
	EXPECT_CALL(*http_client, Delete(kTestUrl));
	Resource resource(kTestUrl, http_client, Resource::IsOwner);
}

TEST_F(TestResource, SharesOwnershipOfParentResource)
{
	EXPECT_CALL(*http_client, Delete(_)).Times(0);
	Shared<Resource> parent(new Resource("parent", http_client, Resource::IsOwner));
	Shared<Resource> child = MakeSubResource(parent, "child", Resource::IsOwner);
	parent = Shared<Resource>();
	Mock::VerifyAndClear(http_client); // Parent shouldn't be deleted at this point because child is alive
	InSequence check_delete_order;
	EXPECT_CALL(*http_client, Delete("parent/child"));
	EXPECT_CALL(*http_client, Delete("parent"));
	child = Shared<Resource>();
}

TEST_F(TestResource, RootResourceReturnsJsonObject)
{
	RootResource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":12345}";
	ASSERT_TRUE(resource.Get("command").is<picojson::object>());
}

TEST_F(TestResource, RootResourceReturnsSessionId)
{
	RootResource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":12345}";
	ASSERT_TRUE(resource.Get("command").contains("sessionId"));
	ASSERT_EQ("123", resource.Get("command").get("sessionId").to_str());
}

TEST_F(TestResource, RootResourceReturnsNullSessionId)
{
	RootResource resource(kTestUrl, http_client);
	http_response.body = "{\"sessionId\":null,\"status\":0,\"value\":12345}";
	ASSERT_TRUE(resource.Get("command").contains("sessionId"));
	ASSERT_TRUE(resource.Get("command").get("sessionId").is<picojson::null>());
}

TEST_F(TestResource, RootResourceReturnsScalarValueFromPositiveResponse)
{
	RootResource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":12345}";
	picojson::value value = resource.Get("command").get("value");
	ASSERT_TRUE(value.is<double>());
	ASSERT_EQ(12345, value.get<double>());
}

TEST_F(TestResource, RootResourceReturnsObjectValueFromPositiveResponse)
{
	RootResource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":{\"member\":12345}}";
	picojson::value value = resource.Get("command").get("value");
	ASSERT_TRUE(value.is<picojson::object>());
	ASSERT_TRUE(value.contains("member"));
	ASSERT_TRUE(value.get("member").is<double>());
	ASSERT_EQ(12345, value.get("member").get<double>());
}

TEST_F(TestResource, ReturnsScalarValueFromPositiveResponse)
{
	Resource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":12345}";
	picojson::value value = resource.Get("command");
	ASSERT_TRUE(value.is<double>());
	ASSERT_EQ(12345, value.get<double>());
}

TEST_F(TestResource, ReturnsObjectValueFromPositiveResponse)
{
	Resource resource(kTestUrl, http_client);
	http_response.http_code = 200;
	http_response.body = "{\"sessionId\":\"123\",\"status\":0,\"value\":{\"member\":12345}}";
	picojson::value value = resource.Get("command");
	ASSERT_TRUE(value.is<picojson::object>());
	ASSERT_TRUE(value.contains("member"));
	ASSERT_TRUE(value.get("member").is<double>());
	ASSERT_EQ(12345, value.get("member").get<double>());
}

// Negative tests

TEST_F(TestResource, ThrowsOnHttp404)
{
	http_response.http_code = 404;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp400)
{
	http_response.http_code = 400;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp499)
{
	http_response.http_code = 499;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp501)
{
	http_response.http_code = 501;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, DoesNotHideHttpExceptions)
{
	EXPECT_CALL(*http_client, Get(_)).WillOnce(Throw(WebDriverException("HTTP failed")));
	Resource resource(kTestUrl, http_client);
	try {
		resource.Get("command");
		FAIL(); // Shouldn't get here
	} catch (const std::exception& e) {
		const std::string message = e.what();
		ASSERT_NE(std::string::npos, message.find("HTTP failed"));
	}
}

TEST_F(TestResource, AddsContextToExceptions)
{
	EXPECT_CALL(*http_client, Get(_)).WillOnce(Throw(WebDriverException("HTTP failed")));
	Resource resource(kTestUrl, http_client);
	try {
		resource.Get("pinky");
		FAIL(); // Shouldn't get here
	} catch (const std::exception& e) {
		const std::string message = e.what();
		ASSERT_NE(std::string::npos, message.find("pinky"));
	}
}

TEST_F(TestResource, WebDriverExceptionContainsCommandAndHttpCodeAndBody)
{
	http_response.http_code = 501;
	http_response.body = "--oops--";
	Resource resource(kTestUrl, http_client);
	try {
		resource.Get("pinky");
		FAIL(); // Shouldn't get here
	} catch (const std::exception& e) {
		const std::string message = e.what();
		ASSERT_NE(std::string::npos, message.find("pinky"));
		ASSERT_NE(std::string::npos, message.find("--oops--"));
		ASSERT_NE(std::string::npos, message.find("501"));
	}
}

TEST_F(TestResource, WebDriverExceptionContainsStatusAndStatusDescription)
{
	http_response.http_code = 500;
	http_response.body = Fmt() << "{\"status\":"
		<< response_status_code::kNoSuchWindow
		<< ",\"value\":{\"message\":\"12345\"}}";
	Resource resource(kTestUrl, http_client);
	try {
		resource.Get("pinky");
		FAIL(); // Shouldn't get here
	} catch (const std::exception& e) {
		const std::string message = e.what();
		ASSERT_NE(std::string::npos, message.find(Fmt() << response_status_code::kNoSuchWindow));
		ASSERT_NE(std::string::npos, message.find(response_status_code::ToString(response_status_code::kNoSuchWindow)));
	}
}

TEST_F(TestResource, ThrowsOnHttp500)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":12,\"value\":{\"message\":\"12345\"}}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndMissingStatus)
{
	http_response.http_code = 500;
	http_response.body = "{\"value\":{\"message\":\"12345\"}}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndInvalidStatus)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":\"xxx\",\"value\":{\"message\":\"12345\"}}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndMissingValue)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":12}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndInvalidValue)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":\"xxx\",\"value\":\"12345\"}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndMissingMessage)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":12,\"value\":{\"xxx\":\"12345\"}}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp500AndInvalidMessage)
{
	http_response.http_code = 500;
	http_response.body = "{\"status\":12,\"value\":{\"message\":12345}}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp399)
{
	http_response.http_code = 399;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnHttp502)
{
	http_response.http_code = 502;
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnEmptyResponse)
{
	Resource resource(kTestUrl, http_client);
	http_response.body = "";
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnMalformedResponse)
{
	Resource resource(kTestUrl, http_client);
	http_response.body = "Blah blah blah";
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsIfResponseIsNotAnObject)
{
	Resource resource(kTestUrl, http_client);
	http_response.body = "\"value\":123";
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnMissingStatus)
{
	http_response.body = "{\"sessionId\":\"123\",\"value\":12345}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnInvalidStatus)
{
	http_response.body = "{\"sessionId\":\"123\",\"status\":\"5\",\"value\":12345}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnNonZeroStatus)
{
	Resource resource(kTestUrl, http_client);
	http_response.body = "{\"sessionId\":\"123\",\"status\":5,\"value\":12345}";
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

TEST_F(TestResource, ThrowsOnMissingValue)
{
	http_response.body = "{\"sessionId\":\"123\",\"status\":0}";
	Resource resource(kTestUrl, http_client);
	ASSERT_THROW(resource.Get("command"), WebDriverException);
}

} // namespace test


================================================
FILE: test/session_test.cpp
================================================
#include "environment.h"
#include <webdriverxx/session.h>
#include <gtest/gtest.h>
#include <algorithm>

namespace test {

using namespace webdriverxx;

class TestSession : public ::testing::Test {
protected:
	TestSession() : driver(GetDriver()) {}

	void ReplaceSpoiledSession() {
		driver = GetFreshDriver();
	}

	WebDriver driver;
};

TEST_F(TestSession, GetsCapabilities)
{
	Capabilities c = driver.GetCapabilities();
	ASSERT_TRUE(c.Has("browserName"));
	ASSERT_TRUE(c.Has("version"));
	ASSERT_TRUE(c.Has("platform"));
	ASSERT_NE("", c.GetBrowserName());
}

TEST_F(TestSession, StartsSecondBrowser) {
	WebDriver second = CreateDriver();
}

TEST_F(TestSession, GetsCurrentWindow) {
	driver.GetCurrentWindow();
}

TEST_F(TestSession, GetsWindowHandle) {
	ASSERT_NE("", driver.GetCurrentWindow().GetHandle());
}

TEST_F(TestSession, SetsFocusToWindow) {
	driver.SetFocusToWindow(driver.GetCurrentWindow().GetHandle());
}

TEST_F(TestSession, ClosesCurrentWindow) {
	driver.CloseCurrentWindow();
	ReplaceSpoiledSession();
}

TEST_F(TestSession, GetsWindowSize) {
	Window window = driver.GetCurrentWindow();
	window.GetSize();
}

TEST_F(TestSession, SetsWindowSize) {
	Window window = driver.GetCurrentWindow();
	Size size1;
	size1.width = 601;
	size1.height = 602;
	window.SetSize(size1);
	Size size2 = window.GetSize();
	ASSERT_EQ(601, size2.width);
	ASSERT_EQ(602, size2.height);
}

TEST_F(TestSession, GetsWindowPosition) {
	Window window = driver.GetCurrentWindow();
	window.GetPosition();
}

TEST_F(TestSession, SetsWindowPosition) {
	if (IsPhantom()) return;
	Window window = driver.GetCurrentWindow();
	Point position1;
	position1.x = 101;
	position1.y = 102;
	window.SetPosition(position1);
	Point position2 = window.GetPosition();
	ASSERT_EQ(101, position2.x);
	ASSERT_EQ(102, position2.y);
}

TEST_F(TestSession, MaximizesWindow) {
	Window window = driver.GetCurrentWindow();
	window.Maximize();
}

TEST_F(TestSession, GetsWindows) {
	driver.GetWindows();
}

TEST_F(TestSession, Navigates) {
	std::string url = GetWebDriverUrl() + "status";
	driver.Navigate(url);
	ASSERT_EQ(url, driver.GetUrl());
}

TEST_F(TestSession, NavigatesToTestPage) {
	const std::string url = GetTestPageUrl("session.html");
	driver.Navigate(url);
	ASSERT_EQ(url, driver.GetUrl());
}

TEST_F(TestSession, GoesBack) {
	const std::string page1 = GetTestPageUrl("navigation1.html");
	const std::string page2 = GetTestPageUrl("navigation2.html");
	driver.Navigate(page1).Navigate(page2).Back();
	ASSERT_EQ(page1, driver.GetUrl());
}

TEST_F(TestSession, GoesForward) {
	const std::string page1 = GetTestPageUrl("navigation1.html");
	const std::string page2 = GetTestPageUrl("navigation2.html");
	driver.Navigate(page1).Navigate(page2).Back().Forward();
	ASSERT_EQ(page2, driver.GetUrl());
}

TEST_F(TestSession, DoesRefresh) {
	const std::string page = GetTestPageUrl("navigation1.html");
	driver.Navigate(page).FindElement(ByTag("input")).Click().SendKeys("abc");
	ASSERT_EQ("abc", driver.FindElement(ByTag("input")).GetAttribute("value"));
	driver.Refresh();
	ASSERT_EQ(page, driver.GetUrl());
	ASSERT_EQ("", driver.FindElement(ByTag("input")).GetAttribute("value"));
}

TEST_F(TestSession, GetsPageSource) {
	driver.Navigate(GetTestPageUrl("session.html"));
	std::string source = driver.GetSource();
	ASSERT_NE(std::string::npos, source.find("<html"));
	ASSERT_NE(std::string::npos, source.find("</html>"));
}

TEST_F(TestSession, GetsPageTitle) {
	driver.Navigate(GetTes
Download .txt
gitextract_52i55o91/

├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include/
│   ├── webdriverxx/
│   │   ├── browsers/
│   │   │   ├── chrome.h
│   │   │   ├── firefox.h
│   │   │   ├── ie.h
│   │   │   └── phantom.h
│   │   ├── by.h
│   │   ├── capabilities.h
│   │   ├── client.h
│   │   ├── client.inl
│   │   ├── conversions.h
│   │   ├── detail/
│   │   │   ├── error_handling.h
│   │   │   ├── factories.h
│   │   │   ├── factories_impl.h
│   │   │   ├── finder.h
│   │   │   ├── finder.inl
│   │   │   ├── http_client.h
│   │   │   ├── http_connection.h
│   │   │   ├── http_request.h
│   │   │   ├── keyboard.h
│   │   │   ├── meta_tools.h
│   │   │   ├── resource.h
│   │   │   ├── shared.h
│   │   │   ├── time.h
│   │   │   ├── to_string.h
│   │   │   └── types.h
│   │   ├── element.h
│   │   ├── element.inl
│   │   ├── errors.h
│   │   ├── js_args.h
│   │   ├── keys.h
│   │   ├── picojson.h
│   │   ├── response_status_code.h
│   │   ├── session.h
│   │   ├── session.inl
│   │   ├── types.h
│   │   ├── wait.h
│   │   ├── wait_match.h
│   │   ├── webdriver.h
│   │   └── window.h
│   └── webdriverxx.h
└── test/
    ├── CMakeLists.txt
    ├── alerts_test.cpp
    ├── browsers_test.cpp
    ├── capabilities_test.cpp
    ├── client_test.cpp
    ├── conversions_test.cpp
    ├── element_test.cpp
    ├── environment.h
    ├── examples_test.cpp
    ├── finder_test.cpp
    ├── frames_test.cpp
    ├── http_connection_test.cpp
    ├── js_test.cpp
    ├── keyboard_test.cpp
    ├── main.cpp
    ├── mouse_test.cpp
    ├── pages/
    │   ├── alerts.html
    │   ├── element.html
    │   ├── finder.html
    │   ├── frame1.html
    │   ├── frame2.html
    │   ├── frame3.html
    │   ├── frames.html
    │   ├── frameset.html
    │   ├── js.html
    │   ├── keyboard.html
    │   ├── mouse.html
    │   ├── navigation1.html
    │   ├── navigation2.html
    │   ├── redirect.html
    │   ├── session.html
    │   └── webdriver.html
    ├── resource_test.cpp
    ├── session_test.cpp
    ├── shared_test.cpp
    ├── to_string_test.cpp
    ├── wait_match_test.cpp
    ├── wait_test.cpp
    └── webdriver_test.cpp
Download .txt
SYMBOL INDEX (422 symbols across 54 files)

FILE: include/webdriverxx/browsers/chrome.h
  type PerfLoggingPrefs (line 9) | struct PerfLoggingPrefs

FILE: include/webdriverxx/browsers/firefox.h
  function namespace (line 6) | namespace webdriverxx {

FILE: include/webdriverxx/browsers/ie.h
  function namespace (line 6) | namespace webdriverxx {

FILE: include/webdriverxx/browsers/phantom.h
  function namespace (line 6) | namespace webdriverxx {

FILE: include/webdriverxx/by.h
  function namespace (line 6) | namespace webdriverxx {

FILE: include/webdriverxx/capabilities.h
  function namespace (line 10) | namespace browser {
  function namespace (line 26) | namespace platform {
  function namespace (line 39) | namespace unexpected_alert_behaviour {
  function namespace (line 47) | namespace proxy_type {
  type Proxy (line 68) | struct Proxy
  function WEBDRIVERXX_PROPERTY (line 69) | WEBDRIVERXX_PROPERTIES_BEGIN(Proxy)
  function Proxy (line 82) | struct SystemProxy : Proxy { // copyable
  type AutomaticProxyFromUrl (line 86) | struct AutomaticProxyFromUrl
  function explicit (line 87) | explicit AutomaticProxyFromUrl(const std::string& url) {
  type ManualProxy (line 97) | struct ManualProxy
  type FtpProxy (line 105) | struct FtpProxy
  function explicit (line 106) | explicit FtpProxy(const std::string& address) { SetProxyAddress(address); }
  type HttpProxy (line 113) | struct HttpProxy
  function explicit (line 114) | explicit HttpProxy(const std::string& address) { SetProxyAddress(address...
  type SslProxy (line 121) | struct SslProxy
  function explicit (line 122) | explicit SslProxy(const std::string& address) { SetProxyAddress(address); }
  type SocksProxy (line 129) | struct SocksProxy
  function explicit (line 130) | explicit SocksProxy(const std::string& address) { SetProxyAddress(addres...
  type LoggingPrefs (line 153) | struct LoggingPrefs

FILE: include/webdriverxx/client.h
  function namespace (line 12) | namespace webdriverxx {

FILE: include/webdriverxx/conversions.h
  function namespace (line 10) | namespace webdriverxx {
  function Has (line 44) | bool Has(const std::string& name) const {
  function namespace (line 57) | namespace conversions_detail {
  function picojson (line 93) | inline
  function picojson (line 98) | inline
  function picojson (line 103) | inline
  function picojson (line 108) | inline
  function picojson (line 113) | inline
  function picojson (line 118) | inline
  function value (line 124) | value CustomToJson(const T& value) {
  function value (line 131) | value ToJson(const T& value) {
  function namespace (line 137) | namespace conversions_detail {
  function CustomFromJson (line 158) | inline
  function CustomFromJson (line 163) | inline
  function CustomFromJson (line 168) | inline
  function CustomFromJson (line 174) | inline
  function CustomFromJson (line 180) | inline
  function CustomFromJson (line 185) | inline
  function CustomFromJson (line 191) | inline
  function picojson (line 218) | inline
  function CustomFromJson (line 226) | inline
  function picojson (line 233) | inline
  function CustomFromJson (line 241) | inline
  function picojson (line 248) | inline
  function CustomFromJson (line 261) | inline

FILE: include/webdriverxx/detail/error_handling.h
  function namespace (line 8) | namespace webdriverxx {

FILE: include/webdriverxx/detail/factories.h
  type IElementFactory (line 22) | struct IElementFactory {

FILE: include/webdriverxx/detail/factories_impl.h
  function namespace (line 10) | namespace webdriverxx {

FILE: include/webdriverxx/detail/finder.h
  function namespace (line 10) | namespace webdriverxx {

FILE: include/webdriverxx/detail/http_client.h
  type IHttpClient (line 18) | struct IHttpClient {

FILE: include/webdriverxx/detail/http_connection.h
  function namespace (line 10) | namespace webdriverxx {

FILE: include/webdriverxx/detail/http_request.h
  function namespace (line 9) | namespace webdriverxx {

FILE: include/webdriverxx/detail/keyboard.h
  function namespace (line 8) | namespace webdriverxx {

FILE: include/webdriverxx/detail/meta_tools.h
  function namespace (line 9) | namespace webdriverxx {

FILE: include/webdriverxx/detail/resource.h
  function namespace (line 11) | namespace webdriverxx {

FILE: include/webdriverxx/detail/shared.h
  function namespace (line 6) | namespace webdriverxx {
  function Swap (line 73) | void Swap(Shared& other) {

FILE: include/webdriverxx/detail/time.h
  function namespace (line 18) | namespace webdriverxx {

FILE: include/webdriverxx/detail/to_string.h
  function namespace (line 8) | namespace webdriverxx {
  function namespace (line 25) | namespace webdriverxx_to_string_impl {
  function namespace (line 35) | namespace webdriverxx {

FILE: include/webdriverxx/detail/types.h
  function namespace (line 9) | namespace webdriverxx {

FILE: include/webdriverxx/errors.h
  function namespace (line 7) | namespace webdriverxx {

FILE: include/webdriverxx/js_args.h
  function namespace (line 7) | namespace webdriverxx {

FILE: include/webdriverxx/keys.h
  function namespace (line 7) | namespace webdriverxx {

FILE: include/webdriverxx/picojson.h
  function namespace (line 89) | namespace picojson {
  function getc (line 532) | int getc() {
  function ungetc (line 548) | void ungetc() {
  function skip_ws (line 556) | void skip_ws() {
  function expect (line 565) | bool expect(int expect) {
  function match (line 573) | bool match(const std::string& pattern) {
  function class (line 800) | class deny_parse_context {
  function parse_array_stop (line 857) | bool parse_array_stop(size_t) { return true; }
  function parse_object_start (line 858) | bool parse_object_start() {
  function set_null (line 879) | bool set_null() { return true; }
  function set_bool (line 880) | bool set_bool(bool) { return true; }
  function set_int64 (line 882) | bool set_int64(int64_t) { return true; }
  function set_number (line 884) | bool set_number(double) { return true; }
  function parse_array_start (line 889) | bool parse_array_start() { return true; }
  function parse_array_stop (line 893) | bool parse_array_stop(size_t) { return true; }
  function parse_object_start (line 894) | bool parse_object_start() { return true; }
  function std (line 933) | inline std::string parse(value& out, std::istream& is) {
  function set_last_error (line 945) | inline void set_last_error(const std::string& s) {
  function std (line 949) | inline const std::string& get_last_error() {
  function namespace (line 977) | namespace std {
  function done_testing (line 1022) | static void done_testing()
  function main (line 1041) | int main(void)

FILE: include/webdriverxx/response_status_code.h
  function namespace (line 5) | namespace response_status_code {

FILE: include/webdriverxx/types.h
  type TimePoint (line 8) | typedef unsigned long long TimePoint;
  type Duration (line 9) | typedef unsigned Duration;
  type Cookie (line 26) | struct Cookie {
  type Button (line 81) | enum Button {

FILE: include/webdriverxx/wait.h
  function namespace (line 10) | namespace webdriverxx {

FILE: include/webdriverxx/wait_match.h
  function namespace (line 13) | namespace webdriverxx {
  function namespace (line 59) | namespace webdriverxx {
  type Value (line 109) | typedef decltype(getter()) Value;

FILE: include/webdriverxx/webdriver.h
  function namespace (line 7) | namespace webdriverxx {

FILE: include/webdriverxx/window.h
  function namespace (line 9) | namespace webdriverxx {

FILE: test/alerts_test.cpp
  type test (line 5) | namespace test {
    class TestAlerts (line 9) | class TestAlerts : public ::testing::Test {
      method SetUpTestCase (line 11) | static void SetUpTestCase() {
      method TestAlerts (line 15) | TestAlerts() : driver(GetDriver()) {}
    function TEST_F (line 20) | TEST_F(TestAlerts, AcceptsAlert) {
    function TEST_F (line 26) | TEST_F(TestAlerts, DismissesAlert) {
    function TEST_F (line 32) | TEST_F(TestAlerts, GetsAlertText) {
    function TEST_F (line 39) | TEST_F(TestAlerts, SendsKeysToAlert) {
    function TEST_F (line 47) | TEST_F(TestAlerts, DismissesSendedKeys) {

FILE: test/browsers_test.cpp
  type test (line 8) | namespace test {
    function TEST (line 12) | TEST(Firefox, WithTheSimplestSyntax) {
    function TEST (line 17) | TEST(Firefox, WithCustomUrl) {
    function TEST (line 22) | TEST(Firefox, WithDefaultCapabilities) {
    function TEST (line 28) | TEST(Firefox, HasCapabilitiesProperties) {
    function TEST (line 33) | TEST(Firefox, ConvertsToJson) {
    function TEST (line 45) | TEST(InternetExplorer, ConvertsToJson) {
    function TEST (line 52) | TEST(Chrome, ConvertsToJson) {

FILE: test/capabilities_test.cpp
  type test (line 4) | namespace test {
    function TEST (line 8) | TEST(Capabilities, AllowsToSetAndGetCustomValues) {
    function TEST (line 20) | TEST(Capabilities, ConvertibleToJson) {
    function TEST (line 30) | TEST(Capabilities, AllowsToSetProxy) {
    function TEST (line 43) | TEST(Capabilities, ConvertsProxyToJson) {

FILE: test/client_test.cpp
  type test (line 5) | namespace test {
    class TestClient (line 9) | class TestClient : public ::testing::Test {
      method SetUpTestCase (line 11) | static void SetUpTestCase() {
      method TearDownTestCase (line 15) | static void TearDownTestCase() {
    function TEST_F (line 25) | TEST_F(TestClient, GetsStatus) {
    function TEST_F (line 31) | TEST_F(TestClient, GetsSessions) {
    function TEST_F (line 35) | TEST_F(TestClient, CreatesSession) {

FILE: test/conversions_test.cpp
  type test (line 6) | namespace test {
    function TEST (line 10) | TEST(ToJson, ConvertsIntegralTypes) {
    function TEST (line 20) | TEST(ToJson, ConvertsStrings) {
    function TEST (line 30) | TEST(ToJson, ConvertsIterables) {
    type custom (line 72) | namespace custom {
      type Object (line 74) | struct Object {
      function CustomToJson (line 79) | picojson::value CustomToJson(const Object& value) {
      function CustomFromJson (line 85) | void CustomFromJson(const picojson::value& value, Object& result) {
    function TEST (line 94) | TEST(ToJson, ConvertsCustomObjects) {
    function J (line 109) | picojson::value J(const std::string& json) {
    function TEST (line 115) | TEST(FromJson, ConvertsIntegralTypes) {
    function TEST (line 123) | TEST(FromJson, ConvertsStrings) {
    function TEST (line 127) | TEST(FromJson, ConvertsIterables) {
    function TEST (line 149) | TEST(FromJson, ConvertsCustomObjects) {

FILE: test/element_test.cpp
  type test (line 5) | namespace test {
    class TestElement (line 9) | class TestElement : public ::testing::Test {
      method SetUpTestCase (line 11) | static void SetUpTestCase() {
      method TestElement (line 15) | TestElement() : driver(GetDriver()) {}
    function TEST_F (line 20) | TEST_F(TestElement, CanBeClicked) {
    function TEST_F (line 26) | TEST_F(TestElement, GetsText) {
    function TEST_F (line 30) | TEST_F(TestElement, CanBeCleared) {
    function TEST_F (line 38) | TEST_F(TestElement, GetsTagName) {
    function TEST_F (line 45) | TEST_F(TestElement, GetsAttributes) {
    function TEST_F (line 51) | TEST_F(TestElement, IsEqualToOtherElement) {
    function TEST_F (line 58) | TEST_F(TestElement, IsNotEqualToOtherElement) {
    function TEST_F (line 65) | TEST_F(TestElement, HasStrictWeakOrdering) {
    function TEST_F (line 79) | TEST_F(TestElement, GetsIsDisplayed) {
    function TEST_F (line 84) | TEST_F(TestElement, GetsLocation) {
    function TEST_F (line 89) | TEST_F(TestElement, GetsSize) {
    function TEST_F (line 95) | TEST_F(TestElement, GetsCssProperty) {

FILE: test/environment.h
  function namespace (line 10) | namespace test {
  function TearDown (line 75) | void TearDown() {
  function DeleteDriver (line 80) | void DeleteDriver() {
  function Parameters (line 91) | inline Parameters GetParameters() { return Environment::Instance().GetPa...
  function std (line 92) | inline std::string GetWebDriverUrl() { return Environment::Instance().Ge...
  function std (line 93) | inline std::string GetTestPageUrl(const std::string& page_name) { return...
  function webdriverxx (line 94) | inline webdriverxx::WebDriver& GetDriver() { return Environment::Instanc...
  function webdriverxx (line 95) | inline webdriverxx::WebDriver& GetFreshDriver() { return Environment::In...
  function webdriverxx (line 96) | inline webdriverxx::WebDriver CreateDriver() { return Environment::Insta...
  function TestRealBrowsers (line 97) | inline bool TestRealBrowsers() { return GetParameters().test_real_browse...
  function std (line 98) | inline std::string GetBrowserName() { return GetDriver().GetCapabilities...
  function IsFirefox (line 99) | inline bool IsFirefox() { return GetBrowserName() == webdriverxx::browse...
  function IsPhantom (line 100) | inline bool IsPhantom() { return GetBrowserName() == webdriverxx::browse...

FILE: test/examples_test.cpp
  type test (line 9) | namespace test {
    class TestExamples (line 13) | class TestExamples : public ::testing::Test {
      method TestExamples (line 15) | TestExamples() : driver(GetDriver()) {}
      method StopNavigation (line 17) | void StopNavigation() {
    function TEST_F (line 26) | TEST_F(TestExamples, QuickExample) {
    function TEST_F (line 36) | TEST_F(TestExamples, ImplicitWait) {
    function TEST_F (line 44) | TEST_F(TestExamples, ExplicitWait1) {
    function TEST_F (line 53) | TEST_F(TestExamples, ExplicitWait2) {
    function TEST_F (line 64) | TEST_F(TestExamples, UseGmockMatchers) {

FILE: test/finder_test.cpp
  type test (line 5) | namespace test {
    class TestFinder (line 9) | class TestFinder : public ::testing::Test {
      method SetUpTestCase (line 11) | static void SetUpTestCase() {
      method TearDownTestCase (line 18) | static void TearDownTestCase() {
      method TestFinder (line 23) | TestFinder() : driver(GetDriver()) {}
    function TEST_F (line 28) | TEST_F(TestFinder, CanFindElement) {
    function TEST_F (line 32) | TEST_F(TestFinder, ThrowsIfElementNotFound) {
    function TEST_F (line 36) | TEST_F(TestFinder, CanFindMoreThanOneElement) {
    function TEST_F (line 40) | TEST_F(TestFinder, ReturnsZeroIfElementsNotFound) {
    function TEST_F (line 44) | TEST_F(TestFinder, FindsElementById) {
    function TEST_F (line 49) | TEST_F(TestFinder, FindsElementByClassName) {
    function TEST_F (line 54) | TEST_F(TestFinder, FindsElementByCssSelector) {
    function TEST_F (line 59) | TEST_F(TestFinder, FindsElementByName) {
    function TEST_F (line 64) | TEST_F(TestFinder, FindsElementByLinkText) {
    function TEST_F (line 69) | TEST_F(TestFinder, FindsElementByPartialLinkText) {
    function TEST_F (line 74) | TEST_F(TestFinder, FindsElementByTagName) {
    function TEST_F (line 79) | TEST_F(TestFinder, FindsElementByXPath) {
    function TEST_F (line 84) | TEST_F(TestFinder, OfElementFindsInnerElement) {
    function TEST_F (line 89) | TEST_F(TestFinder, OfElementDoesNotFindItself) {
    function TEST_F (line 94) | TEST_F(TestFinder, OfElementDoesNotFindNonExistingInnerElements) {
    function TEST_F (line 101) | TEST_F(TestFinder, OfElementFindsMoreThanOneInnerElement) {

FILE: test/frames_test.cpp
  type test (line 5) | namespace test {
    class TestFrames (line 9) | class TestFrames : public ::testing::Test {
      method TestFrames (line 11) | TestFrames()
      method SetUp (line 16) | void SetUp()
    function TEST_F (line 25) | TEST_F(TestFrames, OnTopFrameByDefault) {
    function TEST_F (line 29) | TEST_F(TestFrames, CanSwitchToFrameByNumber) {
    function TEST_F (line 34) | TEST_F(TestFrames, CanSwitchToFrameByName) {
    function TEST_F (line 39) | TEST_F(TestFrames, CanSwitchToFrameByElement) {
    function TEST_F (line 46) | TEST_F(TestFrames, CanSwitchToDefaultFrame) {
    function TEST_F (line 52) | TEST_F(TestFrames, CanSwitchToDeepFrames) {
    function TEST_F (line 57) | TEST_F(TestFrames, CanSwitchToParentFrame) {

FILE: test/http_connection_test.cpp
  type test (line 6) | namespace test {
    function TEST (line 11) | TEST(HttpConnection, CanBeCreated) {
    function TEST (line 15) | TEST(HttpConnection, GetsPage) {
    function TEST (line 22) | TEST(HttpConnection, ThrowsExceptionIfPortIsClosed) {

FILE: test/js_test.cpp
  type test (line 6) | namespace test {
    class TestJsExecutor (line 10) | class TestJsExecutor : public ::testing::Test {
      method SetUpTestCase (line 12) | static void SetUpTestCase() {
      method TestJsExecutor (line 16) | TestJsExecutor() : driver(GetDriver()) {}
    function TEST_F (line 21) | TEST_F(TestJsExecutor, ExecutesSimpleScript) {
    function TEST_F (line 26) | TEST_F(TestJsExecutor, CanPassStringArgument) {
    function TEST_F (line 31) | TEST_F(TestJsExecutor, CanPassStringLiteralArgument) {
    function TEST_F (line 36) | TEST_F(TestJsExecutor, CanPassNumberArgument) {
    function TEST_F (line 43) | TEST_F(TestJsExecutor, CanPassBooleanArgument) {
    function TEST_F (line 49) | TEST_F(TestJsExecutor, CanPassMoreThanOneArgument) {
    function TEST_F (line 55) | TEST_F(TestJsExecutor, CanPassElement) {
    function TEST_F (line 63) | TEST_F(TestJsExecutor, CanPassArray) {
    function TEST_F (line 72) | TEST_F(TestJsExecutor, CanPassOtherContainers) {
    function TEST_F (line 81) | TEST_F(TestJsExecutor, CanPassCArray) {
    type custom (line 89) | namespace custom {
      type Object (line 91) | struct Object {
      function CustomToJson (line 96) | picojson::value CustomToJson(const Object& value) {
      function CustomFromJson (line 102) | void CustomFromJson(const picojson::value& value, Object& result) {
    function TEST_F (line 111) | TEST_F(TestJsExecutor, CanPassCustomObject) {
    function TEST_F (line 120) | TEST_F(TestJsExecutor, EvalsString) {
    function TEST_F (line 124) | TEST_F(TestJsExecutor, EvalsNumber) {
    function TEST_F (line 129) | TEST_F(TestJsExecutor, EvalsBoolean) {
    function TEST_F (line 134) | TEST_F(TestJsExecutor, EvalsElement) {
    function TEST_F (line 139) | TEST_F(TestJsExecutor, EvalsCustomObject) {
    function TEST_F (line 145) | TEST_F(TestJsExecutor, EvalsArrayOfStrings) {
    function TEST_F (line 154) | TEST_F(TestJsExecutor, EvalsArrayOfNumbers) {
    function AsyncScript (line 166) | std::string AsyncScript(const std::string& script) {
    function TEST_F (line 177) | TEST_F(TestJsExecutor, ExecutesSimpleAsyncScript) {
    function TEST_F (line 183) | TEST_F(TestJsExecutor, PassesArgumentsToAsyncScript) {
    function TEST_F (line 190) | TEST_F(TestJsExecutor, ReturnsValueFromAsyncScript) {
    function TEST_F (line 195) | TEST_F(TestJsExecutor, ReturnsElementFromAsyncScript) {

FILE: test/keyboard_test.cpp
  type test (line 6) | namespace test {
    class TestKeyboard (line 10) | class TestKeyboard : public ::testing::Test {
      method SetUpTestCase (line 12) | static void SetUpTestCase() {
      method TestKeyboard (line 16) | TestKeyboard() : driver(GetDriver()) {}
    function TEST_F (line 21) | TEST_F(TestKeyboard, SendsKeysToElement) {
    function TEST_F (line 31) | TEST_F(TestKeyboard, SendsShortcuts) {
    function TEST_F (line 40) | TEST_F(TestKeyboard, SendsKeysToActiveElement) {

FILE: test/main.cpp
  type test (line 5) | namespace test {
    function IsCommandLineArgument (line 9) | bool IsCommandLineArgument(const std::string& arg, const char* name) {
    function GetCommandLineArgumentValue (line 13) | std::string GetCommandLineArgumentValue(const std::string& arg) {
    function Parameters (line 18) | Parameters ParseParameters(int argc, char **argv) {
  function main (line 39) | int main(int argc, char **argv) {

FILE: test/mouse_test.cpp
  type test (line 5) | namespace test {
    class TestMouse (line 9) | class TestMouse : public ::testing::Test {
      method SetUpTestCase (line 11) | static void SetUpTestCase() {
      method TestMouse (line 15) | TestMouse() : driver(GetDriver()) {}
      method SetUp (line 17) | void SetUp() {
      method GetValue (line 24) | std::string GetValue(const Element& element) {
    function TEST_F (line 35) | TEST_F(TestMouse, SendsClick) {
    function TEST_F (line 40) | TEST_F(TestMouse, MovesPointerInsideTarget) {
    function TEST_F (line 45) | TEST_F(TestMouse, MovesPointerOutsideTarget) {
    function TEST_F (line 54) | TEST_F(TestMouse, MovesPointerToTopLeftCorner) {
    function TEST_F (line 59) | TEST_F(TestMouse, MovesPointerToTopLeftCornerWithOffset) {
    function TEST_F (line 64) | TEST_F(TestMouse, MovesPointerToTopLeftCornerWithOffset2) {
    function TEST_F (line 71) | TEST_F(TestMouse, SendsDoubleclicks) {
    function TEST_F (line 76) | TEST_F(TestMouse, SendsButtonDown) {
    function TEST_F (line 82) | TEST_F(TestMouse, SendsButtonUp) {
    function TEST_F (line 87) | TEST_F(TestMouse, SendsDifferentButtons) {

FILE: test/resource_test.cpp
  type test (line 7) | namespace test {
    type MockHttpClient (line 14) | struct MockHttpClient : IHttpClient, SharedObjectBase {
    type TestResource (line 22) | struct TestResource : Test {
      method SetUp (line 23) | void SetUp() {
    function TEST_F (line 43) | TEST_F(TestResource, CanBeCreated) {
    function TEST_F (line 47) | TEST_F(TestResource, ReturnsUrl) {
    function TEST_F (line 52) | TEST_F(TestResource, CanBeUsedToMakeSubResource) {
    function TEST_F (line 61) | TEST_F(TestResource, DoesNotDeleteResourceByDefault)
    function TEST_F (line 67) | TEST_F(TestResource, DeletesResourceIfOwnershipIsEnabled)
    function TEST_F (line 73) | TEST_F(TestResource, SharesOwnershipOfParentResource)
    function TEST_F (line 86) | TEST_F(TestResource, RootResourceReturnsJsonObject)
    function TEST_F (line 94) | TEST_F(TestResource, RootResourceReturnsSessionId)
    function TEST_F (line 103) | TEST_F(TestResource, RootResourceReturnsNullSessionId)
    function TEST_F (line 111) | TEST_F(TestResource, RootResourceReturnsScalarValueFromPositiveResponse)
    function TEST_F (line 121) | TEST_F(TestResource, RootResourceReturnsObjectValueFromPositiveResponse)
    function TEST_F (line 133) | TEST_F(TestResource, ReturnsScalarValueFromPositiveResponse)
    function TEST_F (line 143) | TEST_F(TestResource, ReturnsObjectValueFromPositiveResponse)
    function TEST_F (line 157) | TEST_F(TestResource, ThrowsOnHttp404)
    function TEST_F (line 164) | TEST_F(TestResource, ThrowsOnHttp400)
    function TEST_F (line 171) | TEST_F(TestResource, ThrowsOnHttp499)
    function TEST_F (line 178) | TEST_F(TestResource, ThrowsOnHttp501)
    function TEST_F (line 185) | TEST_F(TestResource, DoesNotHideHttpExceptions)
    function TEST_F (line 198) | TEST_F(TestResource, AddsContextToExceptions)
    function TEST_F (line 211) | TEST_F(TestResource, WebDriverExceptionContainsCommandAndHttpCodeAndBody)
    function TEST_F (line 227) | TEST_F(TestResource, WebDriverExceptionContainsStatusAndStatusDescript...
    function TEST_F (line 244) | TEST_F(TestResource, ThrowsOnHttp500)
    function TEST_F (line 252) | TEST_F(TestResource, ThrowsOnHttp500AndMissingStatus)
    function TEST_F (line 260) | TEST_F(TestResource, ThrowsOnHttp500AndInvalidStatus)
    function TEST_F (line 268) | TEST_F(TestResource, ThrowsOnHttp500AndMissingValue)
    function TEST_F (line 276) | TEST_F(TestResource, ThrowsOnHttp500AndInvalidValue)
    function TEST_F (line 284) | TEST_F(TestResource, ThrowsOnHttp500AndMissingMessage)
    function TEST_F (line 292) | TEST_F(TestResource, ThrowsOnHttp500AndInvalidMessage)
    function TEST_F (line 300) | TEST_F(TestResource, ThrowsOnHttp399)
    function TEST_F (line 307) | TEST_F(TestResource, ThrowsOnHttp502)
    function TEST_F (line 314) | TEST_F(TestResource, ThrowsOnEmptyResponse)
    function TEST_F (line 321) | TEST_F(TestResource, ThrowsOnMalformedResponse)
    function TEST_F (line 328) | TEST_F(TestResource, ThrowsIfResponseIsNotAnObject)
    function TEST_F (line 335) | TEST_F(TestResource, ThrowsOnMissingStatus)
    function TEST_F (line 342) | TEST_F(TestResource, ThrowsOnInvalidStatus)
    function TEST_F (line 349) | TEST_F(TestResource, ThrowsOnNonZeroStatus)
    function TEST_F (line 356) | TEST_F(TestResource, ThrowsOnMissingValue)

FILE: test/session_test.cpp
  type test (line 6) | namespace test {
    class TestSession (line 10) | class TestSession : public ::testing::Test {
      method TestSession (line 12) | TestSession() : driver(GetDriver()) {}
      method ReplaceSpoiledSession (line 14) | void ReplaceSpoiledSession() {
    function TEST_F (line 21) | TEST_F(TestSession, GetsCapabilities)
    function TEST_F (line 30) | TEST_F(TestSession, StartsSecondBrowser) {
    function TEST_F (line 34) | TEST_F(TestSession, GetsCurrentWindow) {
    function TEST_F (line 38) | TEST_F(TestSession, GetsWindowHandle) {
    function TEST_F (line 42) | TEST_F(TestSession, SetsFocusToWindow) {
    function TEST_F (line 46) | TEST_F(TestSession, ClosesCurrentWindow) {
    function TEST_F (line 51) | TEST_F(TestSession, GetsWindowSize) {
    function TEST_F (line 56) | TEST_F(TestSession, SetsWindowSize) {
    function TEST_F (line 67) | TEST_F(TestSession, GetsWindowPosition) {
    function TEST_F (line 72) | TEST_F(TestSession, SetsWindowPosition) {
    function TEST_F (line 84) | TEST_F(TestSession, MaximizesWindow) {
    function TEST_F (line 89) | TEST_F(TestSession, GetsWindows) {
    function TEST_F (line 93) | TEST_F(TestSession, Navigates) {
    function TEST_F (line 99) | TEST_F(TestSession, NavigatesToTestPage) {
    function TEST_F (line 105) | TEST_F(TestSession, GoesBack) {
    function TEST_F (line 112) | TEST_F(TestSession, GoesForward) {
    function TEST_F (line 119) | TEST_F(TestSession, DoesRefresh) {
    function TEST_F (line 128) | TEST_F(TestSession, GetsPageSource) {
    function TEST_F (line 135) | TEST_F(TestSession, GetsPageTitle) {
    function TEST_F (line 140) | TEST_F(TestSession, GetsScreenshot) {
    function TEST_F (line 145) | TEST_F(TestSession, SetsTimeouts) {
    function TEST_F (line 151) | TEST_F(TestSession, SetsAsyncScriptTimeout) {
    function TEST_F (line 155) | TEST_F(TestSession, SetsImplicitTimeout) {
    function TEST_F (line 159) | TEST_F(TestSession, GetsActiveElement) {
    function Cookie (line 166) | Cookie FindCookie(
    type webdriverxx (line 178) | namespace webdriverxx {
      function PrintTo (line 180) | void PrintTo(const Cookie& c, ::std::ostream* os) {
    function TEST_F (line 186) | TEST_F(TestSession, SetsAndGetsCookies) {
    function TEST_F (line 195) | TEST_F(TestSession, SetsAllFieldsOfACookie) {
    function TEST_F (line 202) | TEST_F(TestSession, DeletesCookies) {
    function TEST_F (line 209) | TEST_F(TestSession, DeletesACookie) {

FILE: test/shared_test.cpp
  type test (line 5) | namespace test {
    type WidgetMonitor (line 9) | struct WidgetMonitor {
      method WidgetMonitor (line 10) | WidgetMonitor()
    type Simple (line 21) | struct Simple : SharedObjectBase {
      method Simple (line 23) | Simple() : n() {}
    class Widget (line 26) | class Widget : public SharedObjectBase {
      method Widget (line 28) | Widget(WidgetMonitor& monitor)
      method Widget (line 33) | Widget(const Widget& other)
      method Widget (line 38) | Widget& operator = (const Widget& other) {
    class WidgetSubclass (line 52) | class WidgetSubclass : public Widget {
      method WidgetSubclass (line 54) | WidgetSubclass(WidgetMonitor& monitor) : Widget(monitor) {}
    function TEST (line 57) | TEST(WidgetMonitor, ZeroByDefault) {
    function TEST (line 64) | TEST(WidgetMonitor, ShowsCreateAndDelete) {
    function TEST (line 72) | TEST(WidgetMonitor, ShowsCopy) {
    function TEST (line 81) | TEST(WidgetMonitor, MonitorsWidgetSubclass) {
    function TEST (line 89) | TEST(Shared, DeletesWidget) {
    function TEST (line 96) | TEST(Shared, ShareSingleWidget) {
    function TEST (line 109) | TEST(Shared, SupportsImplicitTypecasts) {
    function TEST (line 122) | TEST(Shared, CanBeUsedInBoolContext) {
    function TEST (line 129) | TEST(Shared, CanBeDereferenced) {
    function TEST (line 137) | TEST(Shared, CanBeCompared) {

FILE: test/to_string_test.cpp
  type test (line 4) | namespace test {
    function TEST (line 9) | TEST(ToString, ConvertsIntegralTypes) {
    type custom (line 20) | namespace custom {
      type A (line 22) | struct A {}
      type B (line 24) | struct B {}
      type C (line 29) | struct C {}
      function ToStream (line 31) | void ToStream(const C&, std::ostream& s) {
      type G (line 35) | struct G {}
      function PrintTo (line 36) | void PrintTo(const G&, std::ostream* s) {
    function TEST (line 42) | TEST(ToString, ConvertsCustomTypes) {
    function TEST (line 49) | TEST(ToString, ConvertsStrings) {
    function TEST (line 67) | TEST(ToString, ConvertsPointers) {
    function TEST (line 76) | TEST(ToString, ConvertsContainersOfIntegralTypes) {
    function TEST (line 86) | TEST(ToString, ConvertsContainersOfCustomTypes) {
    function TEST (line 97) | TEST(ToString, ConvertsContainersOfStrings) {

FILE: test/wait_match_test.cpp
  type test (line 4) | namespace test {
    function FunctionMatcher (line 9) | bool FunctionMatcher(int) { return true; }
    type FunctorMatcher (line 11) | struct FunctorMatcher {
    function TEST (line 17) | TEST(WaitForMatch, CanBeUsedWithFunctionFunctorAndLambda) {
    function TEST (line 23) | TEST(WaitForMatch, ReturnsMatchedValue) {
    function TEST (line 27) | TEST(WaitForMatch, DoesNotWaitIfValueIsMatched) {
    function TEST (line 34) | TEST(WaitForMatch, WaitsUntilValueIsMatched) {
    function TEST (line 42) | TEST(WaitForMatch, ThrowsExceptionOnTimeout) {
    function TEST (line 47) | TEST(WaitForMatch, ExplainsTimeout) {
    function TEST (line 59) | TEST(WaitForMatch, CanUseGMockMatchers) {
    function TEST (line 74) | TEST(WaitForMatch, ExplainsGMockMatcherMismatch) {

FILE: test/wait_test.cpp
  type test (line 4) | namespace test {
    function FunctionGetter (line 9) | int FunctionGetter() { return 123; }
    type FunctorGetter (line 11) | struct FunctorGetter {
    function TEST (line 17) | TEST(WaitForValue, CanBeUsedWithFunctionFunctorAndLambda) {
    function TEST (line 23) | TEST(WaitForValue, DoesNotWaitIfValueIsReturned) {
    function TEST (line 30) | TEST(WaitForValue, CallsGetterOnce) {
    function TEST (line 36) | TEST(WaitForValue, ThrowsExceptionOnTimeout) {
    function TEST (line 41) | TEST(WaitForValue, CallsGetterUntilItSucceeds) {
    function TEST (line 52) | TEST(WaitForValue, PassesErrorMessageFromGetter) {
    function TEST (line 65) | TEST(WaitUntil, DoesNotWaitIfValueNotFalsy) {
    function TEST (line 72) | TEST(WaitUntil, CallsGetterOnce) {
    function TEST (line 78) | TEST(WaitUntil, ThrowsExceptionOnTimeout) {
    function TEST (line 83) | TEST(WaitUntil, ThrowsExceptionOnTimeout2) {

FILE: test/webdriver_test.cpp
  type test (line 5) | namespace test {
    function TEST (line 9) | TEST(WebDriver, CreatesSession) {
    function TEST (line 17) | TEST(WebDriver, DeletesSessionOnDestruction) {
    function TEST (line 28) | TEST(WebDriver, IsCopyable) {
    function TEST (line 39) | TEST(WebDriver, CopyableToClient) {
    function TEST (line 46) | TEST(WebDriver, CopyableToSession) {
    function TEST (line 53) | TEST(WebDriver, AndSatelliteObjectsHasNoLifetimeIssues) {
Condensed preview — 83 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (225K chars).
[
  {
    "path": ".gitignore",
    "chars": 242,
    "preview": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dyl"
  },
  {
    "path": ".travis.yml",
    "chars": 369,
    "preview": "language: cpp\n\ncompiler:\n    - gcc\n    - clang\n\nbranches:\n  only:\n    - master\n    - dev\n\nbefore_script:\n    - mkdir bui"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 619,
    "preview": "cmake_minimum_required(VERSION 2.8)\nproject(webdriverxx)\n\nif (NOT MSVC)\n\tinclude(CheckCXXCompilerFlag)\n\tCHECK_CXX_COMPIL"
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Sergey Kogan\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 7385,
    "preview": "\n# Webdriver++\n\nA C++ client library for [Selenium Webdriver](http://www.seleniumhq.org/).\nYou can use this library in a"
  },
  {
    "path": "include/webdriverxx/browsers/chrome.h",
    "chars": 1878,
    "preview": "#ifndef WEBDRIVERXX_BROWSERS_CHROME_H\n#define WEBDRIVERXX_BROWSERS_CHROME_H\n\n#include \"../capabilities.h\"\n\nnamespace web"
  },
  {
    "path": "include/webdriverxx/browsers/firefox.h",
    "chars": 746,
    "preview": "#ifndef WEBDRIVERXX_BROWSERS_FIREFOX_H\n#define WEBDRIVERXX_BROWSERS_FIREFOX_H\n\n#include \"../capabilities.h\"\n\nnamespace w"
  },
  {
    "path": "include/webdriverxx/browsers/ie.h",
    "chars": 1982,
    "preview": "#ifndef WEBDRIVERXX_BROWSERS_IE_H\n#define WEBDRIVERXX_BROWSERS_IE_H\n\n#include \"../capabilities.h\"\n\nnamespace webdriverxx"
  },
  {
    "path": "include/webdriverxx/browsers/phantom.h",
    "chars": 588,
    "preview": "#ifndef WEBDRIVERXX_BROWSERS_PHANTOM_H\n#define WEBDRIVERXX_BROWSERS_PHANTOM_H\n\n#include \"../capabilities.h\"\n\nnamespace w"
  },
  {
    "path": "include/webdriverxx/by.h",
    "chars": 1099,
    "preview": "#ifndef WEBDRIVERXX_BY_H\n#define WEBDRIVERXX_BY_H\n\n#include <string>\n\nnamespace webdriverxx {\n\nclass By { // copyable\npu"
  },
  {
    "path": "include/webdriverxx/capabilities.h",
    "chars": 6923,
    "preview": "#ifndef WEBDRIVERXX_CAPABILITIES_H\n#define WEBDRIVERXX_CAPABILITIES_H\n\n#include \"conversions.h\"\n#include \"picojson.h\"\n#i"
  },
  {
    "path": "include/webdriverxx/client.h",
    "chars": 990,
    "preview": "#ifndef WEBDRIVERXX_CLIENT_H\n#define WEBDRIVERXX_CLIENT_H\n\n#include \"session.h\"\n#include \"capabilities.h\"\n#include \"deta"
  },
  {
    "path": "include/webdriverxx/client.inl",
    "chars": 2091,
    "preview": "#include \"conversions.h\"\n#include \"detail/shared.h\"\n#include \"detail/error_handling.h\"\n#include \"detail/types.h\"\n#includ"
  },
  {
    "path": "include/webdriverxx/conversions.h",
    "chars": 7850,
    "preview": "#ifndef WEBDRIVERXX_CONVERSIONS_H\n#define WEBDRIVERXX_CONVERSIONS_H\n\n#include \"types.h\"\n#include \"detail/error_handling."
  },
  {
    "path": "include/webdriverxx/detail/error_handling.h",
    "chars": 1513,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_ERROR_HANDLING_H\n#define WEBDRIVERXX_DETAIL_ERROR_HANDLING_H\n\n#include \"../errors.h\"\n#include"
  },
  {
    "path": "include/webdriverxx/detail/factories.h",
    "chars": 508,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_FACTORIES_H\n#define WEBDRIVERXX_DETAIL_FACTORIES_H\n\n#include \"shared.h\"\n#include <string>\n\nna"
  },
  {
    "path": "include/webdriverxx/detail/factories_impl.h",
    "chars": 898,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_FACTORIES_IMPL_H\n#define WEBDRIVERXX_DETAIL_FACTORIES_IMPL_H\n\n#include \"factories.h\"\n#include"
  },
  {
    "path": "include/webdriverxx/detail/finder.h",
    "chars": 618,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_FINDER_H\n#define WEBDRIVERXX_DETAIL_FINDER_H\n\n#include \"shared.h\"\n#include \"resource.h\"\n#incl"
  },
  {
    "path": "include/webdriverxx/detail/finder.inl",
    "chars": 1448,
    "preview": "#include \"../conversions.h\"\n#include \"../element.h\"\n#include \"types.h\"\n#include <algorithm>\n\nnamespace webdriverxx {\nnam"
  },
  {
    "path": "include/webdriverxx/detail/http_client.h",
    "chars": 567,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_HTTP_CLIENT_H\n#define WEBDRIVERXX_DETAIL_HTTP_CLIENT_H\n\n#include <string>\n\nnamespace webdrive"
  },
  {
    "path": "include/webdriverxx/detail/http_connection.h",
    "chars": 1097,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_HTTP_CONNECTION_H\n#define WEBDRIVERXX_DETAIL_HTTP_CONNECTION_H\n\n#include \"http_client.h\"\n#inc"
  },
  {
    "path": "include/webdriverxx/detail/http_request.h",
    "chars": 4390,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_HTTP_REQUEST_H\n#define WEBDRIVERXX_DETAIL_HTTP_REQUEST_H\n\n#include \"error_handling.h\"\n#includ"
  },
  {
    "path": "include/webdriverxx/detail/keyboard.h",
    "chars": 741,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_KEYBOARD_H\n#define WEBDRIVERXX_DETAIL_KEYBOARD_H\n\n#include \"resource.h\"\n#include \"../conversi"
  },
  {
    "path": "include/webdriverxx/detail/meta_tools.h",
    "chars": 906,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_META_TOOLS_H\n#define WEBDRIVERXX_DETAIL_META_TOOLS_H\n\n#include <type_traits>\n#include <utilit"
  },
  {
    "path": "include/webdriverxx/detail/resource.h",
    "chars": 7188,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_RESOURCE_H\n#define WEBDRIVERXX_DETAIL_RESOURCE_H\n\n#include \"error_handling.h\"\n#include \"http_"
  },
  {
    "path": "include/webdriverxx/detail/shared.h",
    "chars": 1534,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_SHARED_H\n#define WEBDRIVERXX_DETAIL_SHARED_H\n\n#include <algorithm>\n\nnamespace webdriverxx {\nn"
  },
  {
    "path": "include/webdriverxx/detail/time.h",
    "chars": 960,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_TIME_H\n#define WEBDRIVERXX_DETAIL_TIME_H\n\n#include \"error_handling.h\"\n#include \"../types.h\"\n\n"
  },
  {
    "path": "include/webdriverxx/detail/to_string.h",
    "chars": 2555,
    "preview": "#ifndef WEBDRIVERXX_TO_STRING_H\n#define WEBDRIVERXX_TO_STRING_H\n\n#include \"meta_tools.h\"\n#include <string>\n#include <sst"
  },
  {
    "path": "include/webdriverxx/detail/types.h",
    "chars": 1781,
    "preview": "#ifndef WEBDRIVERXX_DETAIL_TYPES_H\n#define WEBDRIVERXX_DETAIL_TYPES_H\n\n#include \"../conversions.h\"\n#include \"../capabili"
  },
  {
    "path": "include/webdriverxx/element.h",
    "chars": 1721,
    "preview": "#ifndef WEBDRIVERXX_ELEMENT_H\n#define WEBDRIVERXX_ELEMENT_H\n\n#include \"by.h\"\n#include \"types.h\"\n#include \"keys.h\"\n#inclu"
  },
  {
    "path": "include/webdriverxx/element.inl",
    "chars": 3074,
    "preview": "#include \"conversions.h\"\n#include \"detail/finder.h\"\n#include \"detail/error_handling.h\"\n\nnamespace webdriverxx {\n\ninline\n"
  },
  {
    "path": "include/webdriverxx/errors.h",
    "chars": 304,
    "preview": "#ifndef WEBDRIVERXX_ERRORS_H\n#define WEBDRIVERXX_ERRORS_H\n\n#include <stdexcept>\n#include <string>\n\nnamespace webdriverxx"
  },
  {
    "path": "include/webdriverxx/js_args.h",
    "chars": 1003,
    "preview": "#ifndef WEBDRIVERXX_JS_ARGS_H\n#define WEBDRIVERXX_JS_ARGS_H \n\n#include \"conversions.h\"\n#include \"picojson.h\"\n\nnamespace "
  },
  {
    "path": "include/webdriverxx/keys.h",
    "chars": 2899,
    "preview": "#ifndef WEBDRIVERXX_KEYS_H\n#define WEBDRIVERXX_KEYS_H \n\n#include <string>\n#include <vector>\n\nnamespace webdriverxx {\nnam"
  },
  {
    "path": "include/webdriverxx/picojson.h",
    "chars": 35469,
    "preview": "/*\n * Copyright 2009-2010 Cybozu Labs, Inc.\n * Copyright 2011-2014 Kazuho Oku\n * All rights reserved.\n *\n * Redistributi"
  },
  {
    "path": "include/webdriverxx/response_status_code.h",
    "chars": 3561,
    "preview": "#ifndef WEBDRIVERXX_RESPONSE_STATUS_CODE_H\n#define WEBDRIVERXX_RESPONSE_STATUS_CODE_H \n\nnamespace webdriverxx {\nnamespac"
  },
  {
    "path": "include/webdriverxx/session.h",
    "chars": 4106,
    "preview": "#ifndef WEBDRIVERXX_SESSION_H\n#define WEBDRIVERXX_SESSION_H\n\n#include \"element.h\"\n#include \"window.h\"\n#include \"by.h\"\n#i"
  },
  {
    "path": "include/webdriverxx/session.inl",
    "chars": 8933,
    "preview": "#include \"conversions.h\"\n#include \"detail/error_handling.h\"\n#include \"detail/types.h\"\n#include <algorithm>\n\nnamespace we"
  },
  {
    "path": "include/webdriverxx/types.h",
    "chars": 1553,
    "preview": "#ifndef WEBDRIVERXX_TYPES_H\n#define WEBDRIVERXX_TYPES_H\n\n#include <string>\n\nnamespace webdriverxx {\n\ntypedef unsigned lo"
  },
  {
    "path": "include/webdriverxx/wait.h",
    "chars": 2498,
    "preview": "#ifndef WEBDRIVERXX_WAIT_H\n#define WEBDRIVERXX_WAIT_H\n\n#include \"detail/error_handling.h\"\n#include \"detail/time.h\"\n#incl"
  },
  {
    "path": "include/webdriverxx/wait_match.h",
    "chars": 3391,
    "preview": "#ifndef WEBDRIVERXX_WAIT_MATCH_H\n#define WEBDRIVERXX_WAIT_MATCH_H\n\n#include \"wait.h\"\n#include \"detail/to_string.h\"\n#incl"
  },
  {
    "path": "include/webdriverxx/webdriver.h",
    "chars": 961,
    "preview": "#ifndef WEBDRIVERXX_WEBDRIVER_H\n#define WEBDRIVERXX_WEBDRIVER_H\n\n#include \"client.h\"\n#include \"session.h\"\n\nnamespace web"
  },
  {
    "path": "include/webdriverxx/window.h",
    "chars": 985,
    "preview": "#ifndef WEBDRIVERXX_WINDOW_H\n#define WEBDRIVERXX_WINDOW_H\n\n#include \"types.h\"\n#include \"conversions.h\"\n#include \"detail/"
  },
  {
    "path": "include/webdriverxx.h",
    "chars": 316,
    "preview": "#ifndef WEBDRIVERXX_H\n#define WEBDRIVERXX_H\n\n#include \"webdriverxx/webdriver.h\"\n#include \"webdriverxx/browsers/chrome.h\""
  },
  {
    "path": "test/CMakeLists.txt",
    "chars": 5183,
    "preview": "set(HEADER_FILES\n\t../include/webdriverxx.h \n\t../include/webdriverxx/by.h \n\t../include/webdriverxx/capabilities.h \n\t../in"
  },
  {
    "path": "test/alerts_test.cpp",
    "chars": 1218,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace "
  },
  {
    "path": "test/browsers_test.cpp",
    "chars": 1653,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/browsers/chrome.h>\n#include <webdriverxx/browsers/firefox.h>\n#include <we"
  },
  {
    "path": "test/capabilities_test.cpp",
    "chars": 2020,
    "preview": "#include <webdriverxx/capabilities.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace webdriverxx;\n\nTEST(Cap"
  },
  {
    "path": "test/client_test.cpp",
    "chars": 800,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/client.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace web"
  },
  {
    "path": "test/conversions_test.cpp",
    "chars": 5274,
    "preview": "#include <webdriverxx/conversions.h>\n#include <gtest/gtest.h>\n#include <vector>\n#include <list>\n\nnamespace test {\n\nusing"
  },
  {
    "path": "test/element_test.cpp",
    "chars": 2592,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace "
  },
  {
    "path": "test/environment.h",
    "chars": 2885,
    "preview": "#ifndef WEBDRIVERXX_ENVIRONMENT_H\n#define WEBDRIVERXX_ENVIRONMENT_H\n\n#include <webdriverxx/webdriver.h>\n#include <webdri"
  },
  {
    "path": "test/examples_test.cpp",
    "chars": 1729,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#ifndef WEBDRIVERXX_ENABLE_GMOCK_MATCHERS\n#define WEBDRIVERX"
  },
  {
    "path": "test/finder_test.cpp",
    "chars": 3083,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace "
  },
  {
    "path": "test/frames_test.cpp",
    "chars": 1736,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace "
  },
  {
    "path": "test/http_connection_test.cpp",
    "chars": 722,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/detail/http_connection.h>\n#include <gtest/gtest.h>\n#include <string>\n\nnam"
  },
  {
    "path": "test/js_test.cpp",
    "chars": 6066,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n#include <list>\n\nnamespace test {\n\n"
  },
  {
    "path": "test/keyboard_test.cpp",
    "chars": 1245,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <webdriverxx/keys.h>\n#include <gtest/gtest.h>\n\nname"
  },
  {
    "path": "test/main.cpp",
    "chars": 1382,
    "preview": "#include \"environment.h\"\n#include <gtest/gtest.h>\n#include <string>\n\nnamespace test {\n\nEnvironment* Environment::instanc"
  },
  {
    "path": "test/mouse_test.cpp",
    "chars": 2981,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/session.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace we"
  },
  {
    "path": "test/pages/alerts.html",
    "chars": 29,
    "preview": "<html>\n<body>\n</body>\n</html>"
  },
  {
    "path": "test/pages/element.html",
    "chars": 311,
    "preview": "<html>\n<body>\n<div id=\"first_div\"></div>\n<div id=\"second_div\"></div>\n<div id=\"third_div\"></div>\n<div id=\"element_with_te"
  },
  {
    "path": "test/pages/finder.html",
    "chars": 298,
    "preview": "<html>\n<body>\n<div id=\"test_id\"></div>\n<div class=\"test_class\"></div>\n<div id=\"css_selectable\"></div>\n<input name=\"test_"
  },
  {
    "path": "test/pages/frame1.html",
    "chars": 61,
    "preview": "<html>\n<body>\n<input id=\"tag\" value=\"frame1\">\n</body>\n</html>"
  },
  {
    "path": "test/pages/frame2.html",
    "chars": 61,
    "preview": "<html>\n<body>\n<input id=\"tag\" value=\"frame2\">\n</body>\n</html>"
  },
  {
    "path": "test/pages/frame3.html",
    "chars": 61,
    "preview": "<html>\n<body>\n<input id=\"tag\" value=\"frame3\">\n</body>\n</html>"
  },
  {
    "path": "test/pages/frames.html",
    "chars": 178,
    "preview": "<html>\n<body>\n<input id=\"tag\" value=\"top_frame\">\n<iframe src=\"frameset.html\" name=\"frameset_name\"></iframe>\n<iframe src="
  },
  {
    "path": "test/pages/frameset.html",
    "chars": 148,
    "preview": "<html>\n<frameset rows=\"*\" cols=\"50,*\">\n<frame src=\"frame1.html\" name=\"frame1_name\">\n<frame src=\"frame2.html\" name=\"frame"
  },
  {
    "path": "test/pages/js.html",
    "chars": 37,
    "preview": "<html>\n<body>\n<input>\n</body>\n</html>"
  },
  {
    "path": "test/pages/keyboard.html",
    "chars": 72,
    "preview": "<html>\n<body>\n<input name=\"first\">\n<input name=\"second\">\n</body>\n</html>"
  },
  {
    "path": "test/pages/mouse.html",
    "chars": 883,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n<script type=\"text/javascript\">\n\tfunction report_click(event) {\n\t\tdocument.getElementById("
  },
  {
    "path": "test/pages/navigation1.html",
    "chars": 50,
    "preview": "<html>\n<body>\nNavigation 1\n<input>\n</body>\n</html>"
  },
  {
    "path": "test/pages/navigation2.html",
    "chars": 42,
    "preview": "<html>\n<body>\nNavigation 2\n</body>\n</html>"
  },
  {
    "path": "test/pages/redirect.html",
    "chars": 94,
    "preview": "<html>\n<body>\n<script type=\"text/javascript\">location = \"target.html\"</script>\n</body>\n</html>"
  },
  {
    "path": "test/pages/session.html",
    "chars": 78,
    "preview": "<html>\n<head>\n<title>Test title</title>\n</head>\n<body>\n<input>\n</body>\n</html>"
  },
  {
    "path": "test/pages/webdriver.html",
    "chars": 37,
    "preview": "<html>\n<body>\n<input>\n</body>\n</html>"
  },
  {
    "path": "test/resource_test.cpp",
    "chars": 11769,
    "preview": "#include <webdriverxx/detail/resource.h>\n#include <webdriverxx/detail/http_client.h>\n#include <webdriverxx/response_stat"
  },
  {
    "path": "test/session_test.cpp",
    "chars": 5979,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/session.h>\n#include <gtest/gtest.h>\n#include <algorithm>\n\nnamespace test "
  },
  {
    "path": "test/shared_test.cpp",
    "chars": 2752,
    "preview": "#include <webdriverxx/detail/shared.h>\n#include <gtest/gtest.h>\n#include <cassert>\n\nnamespace test {\n\nusing namespace we"
  },
  {
    "path": "test/to_string_test.cpp",
    "chars": 2586,
    "preview": "#include <webdriverxx/detail/to_string.h>\r\n#include <gtest/gtest.h>\r\n\r\nnamespace test {\r\n\r\nusing namespace webdriverxx;\r"
  },
  {
    "path": "test/wait_match_test.cpp",
    "chars": 2853,
    "preview": "#include <webdriverxx/wait_match.h>\r\n#include <gtest/gtest.h>\r\n\r\nnamespace test {\r\n\r\nusing namespace webdriverxx;\r\nusing"
  },
  {
    "path": "test/wait_test.cpp",
    "chars": 2241,
    "preview": "#include <webdriverxx/wait.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace webdriverxx;\nusing namespace w"
  },
  {
    "path": "test/webdriver_test.cpp",
    "chars": 2404,
    "preview": "#include \"environment.h\"\n#include <webdriverxx/webdriver.h>\n#include <gtest/gtest.h>\n\nnamespace test {\n\nusing namespace "
  }
]

About this extraction

This page contains the full source code of the durdyev/webdriverxx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 83 files (200.1 KB), approximately 55.9k tokens, and a symbol index with 422 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!