use crate::{
keyboard::Keyboard,
rect::Rect,
mouse::Mouse,
result::{PebblesError, PebblesErrorDetails},
service::Service,
webelement::WebElement,
navigator::Navigator,
raise,
error,
};
use serde_json::{json, Value};
use std::rc::Rc;
pub struct Pebbles {
pub service: Rc<Service>,
pub mouse: Mouse,
pub keyboard: Keyboard,
pub navigator: Navigator,
}
impl Pebbles {
pub fn new(geckodriver_cmd: &str, capabilities: Option<&Value>) -> Result<Self, PebblesError> {
let pebbles_capabilities = match capabilities {
Some(caps) => caps.clone(),
None => json!({
"capabilities": {
"firstMatch": [{}],
"alwaysMatch": {
"browserName": "firefox",
"acceptInsecureCerts": true,
"moz:debuggerAddress": true,
"pageLoadStrategy": "normal",
"moz:firefoxOptions": {
"prefs": {},
"args": [],
}
}
}
}),
};
let service = match Service::new(&pebbles_capabilities, geckodriver_cmd) {
Ok(response) => Rc::new(response),
Err(error) => return Err(error),
};
let mouse: Mouse = Mouse::new(service.clone());
let navigator: Navigator = Navigator::new(service.clone());
let keyboard: Keyboard = Keyboard::new(service.clone());
Ok(Pebbles {
service,
mouse,
keyboard,
navigator,
})
}
pub fn query(&self, identifier: &str) -> Result<Vec<WebElement>, PebblesError> {
let body = if identifier.starts_with("$") {
json!({"using": "xpath", "value": &identifier[1..]})
} else {
json!({"using": "css selector", "value": identifier})
};
let all_elements = self.service.post("/elements", &body)?.json::<Value>()?["value"]
.as_array()
.ok_or_else(|| {
PebblesError::PhrasingError(PebblesErrorDetails::new(
"Error Fetching Elements from json.",
))
})?
.clone();
if all_elements.len() == 0 {
return Err(error!(PebblesError::NoElementsError,
"No elements found."
));
}
let elements_vec: Vec<WebElement> = all_elements
.iter()
.filter_map(|element| element["element-6066-11e4-a52e-4f735466cecf"].as_str())
.map(|value| WebElement::new(self.service.clone(), value))
.collect();
Ok(elements_vec)
}
pub fn actions(&self, actions: &Value) -> Result<(), PebblesError> {
self.service.post("/actions", actions)?;
Ok(())
}
pub fn java_script(&self, script: &str, arguments: &[Value]) -> Result<Value, PebblesError> {
match self
.service
.post(
"/execute/sync",
&json!({"script": script, "args": arguments}),
)?
.json::<Value>()?
.get("value") {
Some(value) => Ok(value.clone()),
None => raise!(PebblesError::PhrasingError, "Could not phrase javascript response."),
}
}
pub fn viewport_rect(&self) -> Result<Rect, PebblesError> {
fn get_value_as_i64(json_value: &Value, key: &str) -> Result<i64, PebblesError> {
Ok(json_value.get(key)
.ok_or_else(|| error!(PebblesError::PhrasingError, "Invalid Javascript Response could not get Window Size from WebDriver.", json_value.clone()))?
.as_f64()
.ok_or_else(|| error!(PebblesError::PhrasingError, "Invalid Javascript Response could not get Window Size from WebDriver.", json_value.clone()))? as i64
)
}
let response = self
.service
.post(
"/execute/sync",
&json!({"script": "return {\"x\": window.pageXOffset, \"y\": window.pageYOffset, \"width\": window.innerWidth, \"height\": window.innerHeight};", "args": []})
)?.json::<Value>()?
.get("value")
.ok_or_else(|| error!(PebblesError::PhrasingError, "Could not get window size from javascript response."))?.clone();
let window_x: i64 = get_value_as_i64(&response, "x")?;
let window_y: i64 = get_value_as_i64(&response, "y")?;
let window_width: i64 = get_value_as_i64(&response, "width")?;
let window_height: i64 = get_value_as_i64(&response, "height")?;
Ok(
Rect{
x:window_x,
y:window_y,
width:window_width,
height:window_height,
}
)
}
}