use crate::{
keyboard::Keyboard,
mouse::Mouse,
result::{PebblesError, PebblesErrorDetails},
service::Service,
webelement::WebElement,
};
use serde_json::{json, Value};
use std::rc::Rc;
pub struct Pebbles {
pub service: Rc<Service>,
pub mouse: Mouse,
pub keyboard: Keyboard,
}
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 keyboard: Keyboard = Keyboard::new(service.clone());
Ok(Pebbles {
service,
mouse,
keyboard,
})
}
pub fn get(&self, url: &str) -> Result<(), PebblesError> {
self.service.post("/url", &json!({"url":url}))?;
Ok(())
}
pub fn url(&self) -> Result<String, PebblesError> {
let response = self.service.get("/url")?;
let response_json = response.json::<Value>()?;
match response_json["value"].as_str() {
Some(url) => Ok(url.to_string()),
None => Err(PebblesError::PhrasingError(PebblesErrorDetails::new(
"Json value is not a string",
))),
}
}
pub fn back(&self) -> Result<(), PebblesError> {
self.service.post("/back", &json!({}))?;
Ok(())
}
pub fn forward(&self) -> Result<(), PebblesError> {
self.service.post("/forward", &json!({}))?;
Ok(())
}
pub fn refresh(&self) -> Result<(), PebblesError> {
self.service.post("/refresh", &json!({}))?;
Ok(())
}
pub fn title(&self) -> Result<String, PebblesError> {
let response = self.service.get("/title")?;
let response_json = response.json::<Value>()?;
match response_json["value"].as_str() {
Some(url) => Ok(url.to_string()),
None => Err(PebblesError::PhrasingError(PebblesErrorDetails::new(
"Json value is not a string",
))),
}
}
pub fn close(&self) -> Result<(), PebblesError> {
self.service.delete("/window")?;
Ok(())
}
pub fn maximize(&self) -> Result<(), PebblesError> {
self.service.post("/window/maximize", &json!({}))?;
Ok(())
}
pub fn minimize(&self) -> Result<(), PebblesError> {
self.service.post("/window/minimize", &json!({}))?;
Ok(())
}
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(PebblesError::NoElementsError(PebblesErrorDetails::new(
"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> {
Ok(self
.service
.post(
"/execute/sync",
&json!({"script": script, "args": arguments}),
)?
.json::<Value>()?["value"]
.clone())
}
}