use serde::ser::{Serialize, Serializer};
use serde_json::Value;
use std::fmt;
use std::path::Path;
use std::sync::Arc;
use crate::common::command::Command;
use crate::error::{WebDriverError, WebDriverErrorInner};
use crate::js::SIMULATE_DRAG_AND_DROP;
use crate::session::handle::SessionHandle;
use crate::support::base64_decode;
use crate::{By, ElementRef, common::types::ElementRect, error::WebDriverResult};
use crate::{ElementId, TypingData};
use crate::{IntoArcStr, support};
#[derive(Clone)]
pub struct WebElement {
pub element_id: ElementId,
pub handle: Arc<SessionHandle>,
}
impl fmt::Debug for WebElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WebElement").field("element", &self.element_id).finish()
}
}
impl PartialEq for WebElement {
fn eq(&self, other: &Self) -> bool {
self.element_id() == other.element_id()
}
}
impl Eq for WebElement {}
impl WebElement {
pub(crate) fn new(element_id: ElementId, handle: Arc<SessionHandle>) -> Self {
Self {
element_id,
handle,
}
}
pub fn from_json(value: Value, handle: Arc<SessionHandle>) -> WebDriverResult<Self> {
let element_ref: ElementRef = serde_json::from_value(value)?;
Ok(Self {
element_id: ElementId::from(element_ref.id()),
handle,
})
}
pub fn to_json(&self) -> WebDriverResult<Value> {
Ok(serde_json::to_value(ElementRef::Element {
id: self.element_id.to_string(),
})?)
}
pub fn element_id(&self) -> ElementId {
self.element_id.clone()
}
pub async fn rect(&self) -> WebDriverResult<ElementRect> {
let r = self.handle.cmd(Command::GetElementRect(self.element_id.clone())).await?;
r.value()
}
#[deprecated(since = "0.32.0", note = "Use rect() instead")]
pub async fn rectangle(&self) -> WebDriverResult<ElementRect> {
self.rect().await
}
pub async fn tag_name(&self) -> WebDriverResult<String> {
self.handle.cmd(Command::GetElementTagName(self.element_id.clone())).await?.value()
}
pub async fn class_name(&self) -> WebDriverResult<Option<String>> {
self.attr("class").await
}
pub async fn id(&self) -> WebDriverResult<Option<String>> {
self.attr("id").await
}
pub async fn text(&self) -> WebDriverResult<String> {
self.handle.cmd(Command::GetElementText(self.element_id.clone())).await?.value()
}
pub async fn value(&self) -> WebDriverResult<Option<String>> {
self.prop("value").await
}
pub async fn click(&self) -> WebDriverResult<()> {
self.handle.cmd(Command::ElementClick(self.element_id.clone())).await?;
Ok(())
}
pub async fn clear(&self) -> WebDriverResult<()> {
self.handle.cmd(Command::ElementClear(self.element_id.clone())).await?;
Ok(())
}
pub async fn prop(&self, name: impl IntoArcStr) -> WebDriverResult<Option<String>> {
let resp = self
.handle
.cmd(Command::GetElementProperty(self.element_id.clone(), name.into()))
.await?;
match resp.value()? {
Value::String(v) => Ok(Some(v)),
Value::Bool(b) => Ok(Some(b.to_string())),
Value::Number(number) => Ok(Some(number.to_string())),
Value::Null => Ok(None),
v => Err(WebDriverError::Json(format!("Unexpected value for property: {:?}", v))),
}
}
#[deprecated(since = "0.30.0", note = "This method has been renamed to prop()")]
pub async fn get_property(&self, name: impl IntoArcStr) -> WebDriverResult<Option<String>> {
self.prop(name).await
}
pub async fn attr(&self, name: impl IntoArcStr) -> WebDriverResult<Option<String>> {
self.handle
.cmd(Command::GetElementAttribute(self.element_id.clone(), name.into()))
.await?
.value()
}
#[deprecated(since = "0.30.0", note = "This method has been renamed to attr()")]
pub async fn get_attribute(&self, name: impl IntoArcStr) -> WebDriverResult<Option<String>> {
self.attr(name).await
}
pub async fn css_value(&self, name: impl IntoArcStr) -> WebDriverResult<String> {
self.handle
.cmd(Command::GetElementCssValue(self.element_id.clone(), name.into()))
.await?
.value()
}
#[deprecated(since = "0.30.0", note = "This method has been renamed to css_value()")]
pub async fn get_css_property(&self, name: impl IntoArcStr) -> WebDriverResult<String> {
self.css_value(name).await
}
pub async fn is_selected(&self) -> WebDriverResult<bool> {
self.handle.cmd(Command::IsElementSelected(self.element_id.clone())).await?.value()
}
pub async fn is_displayed(&self) -> WebDriverResult<bool> {
self.handle.cmd(Command::IsElementDisplayed(self.element_id.clone())).await?.value()
}
pub async fn is_enabled(&self) -> WebDriverResult<bool> {
self.handle.cmd(Command::IsElementEnabled(self.element_id.clone())).await?.value()
}
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(e) if matches!(*e, WebDriverErrorInner::StaleElementReference(..)) => false,
Err(e) => return Err(e),
};
Ok(present)
}
pub async fn find(&self, by: By) -> WebDriverResult<WebElement> {
let r = self
.handle
.cmd(Command::FindElementFromElement(self.element_id.clone(), by.into()))
.await?;
r.element(self.handle.clone())
}
#[deprecated(since = "0.30.0", note = "This method has been renamed to find()")]
pub async fn find_element(&self, by: By) -> WebDriverResult<WebElement> {
self.find(by).await
}
pub async fn find_all(&self, by: By) -> WebDriverResult<Vec<WebElement>> {
let r = self
.handle
.cmd(Command::FindElementsFromElement(self.element_id.clone(), by.into()))
.await?;
r.elements(self.handle.clone())
}
#[deprecated(since = "0.30.0", note = "This method has been renamed to find_all()")]
pub async fn find_elements(&self, by: By) -> WebDriverResult<Vec<WebElement>> {
self.find_all(by).await
}
pub async fn send_keys(&self, key: impl Into<TypingData>) -> WebDriverResult<()> {
self.handle.cmd(Command::ElementSendKeys(self.element_id.clone(), key.into())).await?;
Ok(())
}
pub async fn screenshot_as_png_base64(&self) -> WebDriverResult<String> {
self.handle.cmd(Command::TakeElementScreenshot(self.element_id.clone())).await?.value()
}
pub async fn screenshot_as_png(&self) -> WebDriverResult<Vec<u8>> {
base64_decode(&self.screenshot_as_png_base64().await?)
}
pub async fn screenshot(&self, path: &Path) -> WebDriverResult<()> {
let png = self.screenshot_as_png().await?;
support::write_file(path, png).await?;
Ok(())
}
pub async fn focus(&self) -> WebDriverResult<()> {
self.handle.execute(r#"arguments[0].focus();"#, vec![self.to_json()?]).await?;
Ok(())
}
pub async fn scroll_into_view(&self) -> WebDriverResult<()> {
self.handle
.execute(
r#"arguments[0].scrollIntoView({block: "center", inline: "center"});"#,
vec![self.to_json()?],
)
.await?;
Ok(())
}
pub async fn inner_html(&self) -> WebDriverResult<String> {
self.prop("innerHTML").await.map(|x| x.unwrap_or_default())
}
pub async fn outer_html(&self) -> WebDriverResult<String> {
self.prop("outerHTML").await.map(|x| x.unwrap_or_default())
}
pub async fn get_shadow_root(&self) -> WebDriverResult<WebElement> {
let ret =
self.handle.execute("return arguments[0].shadowRoot", vec![self.to_json()?]).await?;
ret.element()
}
pub async fn enter_frame(self) -> WebDriverResult<()> {
self.handle.cmd(Command::SwitchToFrameElement(self.element_id.clone())).await?;
Ok(())
}
pub async fn js_drag_to(&self, target: &Self) -> WebDriverResult<()> {
self.handle
.execute(SIMULATE_DRAG_AND_DROP, vec![self.to_json()?, target.to_json()?])
.await?;
Ok(())
}
pub async fn parent(&self) -> WebDriverResult<Self> {
self.find(By::XPath("./..")).await
}
}
impl fmt::Display for WebElement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.element_id)
}
}
impl Serialize for WebElement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.element_id.serialize(serializer)
}
}