use fantoccini::elements::{Element, ElementRef};
use fantoccini::error::CmdError;
use serde::ser::{Serialize, Serializer};
use serde_json::Value;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use crate::error::WebDriverError;
use crate::session::handle::SessionHandle;
use crate::{common::types::ElementRect, error::WebDriverResult, By, ElementRefHelper};
#[derive(Clone)]
pub struct WebElement {
pub element: Element,
pub handle: SessionHandle,
}
impl Deref for WebElement {
type Target = Element;
fn deref(&self) -> &Self::Target {
&self.element
}
}
impl DerefMut for WebElement {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.element
}
}
impl Debug for WebElement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("WebElement").field("element", &self.element).finish()
}
}
impl WebElement {
pub(crate) fn new(element: Element, handle: SessionHandle) -> Self {
Self {
element,
handle,
}
}
pub fn from_json(value: Value, handle: SessionHandle) -> WebDriverResult<Self> {
let element_ref: ElementRefHelper = serde_json::from_value(value)?;
Ok(Self {
element: Element::from_element_id(handle.client.clone(), element_ref.into()),
handle,
})
}
pub fn to_json(&self) -> WebDriverResult<Value> {
Ok(serde_json::to_value(self.element.clone())?)
}
pub fn element_id(&self) -> ElementRef {
self.element.element_id()
}
pub async fn rect(&self) -> WebDriverResult<ElementRect> {
let (x, y, w, h) = self.element.rectangle().await?;
Ok(ElementRect {
x,
y,
width: w,
height: h,
})
}
pub async fn tag_name(&self) -> WebDriverResult<String> {
Ok(self.element.tag_name().await?)
}
pub async fn class_name(&self) -> WebDriverResult<Option<String>> {
self.get_attribute("class").await
}
pub async fn id(&self) -> WebDriverResult<Option<String>> {
self.get_attribute("id").await
}
pub async fn text(&self) -> WebDriverResult<String> {
Ok(self.element.text().await?)
}
pub async fn value(&self) -> WebDriverResult<Option<String>> {
self.get_attribute("value").await
}
pub async fn click(&self) -> WebDriverResult<()> {
self.element.click().await?;
Ok(())
}
pub async fn clear(&self) -> WebDriverResult<()> {
Ok(self.element.clear().await?)
}
pub async fn get_property(&self, name: &str) -> WebDriverResult<Option<String>> {
Ok(self.element.prop(name).await?)
}
pub async fn get_attribute(&self, name: &str) -> WebDriverResult<Option<String>> {
Ok(self.element.attr(name).await?)
}
pub async fn get_css_property(&self, name: &str) -> WebDriverResult<String> {
Ok(self.element.css_value(name).await?)
}
pub async fn is_selected(&self) -> WebDriverResult<bool> {
Ok(self.element.is_selected().await?)
}
pub async fn is_displayed(&self) -> WebDriverResult<bool> {
Ok(self.element.is_displayed().await?)
}
pub async fn is_enabled(&self) -> WebDriverResult<bool> {
Ok(self.element.is_enabled().await?)
}
pub async fn is_clickable(&self) -> WebDriverResult<bool> {
Ok(self.is_displayed().await? && self.is_enabled().await?)
}
pub async fn is_present(&self) -> WebDriverResult<bool> {
let present = match self.tag_name().await {
Ok(..) => true,
Err(WebDriverError::NoSuchElement(..)) => false,
Err(e) => return Err(e),
};
Ok(present)
}
pub async fn find_element(&self, by: By) -> WebDriverResult<WebElement> {
let elem = self.element.find(by.locator()).await.map_err(|e| match e {
CmdError::NoSuchElement(_) => WebDriverError::NoSuchElement(by.to_string()),
x => WebDriverError::CmdError(x),
})?;
Ok(WebElement::new(elem, self.handle.clone()))
}
pub async fn find_elements(&self, by: By) -> WebDriverResult<Vec<WebElement>> {
let elems = self.element.find_all(by.locator()).await.map_err(|e| match e {
CmdError::NoSuchElement(_) => WebDriverError::NoSuchElement(by.to_string()),
x => WebDriverError::CmdError(x),
})?;
Ok(elems.into_iter().map(|x| WebElement::new(x, self.handle.clone())).collect())
}
pub async fn send_keys(&self, keys: impl AsRef<str>) -> WebDriverResult<()> {
Ok(self.element.send_keys(keys.as_ref()).await?)
}
pub async fn screenshot_as_png(&self) -> WebDriverResult<Vec<u8>> {
Ok(self.element.screenshot().await?)
}
pub async fn screenshot(&self, path: &Path) -> WebDriverResult<()> {
let png = self.screenshot_as_png().await?;
let mut file = File::create(path).await?;
file.write_all(&png).await?;
Ok(())
}
pub async fn focus(&self) -> WebDriverResult<()> {
self.handle.execute_script(r#"arguments[0].focus();"#, vec![self.to_json()?]).await?;
Ok(())
}
pub async fn scroll_into_view(&self) -> WebDriverResult<()> {
self.handle
.execute_script(r#"arguments[0].scrollIntoView();"#, vec![self.to_json()?])
.await?;
Ok(())
}
pub async fn inner_html(&self) -> WebDriverResult<String> {
self.get_property("innerHTML").await.map(|x| x.unwrap_or_default())
}
pub async fn outer_html(&self) -> WebDriverResult<String> {
self.get_property("outerHTML").await.map(|x| x.unwrap_or_default())
}
pub async fn get_shadow_root(&self) -> WebDriverResult<WebElement> {
let ret = self
.handle
.execute_script("return arguments[0].shadowRoot", vec![self.to_json()?])
.await?;
ret.get_element()
}
}
impl fmt::Display for WebElement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.element)
}
}
impl Serialize for WebElement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.element.serialize(serializer)
}
}