use actions::{ActionSequence};
use capabilities::{SpecNewSessionParameters, LegacyNewSessionParameters,
CapabilitiesMatching, BrowserCapabilities, Capabilities};
use common::{Date, Nullable, WebElement, FrameId, LocatorStrategy};
use error::{WebDriverResult, WebDriverError, ErrorStatus};
use httpapi::{Route, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
use regex::Captures;
use rustc_serialize::json;
use rustc_serialize::json::{ToJson, Json};
use std::collections::BTreeMap;
#[derive(Debug, PartialEq)]
pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
NewSession(NewSessionParameters),
DeleteSession,
Get(GetParameters),
GetCurrentUrl,
GoBack,
GoForward,
Refresh,
GetTitle,
GetPageSource,
GetWindowHandle,
GetWindowHandles,
CloseWindow,
GetWindowRect,
SetWindowRect(WindowRectParameters),
MinimizeWindow,
MaximizeWindow,
FullscreenWindow,
SwitchToWindow(SwitchToWindowParameters),
SwitchToFrame(SwitchToFrameParameters),
SwitchToParentFrame,
FindElement(LocatorParameters),
FindElements(LocatorParameters),
FindElementElement(WebElement, LocatorParameters),
FindElementElements(WebElement, LocatorParameters),
GetActiveElement,
IsDisplayed(WebElement),
IsSelected(WebElement),
GetElementAttribute(WebElement, String),
GetElementProperty(WebElement, String),
GetCSSValue(WebElement, String),
GetElementText(WebElement),
GetElementTagName(WebElement),
GetElementRect(WebElement),
IsEnabled(WebElement),
ExecuteScript(JavascriptCommandParameters),
ExecuteAsyncScript(JavascriptCommandParameters),
GetCookies,
GetNamedCookie(String),
AddCookie(AddCookieParameters),
DeleteCookies,
DeleteCookie(String),
GetTimeouts,
SetTimeouts(TimeoutsParameters),
ElementClick(WebElement),
ElementTap(WebElement),
ElementClear(WebElement),
ElementSendKeys(WebElement, SendKeysParameters),
PerformActions(ActionsParameters),
ReleaseActions,
DismissAlert,
AcceptAlert,
GetAlertText,
SendAlertText(SendKeysParameters),
TakeScreenshot,
TakeElementScreenshot(WebElement),
Status,
Extension(T)
}
pub trait WebDriverExtensionCommand : Clone + Send + PartialEq {
fn parameters_json(&self) -> Option<Json>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct VoidWebDriverExtensionCommand;
impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
fn parameters_json(&self) -> Option<Json> {
panic!("No extensions implemented");
}
}
#[derive(Debug, PartialEq)]
pub struct WebDriverMessage <U: WebDriverExtensionRoute=VoidWebDriverExtensionRoute> {
pub session_id: Option<String>,
pub command: WebDriverCommand<U::Command>,
}
impl<U: WebDriverExtensionRoute> WebDriverMessage<U> {
pub fn new(session_id: Option<String>,
command: WebDriverCommand<U::Command>)
-> WebDriverMessage<U> {
WebDriverMessage {
session_id: session_id,
command: command,
}
}
pub fn from_http(match_type: Route<U>,
params: &Captures,
raw_body: &str,
requires_body: bool)
-> WebDriverResult<WebDriverMessage<U>> {
let session_id = WebDriverMessage::<U>::get_session_id(params);
let body_data = try!(WebDriverMessage::<U>::decode_body(raw_body, requires_body));
let command = match match_type {
Route::NewSession => {
let parameters: NewSessionParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::NewSession(parameters)
},
Route::DeleteSession => WebDriverCommand::DeleteSession,
Route::Get => {
let parameters: GetParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::Get(parameters)
},
Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
Route::GoBack => WebDriverCommand::GoBack,
Route::GoForward => WebDriverCommand::GoForward,
Route::Refresh => WebDriverCommand::Refresh,
Route::GetTitle => WebDriverCommand::GetTitle,
Route::GetPageSource => WebDriverCommand::GetPageSource,
Route::GetWindowHandle => WebDriverCommand::GetWindowHandle,
Route::GetWindowHandles => WebDriverCommand::GetWindowHandles,
Route::CloseWindow => WebDriverCommand::CloseWindow,
Route::GetTimeouts => WebDriverCommand::GetTimeouts,
Route::SetTimeouts => {
let parameters: TimeoutsParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::SetTimeouts(parameters)
},
Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => WebDriverCommand::GetWindowRect,
Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
let parameters: WindowRectParameters = Parameters::from_json(&body_data)?;
WebDriverCommand::SetWindowRect(parameters)
},
Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
Route::SwitchToWindow => {
let parameters: SwitchToWindowParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::SwitchToWindow(parameters)
}
Route::SwitchToFrame => {
let parameters: SwitchToFrameParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::SwitchToFrame(parameters)
},
Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
Route::FindElement => {
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::FindElement(parameters)
},
Route::FindElements => {
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::FindElements(parameters)
},
Route::FindElementElement => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::FindElementElement(element, parameters)
},
Route::FindElementElements => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::FindElementElements(element, parameters)
},
Route::GetActiveElement => WebDriverCommand::GetActiveElement,
Route::IsDisplayed => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::IsDisplayed(element)
},
Route::IsSelected => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::IsSelected(element)
},
Route::GetElementAttribute => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let attr = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing name parameter").as_str();
WebDriverCommand::GetElementAttribute(element, attr.into())
},
Route::GetElementProperty => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let property = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing name parameter").as_str();
WebDriverCommand::GetElementProperty(element, property.into())
},
Route::GetCSSValue => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let property = try_opt!(params.name("propertyName"),
ErrorStatus::InvalidArgument,
"Missing propertyName parameter").as_str();
WebDriverCommand::GetCSSValue(element, property.into())
},
Route::GetElementText => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::GetElementText(element)
},
Route::GetElementTagName => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::GetElementTagName(element)
},
Route::GetElementRect => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::GetElementRect(element)
},
Route::IsEnabled => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::IsEnabled(element)
},
Route::ElementClick => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::ElementClick(element)
},
Route::ElementTap => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::ElementTap(element)
},
Route::ElementClear => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::ElementClear(element)
},
Route::ElementSendKeys => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::ElementSendKeys(element, parameters)
},
Route::ExecuteScript => {
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::ExecuteScript(parameters)
},
Route::ExecuteAsyncScript => {
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::ExecuteAsyncScript(parameters)
},
Route::GetCookies => {
WebDriverCommand::GetCookies
},
Route::GetNamedCookie => {
let name = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing 'name' parameter").as_str().into();
WebDriverCommand::GetNamedCookie(name)
},
Route::AddCookie => {
let parameters: AddCookieParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::AddCookie(parameters)
},
Route::DeleteCookies => {
WebDriverCommand::DeleteCookies
},
Route::DeleteCookie => {
let name = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing name parameter").as_str().into();
WebDriverCommand::DeleteCookie(name)
},
Route::PerformActions => {
let parameters: ActionsParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::PerformActions(parameters)
},
Route::ReleaseActions => {
WebDriverCommand::ReleaseActions
},
Route::DismissAlert => {
WebDriverCommand::DismissAlert
},
Route::AcceptAlert => {
WebDriverCommand::AcceptAlert
},
Route::GetAlertText => {
WebDriverCommand::GetAlertText
},
Route::SendAlertText => {
let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
WebDriverCommand::SendAlertText(parameters)
},
Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
Route::TakeElementScreenshot => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::TakeElementScreenshot(element)
},
Route::Status => WebDriverCommand::Status,
Route::Extension(ref extension) => {
try!(extension.command(params, &body_data))
}
};
Ok(WebDriverMessage::new(session_id, command))
}
fn get_session_id(params: &Captures) -> Option<String> {
params.name("sessionId").map(|x| x.as_str().into())
}
fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Json> {
if requires_body {
match Json::from_str(body) {
Ok(x @ Json::Object(_)) => Ok(x),
Ok(_) => {
Err(WebDriverError::new(ErrorStatus::InvalidArgument,
"Body was not a JSON Object"))
}
Err(json::ParserError::SyntaxError(_, line, col)) => {
let msg = format!("Failed to decode request as JSON: \"{}\"", body);
let stack = format!("Syntax error at :{}:{}", line, col);
Err(WebDriverError::new_with_stack(ErrorStatus::InvalidArgument, msg, stack))
}
Err(json::ParserError::IoError(e)) => {
Err(WebDriverError::new(ErrorStatus::InvalidArgument,
format!("I/O error whilst decoding body: {}", e)))
}
}
} else {
Ok(Json::Null)
}
}
}
impl <U:WebDriverExtensionRoute> ToJson for WebDriverMessage<U> {
fn to_json(&self) -> Json {
let parameters = match self.command {
WebDriverCommand::AcceptAlert |
WebDriverCommand::CloseWindow |
WebDriverCommand::ReleaseActions |
WebDriverCommand::DeleteCookie(_) |
WebDriverCommand::DeleteCookies |
WebDriverCommand::DeleteSession |
WebDriverCommand::DismissAlert |
WebDriverCommand::ElementClear(_) |
WebDriverCommand::ElementClick(_) |
WebDriverCommand::ElementTap(_) |
WebDriverCommand::GetActiveElement |
WebDriverCommand::GetAlertText |
WebDriverCommand::GetNamedCookie(_) |
WebDriverCommand::GetCookies |
WebDriverCommand::GetCSSValue(_, _) |
WebDriverCommand::GetCurrentUrl |
WebDriverCommand::GetElementAttribute(_, _) |
WebDriverCommand::GetElementProperty(_, _) |
WebDriverCommand::GetElementRect(_) |
WebDriverCommand::GetElementTagName(_) |
WebDriverCommand::GetElementText(_) |
WebDriverCommand::GetPageSource |
WebDriverCommand::GetTimeouts |
WebDriverCommand::GetTitle |
WebDriverCommand::GetWindowHandle |
WebDriverCommand::GetWindowHandles |
WebDriverCommand::GetWindowRect |
WebDriverCommand::GoBack |
WebDriverCommand::GoForward |
WebDriverCommand::IsDisplayed(_) |
WebDriverCommand::IsEnabled(_) |
WebDriverCommand::IsSelected(_) |
WebDriverCommand::MinimizeWindow |
WebDriverCommand::MaximizeWindow |
WebDriverCommand::FullscreenWindow |
WebDriverCommand::NewSession(_) |
WebDriverCommand::Refresh |
WebDriverCommand::Status |
WebDriverCommand::SwitchToParentFrame |
WebDriverCommand::TakeElementScreenshot(_) |
WebDriverCommand::TakeScreenshot => {
None
},
WebDriverCommand::AddCookie(ref x) => Some(x.to_json()),
WebDriverCommand::ElementSendKeys(_, ref x) => Some(x.to_json()),
WebDriverCommand::ExecuteAsyncScript(ref x) |
WebDriverCommand::ExecuteScript(ref x) => Some(x.to_json()),
WebDriverCommand::FindElementElement(_, ref x) => Some(x.to_json()),
WebDriverCommand::FindElementElements(_, ref x) => Some(x.to_json()),
WebDriverCommand::FindElement(ref x) => Some(x.to_json()),
WebDriverCommand::FindElements(ref x) => Some(x.to_json()),
WebDriverCommand::Get(ref x) => Some(x.to_json()),
WebDriverCommand::PerformActions(ref x) => Some(x.to_json()),
WebDriverCommand::SendAlertText(ref x) => Some(x.to_json()),
WebDriverCommand::SetTimeouts(ref x) => Some(x.to_json()),
WebDriverCommand::SetWindowRect(ref x) => Some(x.to_json()),
WebDriverCommand::SwitchToFrame(ref x) => Some(x.to_json()),
WebDriverCommand::SwitchToWindow(ref x) => Some(x.to_json()),
WebDriverCommand::Extension(ref x) => x.parameters_json(),
};
let mut data = BTreeMap::new();
if let Some(parameters) = parameters {
data.insert("parameters".to_string(), parameters);
}
Json::Object(data)
}
}
pub trait Parameters: Sized {
fn from_json(body: &Json) -> WebDriverResult<Self>;
}
/// Wrapper around the two supported variants of new session paramters
///
/// The Spec variant is used for storing spec-compliant parameters whereas
/// the legacy variant is used to store desiredCapabilities/requiredCapabilities
/// parameters, and is intended to minimise breakage as we transition users to
/// the spec design.
#[derive(Debug, PartialEq)]
pub enum NewSessionParameters {
Spec(SpecNewSessionParameters),
Legacy(LegacyNewSessionParameters),
}
impl Parameters for NewSessionParameters {
fn from_json(body: &Json) -> WebDriverResult<NewSessionParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::UnknownError,
"Message body was not an object");
if data.get("capabilities").is_some() {
Ok(NewSessionParameters::Spec(try!(SpecNewSessionParameters::from_json(body))))
} else {
Ok(NewSessionParameters::Legacy(try!(LegacyNewSessionParameters::from_json(body))))
}
}
}
impl ToJson for NewSessionParameters {
fn to_json(&self) -> Json {
match self {
&NewSessionParameters::Spec(ref x) => x.to_json(),
&NewSessionParameters::Legacy(ref x) => x.to_json()
}
}
}
impl CapabilitiesMatching for NewSessionParameters {
fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
-> WebDriverResult<Option<Capabilities>> {
match self {
&NewSessionParameters::Spec(ref x) => x.match_browser(browser_capabilities),
&NewSessionParameters::Legacy(ref x) => x.match_browser(browser_capabilities)
}
}
}
#[derive(Debug, PartialEq)]
pub struct GetParameters {
pub url: String
}
impl Parameters for GetParameters {
fn from_json(body: &Json) -> WebDriverResult<GetParameters> {
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
"Message body was not an object");
let url = try_opt!(
try_opt!(data.get("url"),
ErrorStatus::InvalidArgument,
"Missing 'url' parameter").as_string(),
ErrorStatus::InvalidArgument,
"'url' not a string");
Ok(GetParameters {
url: url.to_string()
})
}
}
impl ToJson for GetParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("url".to_string(), self.url.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct TimeoutsParameters {
pub script: Option<u64>,
pub page_load: Option<u64>,
pub implicit: Option<u64>,
}
impl Parameters for TimeoutsParameters {
fn from_json(body: &Json) -> WebDriverResult<TimeoutsParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::UnknownError,
"Message body was not an object");
let script = match data.get("script") {
Some(json) => {
Some(try_opt!(json.as_u64(),
ErrorStatus::InvalidArgument,
"Script timeout duration was not a signed integer"))
}
None => None,
};
let page_load = match data.get("pageLoad") {
Some(json) => {
Some(try_opt!(json.as_u64(),
ErrorStatus::InvalidArgument,
"Page load timeout duration was not a signed integer"))
}
None => None,
};
let implicit = match data.get("implicit") {
Some(json) => {
Some(try_opt!(json.as_u64(),
ErrorStatus::InvalidArgument,
"Implicit timeout duration was not a signed integer"))
}
None => None,
};
Ok(TimeoutsParameters {
script: script,
page_load: page_load,
implicit: implicit,
})
}
}
impl ToJson for TimeoutsParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
if let Some(ms) = self.script {
data.insert("script".into(), ms.to_json());
}
if let Some(ms) = self.page_load {
data.insert("pageLoad".into(), ms.to_json());
}
if let Some(ms) = self.implicit {
data.insert("implicit".into(), ms.to_json());
}
Json::Object(data)
}
}
/// A top-level browsing context’s window rect is a dictionary of the
/// [`screenX`], [`screenY`], `width`, and `height` attributes of the
/// `WindowProxy`.
///
/// In some user agents the operating system’s window dimensions, including
/// decorations, are provided by the proprietary `window.outerWidth` and
/// `window.outerHeight` DOM properties.
///
/// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
/// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
#[derive(Debug, PartialEq)]
pub struct WindowRectParameters {
pub x: Nullable<i32>,
pub y: Nullable<i32>,
pub width: Nullable<i32>,
pub height: Nullable<i32>,
}
impl Parameters for WindowRectParameters {
fn from_json(body: &Json) -> WebDriverResult<WindowRectParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::InvalidArgument, "Message body was not an object");
let x = match data.get("x") {
Some(json) => try!(Nullable::from_json(json, |n| {
let x = try_opt!(
n.as_f64(),
ErrorStatus::InvalidArgument,
"'x' is not a number"
) as i64;
if x < i32::min_value() as i64 || x > i32::max_value() as i64 {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"'x' is larger than i32",
));
}
Ok(x as i32)
})),
None => Nullable::Null,
};
let y = match data.get("y") {
Some(json) => try!(Nullable::from_json(json, |n| {
let y = try_opt!(
n.as_f64(),
ErrorStatus::InvalidArgument,
"'y' is not a number"
) as i64;
if y < i32::min_value() as i64 || y > i32::max_value() as i64 {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"'y' is larger than i32",
));
}
Ok(y as i32)
})),
None => Nullable::Null,
};
let width = match data.get("width") {
Some(json) => try!(Nullable::from_json(json, |n| {
let width = try_opt!(
n.as_f64(),
ErrorStatus::InvalidArgument,
"'width' is not a number"
) as i64;
if width < 0 || width > i32::max_value() as i64 {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"'width' is larger than i32",
));
}
Ok(width as i32)
})),
None => Nullable::Null,
};
let height = match data.get("height") {
Some(json) => try!(Nullable::from_json(json, |n| {
let height = try_opt!(
n.as_f64(),
ErrorStatus::InvalidArgument,
"'height' is not a positive integer"
) as i64;
if height < 0 || height > i32::max_value() as i64 {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"'height' is larger than i32",
));
}
Ok(height as i32)
})),
None => Nullable::Null,
};
Ok(WindowRectParameters { x, y, width, height })
}
}
impl ToJson for WindowRectParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("x".to_string(), self.x.to_json());
data.insert("y".to_string(), self.y.to_json());
data.insert("width".to_string(), self.width.to_json());
data.insert("height".to_string(), self.height.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct SwitchToWindowParameters {
pub handle: String
}
impl Parameters for SwitchToWindowParameters {
fn from_json(body: &Json) -> WebDriverResult<SwitchToWindowParameters> {
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
"Message body was not an object");
let handle = try_opt!(
try_opt!(data.get("handle"),
ErrorStatus::InvalidArgument,
"Missing 'handle' parameter").as_string(),
ErrorStatus::InvalidArgument,
"'handle' not a string");
return Ok(SwitchToWindowParameters {
handle: handle.to_string()
})
}
}
impl ToJson for SwitchToWindowParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("handle".to_string(), self.handle.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct LocatorParameters {
pub using: LocatorStrategy,
pub value: String
}
impl Parameters for LocatorParameters {
fn from_json(body: &Json) -> WebDriverResult<LocatorParameters> {
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
"Message body was not an object");
let using = try!(LocatorStrategy::from_json(
try_opt!(data.get("using"),
ErrorStatus::InvalidArgument,
"Missing 'using' parameter")));
let value = try_opt!(
try_opt!(data.get("value"),
ErrorStatus::InvalidArgument,
"Missing 'value' parameter").as_string(),
ErrorStatus::InvalidArgument,
"Could not convert using to string").to_string();
return Ok(LocatorParameters {
using: using,
value: value
})
}
}
impl ToJson for LocatorParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("using".to_string(), self.using.to_json());
data.insert("value".to_string(), self.value.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct SwitchToFrameParameters {
pub id: FrameId
}
impl Parameters for SwitchToFrameParameters {
fn from_json(body: &Json) -> WebDriverResult<SwitchToFrameParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::UnknownError,
"Message body was not an object");
let id = try!(FrameId::from_json(try_opt!(data.get("id"),
ErrorStatus::UnknownError,
"Missing 'id' parameter")));
Ok(SwitchToFrameParameters {
id: id
})
}
}
impl ToJson for SwitchToFrameParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("id".to_string(), self.id.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct SendKeysParameters {
pub text: String
}
impl Parameters for SendKeysParameters {
fn from_json(body: &Json) -> WebDriverResult<SendKeysParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::InvalidArgument,
"Message body was not an object");
let text = try_opt!(try_opt!(data.get("text"),
ErrorStatus::InvalidArgument,
"Missing 'text' parameter").as_string(),
ErrorStatus::InvalidArgument,
"Could not convert 'text' to string");
Ok(SendKeysParameters {
text: text.into()
})
}
}
impl ToJson for SendKeysParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("value".to_string(), self.text.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct JavascriptCommandParameters {
pub script: String,
pub args: Nullable<Vec<Json>>
}
impl Parameters for JavascriptCommandParameters {
fn from_json(body: &Json) -> WebDriverResult<JavascriptCommandParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::InvalidArgument,
"Message body was not an object");
let args_json = try_opt!(data.get("args"),
ErrorStatus::InvalidArgument,
"Missing args parameter");
let args = try!(Nullable::from_json(
args_json,
|x| {
Ok((try_opt!(x.as_array(),
ErrorStatus::InvalidArgument,
"Failed to convert args to Array")).clone())
}));
//TODO: Look for WebElements in args?
let script = try_opt!(
try_opt!(data.get("script"),
ErrorStatus::InvalidArgument,
"Missing script parameter").as_string(),
ErrorStatus::InvalidArgument,
"Failed to convert script to String");
Ok(JavascriptCommandParameters {
script: script.to_string(),
args: args.clone()
})
}
}
impl ToJson for JavascriptCommandParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
//TODO: Wrap script so that it becomes marionette-compatible
data.insert("script".to_string(), self.script.to_json());
data.insert("args".to_string(), self.args.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct GetNamedCookieParameters {
pub name: Nullable<String>,
}
impl Parameters for GetNamedCookieParameters {
fn from_json(body: &Json) -> WebDriverResult<GetNamedCookieParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::InvalidArgument,
"Message body was not an object");
let name_json = try_opt!(data.get("name"),
ErrorStatus::InvalidArgument,
"Missing 'name' parameter");
let name = try!(Nullable::from_json(name_json, |x| {
Ok(try_opt!(x.as_string(),
ErrorStatus::InvalidArgument,
"Failed to convert name to string")
.to_string())
}));
return Ok(GetNamedCookieParameters { name: name });
}
}
impl ToJson for GetNamedCookieParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("name".to_string(), self.name.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct AddCookieParameters {
pub name: String,
pub value: String,
pub path: Nullable<String>,
pub domain: Nullable<String>,
pub expiry: Nullable<Date>,
pub secure: bool,
pub httpOnly: bool
}
impl Parameters for AddCookieParameters {
fn from_json(body: &Json) -> WebDriverResult<AddCookieParameters> {
if !body.is_object() {
return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
"Message body was not an object"));
}
let data = try_opt!(body.find("cookie").and_then(|x| x.as_object()),
ErrorStatus::UnableToSetCookie,
"Cookie parameter not found or not an object");
let name = try_opt!(
try_opt!(data.get("name"),
ErrorStatus::InvalidArgument,
"Missing 'name' parameter").as_string(),
ErrorStatus::InvalidArgument,
"'name' is not a string").to_string();
let value = try_opt!(
try_opt!(data.get("value"),
ErrorStatus::InvalidArgument,
"Missing 'value' parameter").as_string(),
ErrorStatus::InvalidArgument,
"'value' is not a string").to_string();
let path = match data.get("path") {
Some(path_json) => {
try!(Nullable::from_json(
path_json,
|x| {
Ok(try_opt!(x.as_string(),
ErrorStatus::InvalidArgument,
"Failed to convert path to String").to_string())
}))
},
None => Nullable::Null
};
let domain = match data.get("domain") {
Some(domain_json) => {
try!(Nullable::from_json(
domain_json,
|x| {
Ok(try_opt!(x.as_string(),
ErrorStatus::InvalidArgument,
"Failed to convert domain to String").to_string())
}))
},
None => Nullable::Null
};
let expiry = match data.get("expiry") {
Some(expiry_json) => {
try!(Nullable::from_json(
expiry_json,
|x| {
Ok(Date::new(try_opt!(x.as_u64(),
ErrorStatus::InvalidArgument,
"Failed to convert expiry to Date")))
}))
},
None => Nullable::Null
};
let secure = match data.get("secure") {
Some(x) => try_opt!(x.as_boolean(),
ErrorStatus::InvalidArgument,
"Failed to convert secure to boolean"),
None => false
};
let http_only = match data.get("httpOnly") {
Some(x) => try_opt!(x.as_boolean(),
ErrorStatus::InvalidArgument,
"Failed to convert httpOnly to boolean"),
None => false
};
return Ok(AddCookieParameters {
name: name,
value: value,
path: path,
domain: domain,
expiry: expiry,
secure: secure,
httpOnly: http_only
})
}
}
impl ToJson for AddCookieParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("name".to_string(), self.name.to_json());
data.insert("value".to_string(), self.value.to_json());
data.insert("path".to_string(), self.path.to_json());
data.insert("domain".to_string(), self.domain.to_json());
data.insert("expiry".to_string(), self.expiry.to_json());
data.insert("secure".to_string(), self.secure.to_json());
data.insert("httpOnly".to_string(), self.httpOnly.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct TakeScreenshotParameters {
pub element: Nullable<WebElement>
}
impl Parameters for TakeScreenshotParameters {
fn from_json(body: &Json) -> WebDriverResult<TakeScreenshotParameters> {
let data = try_opt!(body.as_object(),
ErrorStatus::InvalidArgument,
"Message body was not an object");
let element = match data.get("element") {
Some(element_json) => try!(Nullable::from_json(
element_json,
|x| {
Ok(try!(WebElement::from_json(x)))
})),
None => Nullable::Null
};
return Ok(TakeScreenshotParameters {
element: element
})
}
}
impl ToJson for TakeScreenshotParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("element".to_string(), self.element.to_json());
Json::Object(data)
}
}
#[derive(Debug, PartialEq)]
pub struct ActionsParameters {
pub actions: Vec<ActionSequence>
}
impl Parameters for ActionsParameters {
fn from_json(body: &Json) -> WebDriverResult<ActionsParameters> {
try_opt!(body.as_object(),
ErrorStatus::InvalidArgument,
"Message body was not an object");
let actions = try_opt!(
try_opt!(body.find("actions"),
ErrorStatus::InvalidArgument,
"No actions parameter found").as_array(),
ErrorStatus::InvalidArgument,
"Parameter 'actions' was not an array");
let mut result = Vec::with_capacity(actions.len());
for chain in actions.iter() {
result.push(try!(ActionSequence::from_json(chain)));
}
Ok(ActionsParameters {
actions: result
})
}
}
impl ToJson for ActionsParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("actions".to_owned(),
self.actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>().to_json());
Json::Object(data)
}
}
#[cfg(test)]
mod tests {
use rustc_serialize::json::Json;
use super::{Nullable, Parameters, WindowRectParameters};
#[test]
fn test_window_rect() {
let expected = WindowRectParameters {
x: Nullable::Value(0i32),
y: Nullable::Value(1i32),
width: Nullable::Value(2i32),
height: Nullable::Value(3i32),
};
let actual = Json::from_str(r#"{"x": 0, "y": 1, "width": 2, "height": 3}"#).unwrap();
assert_eq!(expected, Parameters::from_json(&actual).unwrap());
}
#[test]
fn test_window_rect_nullable() {
let expected = WindowRectParameters {
x: Nullable::Value(0i32),
y: Nullable::Null,
width: Nullable::Value(2i32),
height: Nullable::Null,
};
let actual = Json::from_str(r#"{"x": 0, "y": null, "width": 2, "height": null}"#).unwrap();
assert_eq!(expected, Parameters::from_json(&actual).unwrap());
}
#[test]
fn test_window_rect_missing_fields() {
let expected = WindowRectParameters {
x: Nullable::Value(0i32),
y: Nullable::Null,
width: Nullable::Value(2i32),
height: Nullable::Null,
};
let actual = Json::from_str(r#"{"x": 0, "width": 2}"#).unwrap();
assert_eq!(expected, Parameters::from_json(&actual).unwrap());
}
#[test]
fn test_window_rect_floats() {
let expected = WindowRectParameters {
x: Nullable::Value(1i32),
y: Nullable::Value(2i32),
width: Nullable::Value(3i32),
height: Nullable::Value(4i32),
};
let actual = Json::from_str(r#"{"x": 1.1, "y": 2.2, "width": 3.3, "height": 4.4}"#).unwrap();
assert_eq!(expected, Parameters::from_json(&actual).unwrap());
}
}