mod common; #[cfg(feature = "component")] mod feature_component { use super::common::*; use assert_matches::assert_matches; use rstest::rstest; use std::time::Instant; use thirtyfour::components::{Component, ElementResolver}; use thirtyfour::error::WebDriverErrorInner; use thirtyfour::extensions::query::ElementQueryOptions; use thirtyfour::support::block_on; use thirtyfour::{prelude::*, resolve, resolve_present}; /// This component shows how to nest components inside others. #[derive(Debug, Clone, Component)] pub struct CheckboxSectionComponent { base: WebElement, #[by(tag = "label", not_empty)] boxes: ElementResolver>, // Other fields will be initialised with Default::default(). my_field: bool, } /// This component shows how to wrap a simple web component. #[derive(Debug, Clone, Component)] pub struct CheckboxComponent { #[base] source: WebElement, #[by(css = "input[type='checkbox']", single)] input: ElementResolver, } impl CheckboxComponent { /// Return true if the checkbox is ticked. pub async fn is_ticked(&self) -> WebDriverResult { let prop = resolve!(self.input).prop("checked").await?; Ok(prop.unwrap_or_default() == "true") } /// Tick the checkbox if it is clickable and isn't yet ticked. pub async fn tick(&self) -> WebDriverResult<()> { let elem = resolve_present!(self.input); if elem.is_clickable().await? && !self.is_ticked().await? { elem.click().await?; assert!(self.is_ticked().await?); } Ok(()) } } #[rstest] fn basic_component(test_harness: TestHarness) -> WebDriverResult<()> { let c = test_harness.driver(); block_on(async { let url = sample_page_url(); c.goto(&url).await?; // Get the checkbox div. // NOTE: components using the `Component` derive automatically implement `From`. let section: CheckboxSectionComponent = c.query(By::Id("checkbox-section")).single().await?.into(); assert!(!section.my_field); assert_eq!(section.base.id().await?.unwrap(), "checkbox-section"); // Tick all the checkboxes, ignoring any that are disabled. for checkbox in resolve!(section.boxes) { assert_eq!(checkbox.base_element().tag_name().await?, "label"); checkbox.tick().await?; } Ok(()) }) } #[derive(Debug, Component, Clone)] pub struct TestComponent { base: WebElement, #[by(tag = "label")] elem_single: ElementResolver, #[by(tag = "label", single)] elem_single_explicit: ElementResolver, #[by(tag = "label", first)] elem_first: ElementResolver, #[by(tag = "label", description = "my_test_description")] elem_desc: ElementResolver, #[by(tag = "notfound", ignore_errors, wait(timeout_ms = 1500, interval_ms = 100))] elem_ignore: ElementResolver, #[by(tag = "notfound", nowait)] elem_nowait: ElementResolver, #[by(tag = "notfound", allow_empty, nowait)] elems_allow_empty: ElementResolver>, #[by(tag = "notfound", nowait)] elems_not_empty: ElementResolver>, #[by(tag = "notfound", nowait, not_empty)] elems_not_empty_explicit: ElementResolver>, } #[rstest] fn component_attributes(test_harness: TestHarness) -> WebDriverResult<()> { let c = test_harness.driver(); block_on(async { let url = sample_page_url(); c.goto(&url).await?; ElementQueryOptions::default().set_description(Some("hello")); let elem = c.query(By::Id("checkbox-section")).single().await?; let tc = TestComponent::new(elem); let result = tc.elem_single.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); let result = tc.elem_single_explicit.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); let elem = tc.elem_first.resolve().await?; assert_eq!(elem.tag_name().await?, "label"); let result = tc.elem_desc.resolve().await; assert_matches!(result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(x)) if x.error.contains("my_test_description")); let start = Instant::now(); let result = tc.elem_ignore.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); assert!(start.elapsed().as_secs() > 0); let start = Instant::now(); let result = tc.elem_nowait.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); assert!(start.elapsed().as_secs() < 10); let elems = tc.elems_allow_empty.resolve().await?; assert!(elems.is_empty()); let result = tc.elems_not_empty.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); let result = tc.elems_not_empty_explicit.resolve().await; assert_matches!( result.map_err(WebDriverError::into_inner), Err(WebDriverErrorInner::NoSuchElement(_)) ); Ok(()) }) } #[derive(Debug, Component, Clone)] pub struct TestComponentCustomFn { base: WebElement, #[by(custom = custom_resolve_fn)] elem_custom: ElementResolver, #[by(custom = custom_resolve_fn_multi)] elems_custom: ElementResolver>, #[by(custom = custom_resolve_fn_component)] component_custom: ElementResolver, #[by(custom = custom_resolve_fn_components)] components_custom: ElementResolver>, } async fn custom_resolve_fn(elem: WebElement) -> WebDriverResult { elem.query(By::Tag("label")).first().await } async fn custom_resolve_fn_multi(elem: WebElement) -> WebDriverResult> { elem.query(By::Tag("label")).all_from_selector().await } async fn custom_resolve_fn_component(elem: WebElement) -> WebDriverResult { let cb = elem.query(By::Tag("label")).first().await?; Ok(CheckboxComponent::new(cb)) } async fn custom_resolve_fn_components( elem: WebElement, ) -> WebDriverResult> { let cbs = elem.query(By::Tag("label")).all_from_selector().await?; Ok(cbs.into_iter().map(CheckboxComponent::new).collect()) } #[rstest] fn component_attributes_custom_fn(test_harness: TestHarness) -> WebDriverResult<()> { let c = test_harness.driver(); block_on(async { let url = sample_page_url(); c.goto(&url).await?; ElementQueryOptions::default().set_description(Some("hello")); let elem = c.query(By::Id("checkbox-section")).single().await?; let tc = TestComponentCustomFn::new(elem); tc.elem_custom.resolve().await?; tc.elems_custom.resolve().await?; tc.component_custom.resolve().await?; tc.components_custom.resolve().await?; Ok(()) }) } }