browser_commander/interactions/
keyboard.rs1#[cfg(test)]
11use crate::core::engine::PdfOptions;
12use crate::core::engine::{EngineAdapter, EngineError};
13
14pub async fn press_key(engine: &dyn EngineAdapter, key: &str) -> Result<(), EngineError> {
36 engine.keyboard_press(key).await
37}
38
39pub async fn type_text(engine: &dyn EngineAdapter, text: &str) -> Result<(), EngineError> {
53 engine.keyboard_type(text).await
54}
55
56pub async fn key_down(engine: &dyn EngineAdapter, key: &str) -> Result<(), EngineError> {
69 engine.keyboard_down(key).await
70}
71
72pub async fn key_up(engine: &dyn EngineAdapter, key: &str) -> Result<(), EngineError> {
83 engine.keyboard_up(key).await
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::core::engine::{ElementInfo, EngineError, EngineType};
90 use async_trait::async_trait;
91 use std::sync::{Arc, Mutex};
92
93 struct MockEngine {
95 pressed_keys: Arc<Mutex<Vec<String>>>,
96 typed_texts: Arc<Mutex<Vec<String>>>,
97 down_keys: Arc<Mutex<Vec<String>>>,
98 up_keys: Arc<Mutex<Vec<String>>>,
99 }
100
101 impl MockEngine {
102 fn new() -> Self {
103 Self {
104 pressed_keys: Arc::new(Mutex::new(vec![])),
105 typed_texts: Arc::new(Mutex::new(vec![])),
106 down_keys: Arc::new(Mutex::new(vec![])),
107 up_keys: Arc::new(Mutex::new(vec![])),
108 }
109 }
110 }
111
112 #[async_trait]
113 impl EngineAdapter for MockEngine {
114 fn engine_type(&self) -> EngineType {
115 EngineType::Fantoccini
116 }
117
118 async fn url(&self) -> Result<String, EngineError> {
119 Ok("https://example.com".to_string())
120 }
121
122 async fn goto(&self, _url: &str) -> Result<(), EngineError> {
123 Ok(())
124 }
125
126 async fn query_selector(
127 &self,
128 _selector: &str,
129 ) -> Result<Option<ElementInfo>, EngineError> {
130 Ok(None)
131 }
132
133 async fn query_selector_all(
134 &self,
135 _selector: &str,
136 ) -> Result<Vec<ElementInfo>, EngineError> {
137 Ok(vec![])
138 }
139
140 async fn count(&self, _selector: &str) -> Result<usize, EngineError> {
141 Ok(0)
142 }
143
144 async fn click(&self, _selector: &str) -> Result<(), EngineError> {
145 Ok(())
146 }
147
148 async fn fill(&self, _selector: &str, _text: &str) -> Result<(), EngineError> {
149 Ok(())
150 }
151
152 async fn type_text(&self, _selector: &str, _text: &str) -> Result<(), EngineError> {
153 Ok(())
154 }
155
156 async fn text_content(&self, _selector: &str) -> Result<Option<String>, EngineError> {
157 Ok(None)
158 }
159
160 async fn input_value(&self, _selector: &str) -> Result<Option<String>, EngineError> {
161 Ok(None)
162 }
163
164 async fn get_attribute(
165 &self,
166 _selector: &str,
167 _attribute: &str,
168 ) -> Result<Option<String>, EngineError> {
169 Ok(None)
170 }
171
172 async fn is_visible(&self, _selector: &str) -> Result<bool, EngineError> {
173 Ok(true)
174 }
175
176 async fn is_enabled(&self, _selector: &str) -> Result<bool, EngineError> {
177 Ok(true)
178 }
179
180 async fn wait_for_selector(
181 &self,
182 _selector: &str,
183 _timeout_ms: u64,
184 ) -> Result<(), EngineError> {
185 Ok(())
186 }
187
188 async fn scroll_into_view(&self, _selector: &str) -> Result<(), EngineError> {
189 Ok(())
190 }
191
192 async fn evaluate(&self, _script: &str) -> Result<serde_json::Value, EngineError> {
193 Ok(serde_json::Value::Null)
194 }
195
196 async fn screenshot(&self) -> Result<Vec<u8>, EngineError> {
197 Ok(vec![])
198 }
199
200 async fn bring_to_front(&self) -> Result<(), EngineError> {
201 Ok(())
202 }
203
204 async fn wait_for_navigation(&self, _timeout_ms: u64) -> Result<(), EngineError> {
205 Ok(())
206 }
207
208 async fn keyboard_press(&self, key: &str) -> Result<(), EngineError> {
209 self.pressed_keys.lock().unwrap().push(key.to_string());
210 Ok(())
211 }
212
213 async fn keyboard_type(&self, text: &str) -> Result<(), EngineError> {
214 self.typed_texts.lock().unwrap().push(text.to_string());
215 Ok(())
216 }
217
218 async fn keyboard_down(&self, key: &str) -> Result<(), EngineError> {
219 self.down_keys.lock().unwrap().push(key.to_string());
220 Ok(())
221 }
222
223 async fn keyboard_up(&self, key: &str) -> Result<(), EngineError> {
224 self.up_keys.lock().unwrap().push(key.to_string());
225 Ok(())
226 }
227
228 async fn pdf(&self, _options: PdfOptions) -> Result<Vec<u8>, EngineError> {
229 Err(EngineError::Browser(
230 "PDF not supported in mock engine".to_string(),
231 ))
232 }
233 }
234
235 #[tokio::test]
236 async fn test_press_key() {
237 let engine = MockEngine::new();
238 press_key(&engine, "Escape").await.unwrap();
239 assert_eq!(
240 *engine.pressed_keys.lock().unwrap(),
241 vec!["Escape".to_string()]
242 );
243 }
244
245 #[tokio::test]
246 async fn test_type_text() {
247 let engine = MockEngine::new();
248 type_text(&engine, "Hello World").await.unwrap();
249 assert_eq!(
250 *engine.typed_texts.lock().unwrap(),
251 vec!["Hello World".to_string()]
252 );
253 }
254
255 #[tokio::test]
256 async fn test_key_down() {
257 let engine = MockEngine::new();
258 key_down(&engine, "Control").await.unwrap();
259 assert_eq!(
260 *engine.down_keys.lock().unwrap(),
261 vec!["Control".to_string()]
262 );
263 }
264
265 #[tokio::test]
266 async fn test_key_up() {
267 let engine = MockEngine::new();
268 key_up(&engine, "Control").await.unwrap();
269 assert_eq!(*engine.up_keys.lock().unwrap(), vec!["Control".to_string()]);
270 }
271
272 #[tokio::test]
273 async fn test_press_enter_key() {
274 let engine = MockEngine::new();
275 press_key(&engine, "Enter").await.unwrap();
276 assert_eq!(
277 *engine.pressed_keys.lock().unwrap(),
278 vec!["Enter".to_string()]
279 );
280 }
281}