viewpoint_core/page/locator/queries/
state.rs

1//! Basic state query methods for locators.
2
3use viewpoint_cdp::protocol::dom::BackendNodeId;
4use viewpoint_js::js;
5
6use super::super::Locator;
7use super::super::Selector;
8use crate::error::LocatorError;
9
10impl<'a> Locator<'a> {
11    /// Get the text content of the first matching element.
12    ///
13    /// # Errors
14    ///
15    /// Returns an error if the element cannot be queried.
16    pub async fn text_content(&self) -> Result<Option<String>, LocatorError> {
17        let info = self.query_element_info().await?;
18        Ok(info.text)
19    }
20
21    /// Check if the element is visible.
22    ///
23    /// # Errors
24    ///
25    /// Returns an error if the element cannot be queried.
26    pub async fn is_visible(&self) -> Result<bool, LocatorError> {
27        let info = self.query_element_info().await?;
28        Ok(info.visible.unwrap_or(false))
29    }
30
31    /// Check if the element is checked (for checkboxes/radios).
32    ///
33    /// # Errors
34    ///
35    /// Returns an error if the element cannot be queried.
36    pub async fn is_checked(&self) -> Result<bool, LocatorError> {
37        // Handle Ref selector - lookup in ref map and resolve via CDP
38        if let Selector::Ref(ref_str) = &self.selector {
39            let backend_node_id = self.page.get_backend_node_id_for_ref(ref_str)?;
40            return self.is_checked_by_backend_id(backend_node_id).await;
41        }
42
43        // Handle BackendNodeId selector
44        if let Selector::BackendNodeId(backend_node_id) = &self.selector {
45            return self.is_checked_by_backend_id(*backend_node_id).await;
46        }
47
48        let selector_expr = self.selector.to_js_expression();
49        let js_code = js! {
50            (function() {
51                const elements = @{selector_expr};
52                if (elements.length === 0) return { found: false, checked: false };
53                const el = elements[0];
54                return { found: true, checked: el.checked || false };
55            })()
56        };
57
58        let result = self.evaluate_js(&js_code).await?;
59        let checked: bool = result
60            .get("checked")
61            .and_then(serde_json::Value::as_bool)
62            .unwrap_or(false);
63        Ok(checked)
64    }
65
66    /// Check if element is checked by backend node ID.
67    pub(super) async fn is_checked_by_backend_id(
68        &self,
69        backend_node_id: BackendNodeId,
70    ) -> Result<bool, LocatorError> {
71        let js_fn = js! {
72            (function() {
73                return { checked: this.checked || false };
74            })
75        };
76        // Strip outer parentheses for CDP functionDeclaration
77        let js_fn = js_fn.trim_start_matches('(').trim_end_matches(')');
78
79        let result = self
80            .call_function_on_backend_id(backend_node_id, js_fn)
81            .await?;
82
83        Ok(result
84            .get("checked")
85            .and_then(serde_json::Value::as_bool)
86            .unwrap_or(false))
87    }
88
89    /// Count matching elements.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if the elements cannot be queried.
94    pub async fn count(&self) -> Result<usize, LocatorError> {
95        let info = self.query_element_info().await?;
96        Ok(info.count)
97    }
98
99    /// Return all matching elements as individual locators.
100    ///
101    /// Each returned locator points to a single element (via nth index).
102    ///
103    /// # Example
104    ///
105    /// ```no_run
106    /// use viewpoint_core::Page;
107    ///
108    /// # async fn example(page: &Page) -> Result<(), viewpoint_core::CoreError> {
109    /// let items = page.locator("li").all().await?;
110    /// for item in items {
111    ///     println!("{}", item.text_content().await?.unwrap_or_default());
112    /// }
113    /// # Ok(())
114    /// # }
115    /// ```
116    ///
117    /// # Errors
118    ///
119    /// Returns an error if the elements cannot be queried.
120    pub async fn all(&self) -> Result<Vec<Locator<'a>>, LocatorError> {
121        let count = self.count().await?;
122        let mut locators = Vec::with_capacity(count);
123        for i in 0..count {
124            locators.push(self.nth(i as i32));
125        }
126        Ok(locators)
127    }
128}