viewpoint_core/page/locator/queries/
attributes.rs1use viewpoint_cdp::protocol::dom::BackendNodeId;
4use viewpoint_js::js;
5
6use super::super::Locator;
7use super::super::Selector;
8use crate::error::LocatorError;
9
10impl Locator<'_> {
11 pub async fn get_attribute(&self, name: &str) -> Result<Option<String>, LocatorError> {
17 if let Selector::Ref(ref_str) = &self.selector {
19 let backend_node_id = self.page.get_backend_node_id_for_ref(ref_str)?;
20 return self
21 .get_attribute_by_backend_id(backend_node_id, name)
22 .await;
23 }
24
25 if let Selector::BackendNodeId(backend_node_id) = &self.selector {
27 return self
28 .get_attribute_by_backend_id(*backend_node_id, name)
29 .await;
30 }
31
32 let selector_expr = self.selector.to_js_expression();
33 let js_code = js! {
34 (function() {
35 const elements = @{selector_expr};
36 if (elements.length === 0) return { found: false };
37 const attr = elements[0].getAttribute(#{name});
38 return { found: true, value: attr };
39 })()
40 };
41
42 let result = self.evaluate_js(&js_code).await?;
43
44 let found = result
45 .get("found")
46 .and_then(serde_json::Value::as_bool)
47 .unwrap_or(false);
48 if !found {
49 return Err(LocatorError::NotFound(format!("{:?}", self.selector)));
50 }
51
52 Ok(result
53 .get("value")
54 .and_then(|v| if v.is_null() { None } else { v.as_str() })
55 .map(std::string::ToString::to_string))
56 }
57
58 pub(super) async fn get_attribute_by_backend_id(
60 &self,
61 backend_node_id: BackendNodeId,
62 name: &str,
63 ) -> Result<Option<String>, LocatorError> {
64 let js_fn = js! {
67 (function() {
68 const attr = this.getAttribute(#{name});
69 return { value: attr };
70 })
71 };
72 let js_fn = js_fn.trim_start_matches('(').trim_end_matches(')');
74 let result = self
75 .call_function_on_backend_id_with_fn(backend_node_id, js_fn)
76 .await?;
77
78 Ok(result
79 .get("value")
80 .and_then(|v| if v.is_null() { None } else { v.as_str() })
81 .map(std::string::ToString::to_string))
82 }
83
84 pub async fn input_value(&self) -> Result<String, LocatorError> {
92 if let Selector::Ref(ref_str) = &self.selector {
94 let backend_node_id = self.page.get_backend_node_id_for_ref(ref_str)?;
95 return self.input_value_by_backend_id(backend_node_id).await;
96 }
97
98 if let Selector::BackendNodeId(backend_node_id) = &self.selector {
100 return self.input_value_by_backend_id(*backend_node_id).await;
101 }
102
103 let selector_expr = self.selector.to_js_expression();
104 let js_code = js! {
105 (function() {
106 const elements = @{selector_expr};
107 if (elements.length === 0) return { found: false };
108 const el = elements[0];
109 return { found: true, value: el.value || "" };
110 })()
111 };
112
113 let result = self.evaluate_js(&js_code).await?;
114
115 let found = result
116 .get("found")
117 .and_then(serde_json::Value::as_bool)
118 .unwrap_or(false);
119 if !found {
120 return Err(LocatorError::NotFound(format!("{:?}", self.selector)));
121 }
122
123 Ok(result
124 .get("value")
125 .and_then(|v| v.as_str())
126 .unwrap_or("")
127 .to_string())
128 }
129
130 pub(super) async fn input_value_by_backend_id(
132 &self,
133 backend_node_id: BackendNodeId,
134 ) -> Result<String, LocatorError> {
135 let js_fn = js! {
136 (function() {
137 return { value: this.value || "" };
138 })
139 };
140 let js_fn = js_fn.trim_start_matches('(').trim_end_matches(')');
142
143 let result = self
144 .call_function_on_backend_id(backend_node_id, js_fn)
145 .await?;
146
147 Ok(result
148 .get("value")
149 .and_then(|v| v.as_str())
150 .unwrap_or("")
151 .to_string())
152 }
153}