mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
webdriver: Initial commit for seperate webdriver module.
Source-Repo: https://github.com/mozilla/webdriver-rust Source-Revision: da53f261e0ee97c68cfd567e3dac1aa4bcc1a151 --HG-- extra : subtree_source : http%3A//tristan.corp.lon2.mozilla.com%3A8000 extra : subtree_revision : d3a8d3cc76bca7cf4f714aba074a55be7a4c91d2
This commit is contained in:
parent
7afb65280a
commit
41c604968a
15
testing/webdriver/Cargo.toml
Normal file
15
testing/webdriver/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
|
||||||
|
name = "webdriver"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["James Graham <james@hoppipolla.co.uk>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.1.9"
|
||||||
|
rustc-serialize = "0.2.7"
|
||||||
|
|
||||||
|
[dependencies.hyper]
|
||||||
|
git = "https://github.com/hyperium/hyper.git"
|
||||||
|
|
||||||
|
[dependencies.uuid]
|
||||||
|
git = "https://github.com/rust-lang/uuid.git"
|
805
testing/webdriver/src/command.rs
Normal file
805
testing/webdriver/src/command.rs
Normal file
@ -0,0 +1,805 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use rustc_serialize::json::{ToJson, Json};
|
||||||
|
use regex::Captures;
|
||||||
|
|
||||||
|
use common::{WebDriverResult, WebDriverError, ErrorStatus, Nullable, WebElement, FrameId, LocatorStrategy};
|
||||||
|
use response::Date; //TODO: Put all these types in a specific file
|
||||||
|
use messagebuilder::MatchType;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum WebDriverCommand {
|
||||||
|
NewSession,
|
||||||
|
DeleteSession,
|
||||||
|
Get(GetParameters),
|
||||||
|
GetCurrentUrl,
|
||||||
|
GoBack,
|
||||||
|
GoForward,
|
||||||
|
Refresh,
|
||||||
|
GetTitle,
|
||||||
|
GetWindowHandle,
|
||||||
|
GetWindowHandles,
|
||||||
|
Close,
|
||||||
|
SetWindowSize(WindowSizeParameters),
|
||||||
|
GetWindowSize,
|
||||||
|
MaximizeWindow,
|
||||||
|
// FullscreenWindow // Not supported in marionette
|
||||||
|
SwitchToWindow(SwitchToWindowParameters),
|
||||||
|
SwitchToFrame(SwitchToFrameParameters),
|
||||||
|
SwitchToParentFrame,
|
||||||
|
FindElement(LocatorParameters),
|
||||||
|
FindElements(LocatorParameters),
|
||||||
|
IsDisplayed(WebElement),
|
||||||
|
IsSelected(WebElement),
|
||||||
|
GetElementAttribute(WebElement, String),
|
||||||
|
GetCSSValue(WebElement, String),
|
||||||
|
GetElementText(WebElement),
|
||||||
|
GetElementTagName(WebElement),
|
||||||
|
GetElementRect(WebElement),
|
||||||
|
IsEnabled(WebElement),
|
||||||
|
ExecuteScript(JavascriptCommandParameters),
|
||||||
|
ExecuteAsyncScript(JavascriptCommandParameters),
|
||||||
|
GetCookie(GetCookieParameters),
|
||||||
|
AddCookie(AddCookieParameters),
|
||||||
|
SetTimeouts(TimeoutsParameters),
|
||||||
|
//Actions(ActionsParameters),
|
||||||
|
ElementClick(WebElement),
|
||||||
|
ElementTap(WebElement),
|
||||||
|
ElementClear(WebElement),
|
||||||
|
ElementSendKeys(WebElement, SendKeysParameters),
|
||||||
|
DismissAlert,
|
||||||
|
AcceptAlert,
|
||||||
|
GetAlertText,
|
||||||
|
SendAlertText(SendAlertTextParameters),
|
||||||
|
TakeScreenshot(TakeScreenshotParameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct WebDriverMessage {
|
||||||
|
pub session_id: Option<String>,
|
||||||
|
pub command: WebDriverCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebDriverMessage {
|
||||||
|
pub fn new(session_id: Option<String>, command: WebDriverCommand) -> WebDriverMessage {
|
||||||
|
WebDriverMessage {
|
||||||
|
session_id: session_id,
|
||||||
|
command: command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_http(match_type: MatchType, params: &Captures, body: &str) -> WebDriverResult<WebDriverMessage> {
|
||||||
|
let session_id = WebDriverMessage::get_session_id(params);
|
||||||
|
let body_data = if body != "" {
|
||||||
|
debug!("Got request body {}", body);
|
||||||
|
match Json::from_str(body) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
||||||
|
format!("Failed to decode request body as json: {}", body).as_slice()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Json::Null
|
||||||
|
};
|
||||||
|
let command = match match_type {
|
||||||
|
MatchType::NewSession => WebDriverCommand::NewSession,
|
||||||
|
MatchType::DeleteSession => WebDriverCommand::DeleteSession,
|
||||||
|
MatchType::Get => {
|
||||||
|
let parameters: GetParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::Get(parameters)
|
||||||
|
},
|
||||||
|
MatchType::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
|
||||||
|
MatchType::GoBack => WebDriverCommand::GoBack,
|
||||||
|
MatchType::GoForward => WebDriverCommand::GoForward,
|
||||||
|
MatchType::Refresh => WebDriverCommand::Refresh,
|
||||||
|
MatchType::GetTitle => WebDriverCommand::GetTitle,
|
||||||
|
MatchType::GetWindowHandle => WebDriverCommand::GetWindowHandle,
|
||||||
|
MatchType::GetWindowHandles => WebDriverCommand::GetWindowHandles,
|
||||||
|
MatchType::Close => WebDriverCommand::Close,
|
||||||
|
MatchType::SetTimeouts => {
|
||||||
|
let parameters: TimeoutsParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SetTimeouts(parameters)
|
||||||
|
},
|
||||||
|
MatchType::SetWindowSize => {
|
||||||
|
let parameters: WindowSizeParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SetWindowSize(parameters)
|
||||||
|
},
|
||||||
|
MatchType::GetWindowSize => WebDriverCommand::GetWindowSize,
|
||||||
|
MatchType::MaximizeWindow => WebDriverCommand::MaximizeWindow,
|
||||||
|
MatchType::SwitchToWindow => {
|
||||||
|
let parameters: SwitchToWindowParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SwitchToWindow(parameters)
|
||||||
|
}
|
||||||
|
MatchType::SwitchToFrame => {
|
||||||
|
let parameters: SwitchToFrameParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SwitchToFrame(parameters)
|
||||||
|
},
|
||||||
|
MatchType::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
|
||||||
|
MatchType::FindElement => {
|
||||||
|
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::FindElement(parameters)
|
||||||
|
},
|
||||||
|
MatchType::FindElements => {
|
||||||
|
let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::FindElements(parameters)
|
||||||
|
},
|
||||||
|
MatchType::IsDisplayed => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::IsDisplayed(element)
|
||||||
|
},
|
||||||
|
MatchType::IsSelected => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::IsSelected(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementAttribute => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
let attr = try_opt!(params.name("name"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing name parameter").to_string();
|
||||||
|
WebDriverCommand::GetElementAttribute(element, attr)
|
||||||
|
},
|
||||||
|
MatchType::GetCSSValue => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
let property = try_opt!(params.name("propertyName"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing propertyName parameter").to_string();
|
||||||
|
WebDriverCommand::GetCSSValue(element, property)
|
||||||
|
},
|
||||||
|
MatchType::GetElementText => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::GetElementText(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementTagName => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::GetElementTagName(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementRect => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::GetElementRect(element)
|
||||||
|
},
|
||||||
|
MatchType::IsEnabled => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::IsEnabled(element)
|
||||||
|
},
|
||||||
|
MatchType::ElementClick => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::ElementClick(element)
|
||||||
|
},
|
||||||
|
MatchType::ElementTap => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::ElementTap(element)
|
||||||
|
},
|
||||||
|
MatchType::ElementClear => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
WebDriverCommand::ElementClear(element)
|
||||||
|
},
|
||||||
|
MatchType::ElementSendKeys => {
|
||||||
|
let element_id = try_opt!(params.name("elementId"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing elementId parameter");
|
||||||
|
let element = WebElement::new(element_id.to_string());
|
||||||
|
let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::ElementSendKeys(element, parameters)
|
||||||
|
},
|
||||||
|
MatchType::ExecuteScript => {
|
||||||
|
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::ExecuteScript(parameters)
|
||||||
|
},
|
||||||
|
MatchType::ExecuteAsyncScript => {
|
||||||
|
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::ExecuteAsyncScript(parameters)
|
||||||
|
},
|
||||||
|
MatchType::GetCookie => {
|
||||||
|
let parameters: GetCookieParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::GetCookie(parameters)
|
||||||
|
},
|
||||||
|
MatchType::AddCookie => {
|
||||||
|
let parameters: AddCookieParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::AddCookie(parameters)
|
||||||
|
},
|
||||||
|
MatchType::DismissAlert => {
|
||||||
|
WebDriverCommand::DismissAlert
|
||||||
|
},
|
||||||
|
MatchType::AcceptAlert => {
|
||||||
|
WebDriverCommand::AcceptAlert
|
||||||
|
},
|
||||||
|
MatchType::GetAlertText => {
|
||||||
|
WebDriverCommand::GetAlertText
|
||||||
|
},
|
||||||
|
MatchType::SendAlertText => {
|
||||||
|
let parameters: SendAlertTextParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SendAlertText(parameters)
|
||||||
|
}
|
||||||
|
MatchType::TakeScreenshot => {
|
||||||
|
let parameters: TakeScreenshotParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::TakeScreenshot(parameters)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(WebDriverMessage::new(session_id, command))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_session_id(params: &Captures) -> Option<String> {
|
||||||
|
params.name("sessionId").map(|x| x.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WebDriverMessage {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
let parameters = match self.command {
|
||||||
|
WebDriverCommand::NewSession |
|
||||||
|
WebDriverCommand::DeleteSession | WebDriverCommand::GetCurrentUrl |
|
||||||
|
WebDriverCommand::GoBack | WebDriverCommand::GoForward | WebDriverCommand::Refresh |
|
||||||
|
WebDriverCommand::GetTitle | WebDriverCommand::GetWindowHandle |
|
||||||
|
WebDriverCommand::GetWindowHandles | WebDriverCommand::Close |
|
||||||
|
WebDriverCommand::GetWindowSize | WebDriverCommand::MaximizeWindow |
|
||||||
|
WebDriverCommand::SwitchToParentFrame | WebDriverCommand::IsDisplayed(_) |
|
||||||
|
WebDriverCommand::IsSelected(_) | WebDriverCommand::GetElementAttribute(_, _) |
|
||||||
|
WebDriverCommand::GetCSSValue(_, _) | WebDriverCommand::GetElementText(_) |
|
||||||
|
WebDriverCommand::GetElementTagName(_) | WebDriverCommand::GetElementRect(_) |
|
||||||
|
WebDriverCommand::IsEnabled(_) | WebDriverCommand::AddCookie(_) |
|
||||||
|
WebDriverCommand::DismissAlert | WebDriverCommand::AcceptAlert |
|
||||||
|
WebDriverCommand::GetAlertText | WebDriverCommand::ElementClick(_) |
|
||||||
|
WebDriverCommand::ElementTap(_) | WebDriverCommand::ElementClear(_) => {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
WebDriverCommand::Get(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::SetTimeouts(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::SetWindowSize(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::SwitchToWindow(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::SwitchToFrame(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::FindElement(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::FindElements(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::ElementSendKeys(_, ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::ExecuteScript(ref x) |
|
||||||
|
WebDriverCommand::ExecuteAsyncScript(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::GetCookie(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::SendAlertText(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::TakeScreenshot(ref x) => Some(x.to_json())
|
||||||
|
};
|
||||||
|
if parameters.is_some() {
|
||||||
|
data.insert("parameters".to_string(), parameters.unwrap());
|
||||||
|
}
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Parameters {
|
||||||
|
fn from_json(body: &Json) -> WebDriverResult<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct GetParameters {
|
||||||
|
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");
|
||||||
|
return 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(PartialEq)]
|
||||||
|
pub struct TimeoutsParameters {
|
||||||
|
type_: String,
|
||||||
|
ms: 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 type_ = try_opt!(
|
||||||
|
try_opt!(data.get("type"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'type' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'type' not a string");
|
||||||
|
|
||||||
|
let ms = try_opt!(
|
||||||
|
try_opt!(data.get("ms"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'ms' parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'ms' not an integer");
|
||||||
|
return Ok(TimeoutsParameters {
|
||||||
|
type_: type_.to_string(),
|
||||||
|
ms: ms
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for TimeoutsParameters {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("type".to_string(), self.type_.to_json());
|
||||||
|
data.insert("ms".to_string(), self.ms.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct WindowSizeParameters {
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for WindowSizeParameters {
|
||||||
|
fn from_json(body: &Json) -> WebDriverResult<WindowSizeParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let height = try_opt!(
|
||||||
|
try_opt!(data.get("height"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'height' parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'height' is not a positive integer");
|
||||||
|
let width = try_opt!(
|
||||||
|
try_opt!(data.get("width"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing width parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'width' is not a positive integer");
|
||||||
|
return Ok(WindowSizeParameters {
|
||||||
|
height: height,
|
||||||
|
width: width
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WindowSizeParameters {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("width".to_string(), self.width.to_json());
|
||||||
|
data.insert("height".to_string(), self.height.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct SwitchToWindowParameters {
|
||||||
|
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(PartialEq)]
|
||||||
|
pub struct LocatorParameters {
|
||||||
|
using: LocatorStrategy,
|
||||||
|
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 'using' 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(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(PartialEq)]
|
||||||
|
pub struct SendKeysParameters {
|
||||||
|
pub value: 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 value = try_opt!(try_opt!(data.get("value"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'value' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'value' not a string").to_string();
|
||||||
|
|
||||||
|
Ok(SendKeysParameters {
|
||||||
|
value: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for SendKeysParameters {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("value".to_string(), self.value.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct JavascriptCommandParameters {
|
||||||
|
script: String,
|
||||||
|
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());
|
||||||
|
data.insert("newSandbox".to_string(), false.to_json());
|
||||||
|
data.insert("specialPowers".to_string(), false.to_json());
|
||||||
|
data.insert("scriptTimeout".to_string(), Json::Null);
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct GetCookieParameters {
|
||||||
|
name: Nullable<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for GetCookieParameters {
|
||||||
|
fn from_json(body: &Json) -> WebDriverResult<GetCookieParameters> {
|
||||||
|
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(GetCookieParameters {
|
||||||
|
name: name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for GetCookieParameters {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("name".to_string(), self.name.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct AddCookieParameters {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
pub path: Nullable<String>,
|
||||||
|
pub domain: Nullable<String>,
|
||||||
|
pub expiry: Nullable<Date>,
|
||||||
|
pub maxAge: Nullable<Date>,
|
||||||
|
pub secure: bool,
|
||||||
|
pub httpOnly: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for AddCookieParameters {
|
||||||
|
fn from_json(body: &Json) -> WebDriverResult<AddCookieParameters> {
|
||||||
|
let data = try_opt!(body.as_object(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Message body was 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
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: This is supposed to support some text format
|
||||||
|
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 max_age = match data.get("maxAge") {
|
||||||
|
Some(max_age_json) => {
|
||||||
|
try!(Nullable::from_json(
|
||||||
|
max_age_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,
|
||||||
|
maxAge: max_age,
|
||||||
|
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("maxAge".to_string(), self.maxAge.to_json());
|
||||||
|
data.insert("secure".to_string(), self.secure.to_json());
|
||||||
|
data.insert("httpOnly".to_string(), self.httpOnly.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct SendAlertTextParameters {
|
||||||
|
keysToSend: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for SendAlertTextParameters {
|
||||||
|
fn from_json(body: &Json) -> WebDriverResult<SendAlertTextParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::InvalidArgument,
|
||||||
|
"Message body was not an object");
|
||||||
|
let keys = try_opt!(
|
||||||
|
try_opt!(data.get("keysToSend"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'handle' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'keysToSend' not a string").to_string();
|
||||||
|
return Ok(SendAlertTextParameters {
|
||||||
|
keysToSend: keys
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for SendAlertTextParameters {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("keysToSend".to_string(), self.keysToSend.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(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)
|
||||||
|
}
|
||||||
|
}
|
284
testing/webdriver/src/common.rs
Normal file
284
testing/webdriver/src/common.rs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
use core::num::ToPrimitive;
|
||||||
|
use rustc_serialize::json::{Json, ToJson, ParserError};
|
||||||
|
use rustc_serialize::{Encodable, Encoder};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::error::{Error, FromError};
|
||||||
|
|
||||||
|
static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf";
|
||||||
|
|
||||||
|
#[derive(PartialEq, Show)]
|
||||||
|
pub enum ErrorStatus {
|
||||||
|
ElementNotSelectable,
|
||||||
|
ElementNotVisible,
|
||||||
|
InvalidArgument,
|
||||||
|
InvalidCookieDomain,
|
||||||
|
InvalidElementCoordinates,
|
||||||
|
InvalidElementState,
|
||||||
|
InvalidSelector,
|
||||||
|
InvalidSessionId,
|
||||||
|
JavascriptError,
|
||||||
|
MoveTargetOutOfBounds,
|
||||||
|
NoSuchAlert,
|
||||||
|
NoSuchElement,
|
||||||
|
NoSuchFrame,
|
||||||
|
NoSuchWindow,
|
||||||
|
ScriptTimeout,
|
||||||
|
SessionNotCreated,
|
||||||
|
StaleElementReference,
|
||||||
|
Timeout,
|
||||||
|
UnableToSetCookie,
|
||||||
|
UnexpectedAlertOpen,
|
||||||
|
UnknownError,
|
||||||
|
UnknownPath,
|
||||||
|
UnknownMethod,
|
||||||
|
UnsupportedOperation,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WebDriverResult<T> = Result<T, WebDriverError>;
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
pub struct WebDriverError {
|
||||||
|
pub status: ErrorStatus,
|
||||||
|
pub message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebDriverError {
|
||||||
|
pub fn new(status: ErrorStatus, message: &str) -> WebDriverError {
|
||||||
|
WebDriverError {
|
||||||
|
status: status,
|
||||||
|
message: message.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status_code(&self) -> &'static str {
|
||||||
|
match self.status {
|
||||||
|
ErrorStatus::ElementNotSelectable => "element not selectable",
|
||||||
|
ErrorStatus::ElementNotVisible => "element not visible",
|
||||||
|
ErrorStatus::InvalidArgument => "invalid argument",
|
||||||
|
ErrorStatus::InvalidCookieDomain => "invalid cookie domain",
|
||||||
|
ErrorStatus::InvalidElementCoordinates => "invalid element coordinates",
|
||||||
|
ErrorStatus::InvalidElementState => "invalid element state",
|
||||||
|
ErrorStatus::InvalidSelector => "invalid selector",
|
||||||
|
ErrorStatus::InvalidSessionId => "invalid session id",
|
||||||
|
ErrorStatus::JavascriptError => "javascript error",
|
||||||
|
ErrorStatus::MoveTargetOutOfBounds => "move target out of bounds",
|
||||||
|
ErrorStatus::NoSuchAlert => "no such alert",
|
||||||
|
ErrorStatus::NoSuchElement => "no such element",
|
||||||
|
ErrorStatus::NoSuchFrame => "no such frame",
|
||||||
|
ErrorStatus::NoSuchWindow => "no such window",
|
||||||
|
ErrorStatus::ScriptTimeout => "script timeout",
|
||||||
|
ErrorStatus::SessionNotCreated => "session not created",
|
||||||
|
ErrorStatus::StaleElementReference => "stale element reference",
|
||||||
|
ErrorStatus::Timeout => "timeout",
|
||||||
|
ErrorStatus::UnableToSetCookie => "unable to set cookie",
|
||||||
|
ErrorStatus::UnexpectedAlertOpen => "unexpected alert open",
|
||||||
|
ErrorStatus::UnknownError => "unknown error",
|
||||||
|
ErrorStatus::UnknownPath => "unknown command",
|
||||||
|
ErrorStatus::UnknownMethod => "unknown command",
|
||||||
|
ErrorStatus::UnsupportedOperation => "unsupported operation",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn http_status(&self) -> u32 {
|
||||||
|
match self.status {
|
||||||
|
ErrorStatus::UnknownPath => 404u32,
|
||||||
|
ErrorStatus::UnknownMethod => 405u32,
|
||||||
|
_ => 500u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_json_string(&self) -> String {
|
||||||
|
self.to_json().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WebDriverError {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert("status".to_string(), self.status_code().to_json());
|
||||||
|
data.insert("error".to_string(), self.message.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for WebDriverError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
self.status_code()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detail(&self) -> Option<String> {
|
||||||
|
Some(self.message.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromError<ParserError> for WebDriverError {
|
||||||
|
fn from_error(err: ParserError) -> WebDriverError {
|
||||||
|
let msg = format!("{:?}", err);
|
||||||
|
WebDriverError::new(ErrorStatus::UnknownError, msg.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Show)]
|
||||||
|
pub enum Nullable<T: ToJson> {
|
||||||
|
Value(T),
|
||||||
|
Null
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToJson> Nullable<T> {
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Nullable::Value(_) => false,
|
||||||
|
Nullable::Null => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_value(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Nullable::Value(_) => true,
|
||||||
|
Nullable::Null => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToJson> Nullable<T> {
|
||||||
|
//This is not very pretty
|
||||||
|
pub fn from_json<F: FnOnce(&Json) -> WebDriverResult<T>>(value: &Json, f: F) -> WebDriverResult<Nullable<T>> {
|
||||||
|
if value.is_null() {
|
||||||
|
Ok(Nullable::Null)
|
||||||
|
} else {
|
||||||
|
Ok(Nullable::Value(try!(f(value))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToJson> ToJson for Nullable<T> {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
match *self {
|
||||||
|
Nullable::Value(ref x) => x.to_json(),
|
||||||
|
Nullable::Null => Json::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToJson> Encodable for Nullable<T> {
|
||||||
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
|
match *self {
|
||||||
|
Nullable::Value(ref x) => x.to_json().encode(s),
|
||||||
|
Nullable::Null => s.emit_option_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct WebElement {
|
||||||
|
pub id: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebElement {
|
||||||
|
pub fn new(id: String) -> WebElement {
|
||||||
|
WebElement {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_json(data: &Json) -> WebDriverResult<WebElement> {
|
||||||
|
let object = try_opt!(data.as_object(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Could not convert webelement to object");
|
||||||
|
let id_value = try_opt!(object.get(ELEMENT_KEY),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Could not find webelement key");
|
||||||
|
|
||||||
|
let id = try_opt!(id_value.as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Could not convert web element to string").to_string();
|
||||||
|
|
||||||
|
Ok(WebElement::new(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WebElement {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert(ELEMENT_KEY.to_string(), self.id.to_json());
|
||||||
|
Json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum FrameId {
|
||||||
|
Short(u16),
|
||||||
|
Element(WebElement),
|
||||||
|
Null
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameId {
|
||||||
|
pub fn from_json(data: &Json) -> WebDriverResult<FrameId> {
|
||||||
|
match data {
|
||||||
|
&Json::U64(x) => {
|
||||||
|
let id = try_opt!(x.to_u16(),
|
||||||
|
ErrorStatus::NoSuchFrame,
|
||||||
|
"frame id out of range");
|
||||||
|
Ok(FrameId::Short(id))
|
||||||
|
},
|
||||||
|
&Json::Null => Ok(FrameId::Null),
|
||||||
|
&Json::String(ref x) => Ok(FrameId::Element(WebElement::new(x.clone()))),
|
||||||
|
_ => Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
||||||
|
"frame id has unexpected type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for FrameId {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
match *self {
|
||||||
|
FrameId::Short(x) => {
|
||||||
|
Json::U64(x as u64)
|
||||||
|
},
|
||||||
|
FrameId::Element(ref x) => {
|
||||||
|
Json::String(x.id.clone())
|
||||||
|
},
|
||||||
|
FrameId::Null => {
|
||||||
|
Json::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum LocatorStrategy {
|
||||||
|
CSSSelector,
|
||||||
|
LinkText,
|
||||||
|
PartialLinkText,
|
||||||
|
XPath
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocatorStrategy {
|
||||||
|
pub fn from_json(body: &Json) -> WebDriverResult<LocatorStrategy> {
|
||||||
|
match try_opt!(body.as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Cound not convert strategy to string") {
|
||||||
|
"css selector" => Ok(LocatorStrategy::CSSSelector),
|
||||||
|
"link text" => Ok(LocatorStrategy::LinkText),
|
||||||
|
"partial link text" => Ok(LocatorStrategy::PartialLinkText),
|
||||||
|
"xpath" => Ok(LocatorStrategy::XPath),
|
||||||
|
_ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
||||||
|
"Unknown locator strategy"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for LocatorStrategy {
|
||||||
|
fn to_json(&self) -> Json {
|
||||||
|
Json::String(match *self {
|
||||||
|
LocatorStrategy::CSSSelector => "css selector",
|
||||||
|
LocatorStrategy::LinkText => "link text",
|
||||||
|
LocatorStrategy::PartialLinkText => "partial link text",
|
||||||
|
LocatorStrategy::XPath => "xpath"
|
||||||
|
}.to_string())
|
||||||
|
}
|
||||||
|
}
|
238
testing/webdriver/src/httpserver.rs
Normal file
238
testing/webdriver/src/httpserver.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
use std::io::net::ip::IpAddr;
|
||||||
|
use std::num::FromPrimitive;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::thread::Thread;
|
||||||
|
|
||||||
|
use hyper::header::common::ContentLength;
|
||||||
|
use hyper::method::Method;
|
||||||
|
use hyper::server::{Server, Handler, Request, Response};
|
||||||
|
use hyper::uri::RequestUri::AbsolutePath;
|
||||||
|
|
||||||
|
use command::{WebDriverMessage, WebDriverCommand};
|
||||||
|
use common::{WebDriverResult, ErrorStatus, WebDriverError};
|
||||||
|
use messagebuilder::{get_builder, MessageBuilder};
|
||||||
|
use response::WebDriverResponse;
|
||||||
|
|
||||||
|
enum DispatchMessage {
|
||||||
|
HandleWebDriver(WebDriverMessage, Sender<WebDriverResult<WebDriverResponse>>),
|
||||||
|
Quit
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub struct Session {
|
||||||
|
id: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
fn new(id: String) -> Session {
|
||||||
|
Session {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WebDriverHandler : Send {
|
||||||
|
fn handle_command(&mut self, session: &Option<Session>, msg: &WebDriverMessage) -> WebDriverResult<WebDriverResponse>;
|
||||||
|
fn delete_session(&mut self, session: &Option<Session>);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dispatcher<T: WebDriverHandler> {
|
||||||
|
handler: T,
|
||||||
|
session: Option<Session>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WebDriverHandler> Dispatcher<T> {
|
||||||
|
fn new(handler: T) -> Dispatcher<T> {
|
||||||
|
Dispatcher {
|
||||||
|
handler: handler,
|
||||||
|
session: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, msg_chan: Receiver<DispatchMessage>) {
|
||||||
|
loop {
|
||||||
|
match msg_chan.recv() {
|
||||||
|
Ok(DispatchMessage::HandleWebDriver(msg, resp_chan)) => {
|
||||||
|
let resp = match self.check_session(&msg) {
|
||||||
|
Ok(_) => self.handler.handle_command(&self.session, &msg),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
};
|
||||||
|
|
||||||
|
match resp {
|
||||||
|
Ok(WebDriverResponse::NewSession(ref new_session)) => {
|
||||||
|
self.session = Some(Session::new(new_session.sessionId.clone()));
|
||||||
|
},
|
||||||
|
Ok(WebDriverResponse::DeleteSession) => {
|
||||||
|
debug!("Deleting session");
|
||||||
|
self.handler.delete_session(&self.session);
|
||||||
|
self.session = None;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp_chan.send(resp).is_err() {
|
||||||
|
error!("Sending response to the main thread failed");
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Ok(DispatchMessage::Quit) => {
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(_) => panic!("Error receiving message in handler")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_session(&self, msg: &WebDriverMessage) -> WebDriverResult<()> {
|
||||||
|
match msg.session_id {
|
||||||
|
Some(ref msg_session_id) => {
|
||||||
|
match self.session {
|
||||||
|
Some(ref existing_session) => {
|
||||||
|
if existing_session.id != *msg_session_id {
|
||||||
|
Err(WebDriverError::new(
|
||||||
|
ErrorStatus::InvalidSessionId,
|
||||||
|
format!("Got unexpected session id {} expected {}",
|
||||||
|
msg_session_id,
|
||||||
|
existing_session.id).as_slice()))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
match self.session {
|
||||||
|
Some(_) => {
|
||||||
|
match msg.command {
|
||||||
|
WebDriverCommand::NewSession => {
|
||||||
|
Err(WebDriverError::new(
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Session is already started"))
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
//This should be impossible
|
||||||
|
error!("Got a message with no session id");
|
||||||
|
Err(WebDriverError::new(
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Got a command with no session?!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
match msg.command {
|
||||||
|
WebDriverCommand::NewSession => Ok(()),
|
||||||
|
|
||||||
|
_ => Err(WebDriverError::new(
|
||||||
|
ErrorStatus::SessionNotCreated,
|
||||||
|
"Tried to run a command before creating a session"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HttpHandler {
|
||||||
|
chan: Mutex<Sender<DispatchMessage>>,
|
||||||
|
builder: Mutex<MessageBuilder>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpHandler {
|
||||||
|
fn new(builder: MessageBuilder, chan: Sender<DispatchMessage>) -> HttpHandler {
|
||||||
|
HttpHandler {
|
||||||
|
chan: Mutex::new(chan),
|
||||||
|
builder: Mutex::new(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for HttpHandler {
|
||||||
|
fn handle(&self, req: Request, res: Response) {
|
||||||
|
let mut req = req;
|
||||||
|
let mut res = res;
|
||||||
|
|
||||||
|
let body = match req.method {
|
||||||
|
Method::Post => req.read_to_string().unwrap(),
|
||||||
|
_ => "".to_string()
|
||||||
|
};
|
||||||
|
debug!("Got request {} {:?}", req.method, req.uri);
|
||||||
|
match req.uri {
|
||||||
|
AbsolutePath(path) => {
|
||||||
|
let msg_result = {
|
||||||
|
// The fact that this locks for basically the whole request doesn't
|
||||||
|
// matter as long as we are only handling one request at a time.
|
||||||
|
match self.builder.lock() {
|
||||||
|
Ok(ref builder) => {
|
||||||
|
builder.from_http(req.method, path.as_slice(), body.as_slice())
|
||||||
|
},
|
||||||
|
Err(_) => return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (status, resp_body) = match msg_result {
|
||||||
|
Ok(message) => {
|
||||||
|
let (send_res, recv_res) = channel();
|
||||||
|
match self.chan.lock() {
|
||||||
|
Ok(ref c) => {
|
||||||
|
let res = c.send(DispatchMessage::HandleWebDriver(message,
|
||||||
|
send_res));
|
||||||
|
match res {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
error!("Something terrible happened");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
error!("Something terrible happened");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match recv_res.recv() {
|
||||||
|
Ok(data) => match data {
|
||||||
|
Ok(response) => (200, response.to_json_string()),
|
||||||
|
Err(err) => (err.http_status(), err.to_json_string()),
|
||||||
|
},
|
||||||
|
Err(_) => panic!("Error reading response")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
(err.http_status(), err.to_json_string())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if status != 200 {
|
||||||
|
error!("Returning status code {}", status);
|
||||||
|
error!("Returning body {}", resp_body);
|
||||||
|
} else {
|
||||||
|
debug!("Returning status code {}", status);
|
||||||
|
debug!("Returning body {}", resp_body);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let status_code = res.status_mut();
|
||||||
|
*status_code = FromPrimitive::from_u32(status).unwrap();
|
||||||
|
}
|
||||||
|
res.headers_mut().set(ContentLength(resp_body.len() as u64));
|
||||||
|
let mut stream = res.start();
|
||||||
|
stream.write_str(resp_body.as_slice()).unwrap();
|
||||||
|
stream.unwrap().end().unwrap();
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start<T: WebDriverHandler>(ip_address: IpAddr, port: u16, handler: T) {
|
||||||
|
let server = Server::http(ip_address, port);
|
||||||
|
|
||||||
|
let (msg_send, msg_recv) = channel();
|
||||||
|
|
||||||
|
Thread::spawn(move || {
|
||||||
|
let mut dispatcher = Dispatcher::new(handler);
|
||||||
|
dispatcher.run(msg_recv)
|
||||||
|
});
|
||||||
|
let builder = get_builder();
|
||||||
|
let http_handler = HttpHandler::new(builder, msg_send.clone());
|
||||||
|
server.listen(http_handler).unwrap();
|
||||||
|
}
|
29
testing/webdriver/src/lib.rs
Normal file
29
testing/webdriver/src/lib.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//Until it's clear what the unstable things are replaced by
|
||||||
|
#![allow(unstable)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate "rustc-serialize" as rustc_serialize;
|
||||||
|
extern crate core;
|
||||||
|
extern crate hyper;
|
||||||
|
extern crate regex;
|
||||||
|
|
||||||
|
macro_rules! try_opt {
|
||||||
|
($expr:expr, $err_type:expr, $err_msg:expr) => ({
|
||||||
|
match $expr {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Err(WebDriverError::new($err_type, $err_msg))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod command;
|
||||||
|
pub mod common;
|
||||||
|
pub mod httpserver;
|
||||||
|
pub mod response;
|
||||||
|
mod messagebuilder;
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
}
|
3
testing/webdriver/src/marionette_bits.rs
Normal file
3
testing/webdriver/src/marionette_bits.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
|
186
testing/webdriver/src/messagebuilder.rs
Normal file
186
testing/webdriver/src/messagebuilder.rs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
use regex::{Regex, Captures};
|
||||||
|
|
||||||
|
use hyper::method::Method;
|
||||||
|
use hyper::method::Method::{Get, Post, Delete};
|
||||||
|
|
||||||
|
use command::{WebDriverMessage};
|
||||||
|
use common::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MatchType {
|
||||||
|
NewSession,
|
||||||
|
DeleteSession,
|
||||||
|
Get,
|
||||||
|
GetCurrentUrl,
|
||||||
|
GoBack,
|
||||||
|
GoForward,
|
||||||
|
Refresh,
|
||||||
|
GetTitle,
|
||||||
|
GetWindowHandle,
|
||||||
|
GetWindowHandles,
|
||||||
|
Close,
|
||||||
|
SetWindowSize,
|
||||||
|
GetWindowSize,
|
||||||
|
MaximizeWindow,
|
||||||
|
SwitchToWindow,
|
||||||
|
SwitchToFrame,
|
||||||
|
SwitchToParentFrame,
|
||||||
|
FindElement,
|
||||||
|
FindElements,
|
||||||
|
IsDisplayed,
|
||||||
|
IsSelected,
|
||||||
|
GetElementAttribute,
|
||||||
|
GetCSSValue,
|
||||||
|
GetElementText,
|
||||||
|
GetElementTagName,
|
||||||
|
GetElementRect,
|
||||||
|
IsEnabled,
|
||||||
|
ExecuteScript,
|
||||||
|
ExecuteAsyncScript,
|
||||||
|
GetCookie,
|
||||||
|
AddCookie,
|
||||||
|
SetTimeouts,
|
||||||
|
//Actions XXX - once I understand the spec, perhaps
|
||||||
|
ElementClick,
|
||||||
|
ElementTap,
|
||||||
|
ElementClear,
|
||||||
|
ElementSendKeys,
|
||||||
|
DismissAlert,
|
||||||
|
AcceptAlert,
|
||||||
|
GetAlertText,
|
||||||
|
SendAlertText,
|
||||||
|
TakeScreenshot
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RequestMatcher {
|
||||||
|
method: Method,
|
||||||
|
path_regexp: Regex,
|
||||||
|
match_type: MatchType
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestMatcher {
|
||||||
|
pub fn new(method: Method, path: &str, match_type: MatchType) -> RequestMatcher {
|
||||||
|
let path_regexp = RequestMatcher::compile_path(path);
|
||||||
|
RequestMatcher {
|
||||||
|
method: method,
|
||||||
|
path_regexp: path_regexp,
|
||||||
|
match_type: match_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_match<'t>(&'t self, method: Method, path: &'t str) -> (bool, Option<Captures>) {
|
||||||
|
let captures = self.path_regexp.captures(path);
|
||||||
|
(method == self.method, captures)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_path(path: &str) -> Regex {
|
||||||
|
let mut rv = String::new();
|
||||||
|
rv.push_str("^");
|
||||||
|
let mut components = path.split('/');
|
||||||
|
for component in components {
|
||||||
|
if component.starts_with("{") {
|
||||||
|
if !component.ends_with("}") {
|
||||||
|
panic!("Invalid url pattern")
|
||||||
|
}
|
||||||
|
rv.push_str(format!("(?P<{}>[^/]+)/", &component[1..component.len()-1]).as_slice());
|
||||||
|
} else {
|
||||||
|
rv.push_str(format!("{}/", component).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Remove the trailing /
|
||||||
|
rv.pop();
|
||||||
|
rv.push_str("$");
|
||||||
|
//This will fail at runtime if the regexp is invalid
|
||||||
|
Regex::new(rv.as_slice()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MessageBuilder {
|
||||||
|
http_matchers: Vec<(Method, RequestMatcher)>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBuilder {
|
||||||
|
pub fn new() -> MessageBuilder {
|
||||||
|
MessageBuilder {
|
||||||
|
http_matchers: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_http(&self, method: Method, path: &str, body: &str) -> WebDriverResult<WebDriverMessage> {
|
||||||
|
let mut error = ErrorStatus::UnknownPath;
|
||||||
|
for &(ref match_method, ref matcher) in self.http_matchers.iter() {
|
||||||
|
if method == *match_method {
|
||||||
|
let (method_match, captures) = matcher.get_match(method.clone(), path);
|
||||||
|
if captures.is_some() {
|
||||||
|
if method_match {
|
||||||
|
return WebDriverMessage::from_http(matcher.match_type,
|
||||||
|
&captures.unwrap(),
|
||||||
|
body)
|
||||||
|
} else {
|
||||||
|
error = ErrorStatus::UnknownMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(WebDriverError::new(error,
|
||||||
|
format!("{} {} did not match a known command", method, path).as_slice()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, method: Method, path: &str, match_type: MatchType) {
|
||||||
|
let http_matcher = RequestMatcher::new(method.clone(), path, match_type);
|
||||||
|
self.http_matchers.push((method, http_matcher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_builder() -> MessageBuilder {
|
||||||
|
let mut builder = MessageBuilder::new();
|
||||||
|
let matchers = vec![(Post, "/session", MatchType::NewSession),
|
||||||
|
(Delete, "/session/{sessionId}", MatchType::DeleteSession),
|
||||||
|
(Post, "/session/{sessionId}/url", MatchType::Get),
|
||||||
|
(Get, "/session/{sessionId}/url", MatchType::GetCurrentUrl),
|
||||||
|
(Post, "/session/{sessionId}/back", MatchType::GoBack),
|
||||||
|
(Post, "/session/{sessionId}/forward", MatchType::GoForward),
|
||||||
|
(Post, "/session/{sessionId}/refresh", MatchType::Refresh),
|
||||||
|
(Get, "/session/{sessionId}/title", MatchType::GetTitle),
|
||||||
|
(Get, "/session/{sessionId}/window_handle", MatchType::GetWindowHandle),
|
||||||
|
(Get, "/session/{sessionId}/window_handles", MatchType::GetWindowHandles),
|
||||||
|
(Delete, "/session/{sessionId}/window_handle", MatchType::Close),
|
||||||
|
(Post, "/session/{sessionId}/window/size", MatchType::SetWindowSize),
|
||||||
|
(Get, "/session/{sessionId}/window/size", MatchType::GetWindowSize),
|
||||||
|
(Post, "/session/{sessionId}/window/maximize", MatchType::MaximizeWindow),
|
||||||
|
(Post, "/session/{sessionId}/window", MatchType::SwitchToWindow),
|
||||||
|
(Post, "/session/{sessionId}/frame", MatchType::SwitchToFrame),
|
||||||
|
(Post, "/session/{sessionId}/frame/parent", MatchType::SwitchToParentFrame),
|
||||||
|
(Post, "/session/{sessionId}/element", MatchType::FindElement),
|
||||||
|
(Post, "/session/{sessionId}/elements", MatchType::FindElements),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/displayed", MatchType::IsDisplayed),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/selected", MatchType::IsSelected),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/attribute/{name}", MatchType::GetElementAttribute),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/css/{propertyName}", MatchType::GetCSSValue),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/text", MatchType::GetElementText),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/name", MatchType::GetElementTagName),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/rect", MatchType::GetElementRect),
|
||||||
|
(Get, "/session/{sessionId}/element/{elementId}/enabled", MatchType::IsEnabled),
|
||||||
|
(Post, "/session/{sessionId}/execute", MatchType::ExecuteScript),
|
||||||
|
(Post, "/session/{sessionId}/execute_async", MatchType::ExecuteAsyncScript),
|
||||||
|
(Get, "/session/{sessionId}/cookie", MatchType::GetCookie),
|
||||||
|
(Post, "/session/{sessionId}/cookie", MatchType::AddCookie),
|
||||||
|
(Post, "/session/{sessionId}/timeouts", MatchType::SetTimeouts),
|
||||||
|
//(Post, "/session/{sessionId}/actions", MatchType::Actions),
|
||||||
|
(Post, "/session/{sessionId}/element/{elementId}/click", MatchType::ElementClick),
|
||||||
|
(Post, "/session/{sessionId}/element/{elementId}/tap", MatchType::ElementTap),
|
||||||
|
(Post, "/session/{sessionId}/element/{elementId}/clear", MatchType::ElementClear),
|
||||||
|
(Post, "/session/{sessionId}/element/{elementId}/sendKeys", MatchType::ElementSendKeys),
|
||||||
|
(Post, "/session/{sessionId}/dismiss_alert", MatchType::DismissAlert),
|
||||||
|
(Post, "/session/{sessionId}/accept_alert", MatchType::AcceptAlert),
|
||||||
|
(Get, "/session/{sessionId}/alert_text", MatchType::GetAlertText),
|
||||||
|
(Post, "/session/{sessionId}/alert_text", MatchType::SendAlertText),
|
||||||
|
(Get, "/session/{sessionId}/screenshot", MatchType::TakeScreenshot)
|
||||||
|
];
|
||||||
|
debug!("Creating routes");
|
||||||
|
for &(ref method, ref url, ref match_type) in matchers.iter() {
|
||||||
|
builder.add(method.clone(), *url, *match_type);
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
}
|
149
testing/webdriver/src/response.rs
Normal file
149
testing/webdriver/src/response.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
use rustc_serialize::json;
|
||||||
|
use rustc_serialize::json::ToJson;
|
||||||
|
|
||||||
|
use common::Nullable;
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
pub enum WebDriverResponse {
|
||||||
|
NewSession(NewSessionResponse),
|
||||||
|
DeleteSession,
|
||||||
|
WindowSize(WindowSizeResponse),
|
||||||
|
ElementRect(ElementRectResponse),
|
||||||
|
Cookie(CookieResponse),
|
||||||
|
Generic(ValueResponse),
|
||||||
|
Void
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebDriverResponse {
|
||||||
|
pub fn to_json_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
WebDriverResponse::NewSession(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::DeleteSession => "".to_string(),
|
||||||
|
WebDriverResponse::WindowSize(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::ElementRect(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::Cookie(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::Generic(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::Void => "".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, Show)]
|
||||||
|
pub struct NewSessionResponse {
|
||||||
|
pub sessionId: String,
|
||||||
|
pub value: json::Json
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewSessionResponse {
|
||||||
|
pub fn new(session_id: String, value: json::Json) -> NewSessionResponse {
|
||||||
|
NewSessionResponse {
|
||||||
|
value: value,
|
||||||
|
sessionId: session_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, Show)]
|
||||||
|
pub struct ValueResponse {
|
||||||
|
value: json::Json
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueResponse {
|
||||||
|
pub fn new(value: json::Json) -> ValueResponse {
|
||||||
|
ValueResponse {
|
||||||
|
value: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, Show)]
|
||||||
|
pub struct WindowSizeResponse {
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowSizeResponse {
|
||||||
|
pub fn new(width: u64, height: u64) -> WindowSizeResponse {
|
||||||
|
WindowSizeResponse {
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, Show)]
|
||||||
|
pub struct ElementRectResponse {
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementRectResponse {
|
||||||
|
pub fn new(x: u64, y: u64, width: u64, height: u64) -> ElementRectResponse {
|
||||||
|
ElementRectResponse {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, PartialEq, Show)]
|
||||||
|
pub struct Date(u64);
|
||||||
|
|
||||||
|
impl Date {
|
||||||
|
pub fn new(timestamp: u64) -> Date {
|
||||||
|
Date(timestamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for Date {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let &Date(x) = self;
|
||||||
|
x.to_json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: some of these fields are probably supposed to be optional
|
||||||
|
#[derive(RustcEncodable, PartialEq, Show)]
|
||||||
|
pub struct Cookie {
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
path: Nullable<String>,
|
||||||
|
domain: Nullable<String>,
|
||||||
|
expiry: Nullable<Date>,
|
||||||
|
maxAge: Date,
|
||||||
|
secure: bool,
|
||||||
|
httpOnly: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cookie {
|
||||||
|
pub fn new(name: String, value: String, path: Nullable<String>, domain: Nullable<String>,
|
||||||
|
expiry: Nullable<Date>, max_age: Date, secure: bool, http_only: bool) -> Cookie {
|
||||||
|
Cookie {
|
||||||
|
name: name,
|
||||||
|
value: value,
|
||||||
|
path: path,
|
||||||
|
domain: domain,
|
||||||
|
expiry: expiry,
|
||||||
|
maxAge: max_age,
|
||||||
|
secure: secure,
|
||||||
|
httpOnly: http_only
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, Show)]
|
||||||
|
pub struct CookieResponse {
|
||||||
|
value: Vec<Cookie>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CookieResponse {
|
||||||
|
pub fn new(value: Vec<Cookie>) -> CookieResponse {
|
||||||
|
CookieResponse {
|
||||||
|
value: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user