1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! # Components
//!
//! Components allow you to wrap elements or groups of elements in order to abstract the
//! element selectors and focus more on the logic and flow of your website or web app.
//!
//! This approach may be familiar to anyone who has used a
//! [Page Object Model](https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models/) before.
//! However a `Component` can wrap any node in the DOM, not just "pages".
//!
//! It uses smart element resolvers that can lazily resolve elements within the component and cache them for subsequent
//! uses. You can also nest components, making them an extremely powerful feature for automating any modern web app.
//!
//! ### Example
//!
//! Given the following HTML structure:
//!
//! ```html
//! <div id="checkbox-section">
//!     <label>
//!         <input type="checkbox" id="checkbox-option-1" />
//!         Option 1
//!     </label>
//!
//!     <label>
//!         <input type="checkbox" id="checkbox-disabled" disabled />
//!         Option 2
//!     </label>
//!
//!     <label>
//!         <input type="checkbox" id="checkbox-hidden" style="display: none;" />
//!         Option 3
//!     </label>
//! </div>
//! ```
//!
//! ```ignore
//! /// This component shows how to wrap a simple web component.
//! #[derive(Debug, Clone, Component)]
//! pub struct CheckboxComponent {
//!     base: WebElement, // This is the <label> element
//!     #[by(css = "input[type='checkbox']")]
//!     input: ElementResolver<WebElement>, // This is the <input /> element
//! }
//!
//! impl CheckboxComponent {
//!     /// Return true if the checkbox is ticked.
//!     pub async fn is_ticked(&self) -> WebDriverResult<bool> {
//!         let elem = self.input.resolve().await?;
//!         let prop = elem.prop("checked").await?;
//!         Ok(prop.unwrap_or_default() == "true")
//!     }
//!
//!     /// Tick the checkbox if it is clickable and isn't already ticked.
//!     pub async fn tick(&self) -> WebDriverResult<()> {
//!         // This checks that the element is present before returning the element.
//!         // If the element had become stale, this would implicitly re-query the element.
//!         let elem = self.input.resolve_present().await?;
//!         if elem.is_clickable().await? && !self.is_ticked().await? {
//!             elem.click().await?;
//!             // Now make sure it's ticked.
//!             assert!(self.is_ticked().await?);
//!         }
//!
//!         Ok(())
//!     }
//! }
//!
//! /// This component shows how to nest components inside others.
//! #[derive(Debug, Clone, Component)]
//! pub struct CheckboxSectionComponent {
//!     base: WebElement, // This is the outer <div>
//!     #[by(tag = "label", allow_empty)]
//!     boxes: ElementResolver<Vec<CheckboxComponent>>, // ElementResolver works with Components too.
//!     // Other fields will be initialised with Default::default().
//!     my_field: bool,
//! }
//! ```
//!
//! So how do you construct a Component?
//!
//! Simple! The `Component` derive automatically implements `From<WebElement>`.
//!
//! ```ignore
//! let elem = driver.query(By::Id("checkbox-section")).await?;
//! let component = CheckboxSectionComponent::from(elem);
//!
//! // Now you can get the checkbox components easily like this.
//! let checkboxes = component.boxes.resolve().await?;
//! for checkbox in checkboxes {
//!     checkbox.tick().await?;
//! }
//! ```
//!
//! This allows you to wrap any component using `ElementResolver` to resolve elements and nested
//! components easily.
//!

/// Wrapper for `<select>` elements.
mod select;
/// Component wrappers.
mod wrapper;

pub use select::*;
pub use wrapper::*;