1use limit_agent::error::AgentError;
7use std::str::FromStr;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum BrowserAction {
15 Open,
19 Close,
20 Snapshot,
21 Screenshot,
22 Back,
23 Forward,
24 Reload,
25
26 Click,
30 Fill,
31 Type,
32 Press,
33 Hover,
34 Select,
35 Dblclick,
36 Focus,
37 Check,
38 Uncheck,
39 Scrollintoview,
40 Drag,
41 Upload,
42 Pdf,
43
44 Get,
48 GetAttr,
49 GetCount,
50 GetBox,
51 GetStyles,
52
53 Wait,
57 WaitForText,
58 WaitForUrl,
59 WaitForLoad,
60 WaitForDownload,
61 WaitForFn,
62 WaitForState,
63
64 Find,
68 Scroll,
69 Is,
70 Download,
71
72 TabList,
76 TabNew,
77 TabClose,
78 TabSelect,
79
80 DialogAccept,
84 DialogDismiss,
85
86 Cookies,
90 CookiesSet,
91 StorageGet,
92 StorageSet,
93 NetworkRequests,
94
95 SetViewport,
99 SetDevice,
100 SetGeo,
101
102 Eval,
106}
107
108impl FromStr for BrowserAction {
109 type Err = AgentError;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 match s {
113 "open" => Ok(Self::Open),
115 "close" => Ok(Self::Close),
116 "snapshot" => Ok(Self::Snapshot),
117 "screenshot" => Ok(Self::Screenshot),
118 "back" => Ok(Self::Back),
119 "forward" => Ok(Self::Forward),
120 "reload" => Ok(Self::Reload),
121
122 "click" => Ok(Self::Click),
124 "fill" => Ok(Self::Fill),
125 "type" => Ok(Self::Type),
126 "press" => Ok(Self::Press),
127 "hover" => Ok(Self::Hover),
128 "select" => Ok(Self::Select),
129 "dblclick" => Ok(Self::Dblclick),
130 "focus" => Ok(Self::Focus),
131 "check" => Ok(Self::Check),
132 "uncheck" => Ok(Self::Uncheck),
133 "scrollintoview" => Ok(Self::Scrollintoview),
134 "drag" => Ok(Self::Drag),
135 "upload" => Ok(Self::Upload),
136 "pdf" => Ok(Self::Pdf),
137
138 "get" => Ok(Self::Get),
140 "get_attr" => Ok(Self::GetAttr),
141 "get_count" => Ok(Self::GetCount),
142 "get_box" => Ok(Self::GetBox),
143 "get_styles" => Ok(Self::GetStyles),
144
145 "wait" => Ok(Self::Wait),
147 "wait_for_text" => Ok(Self::WaitForText),
148 "wait_for_url" => Ok(Self::WaitForUrl),
149 "wait_for_load" => Ok(Self::WaitForLoad),
150 "wait_for_download" => Ok(Self::WaitForDownload),
151 "wait_for_fn" => Ok(Self::WaitForFn),
152 "wait_for_state" => Ok(Self::WaitForState),
153
154 "find" => Ok(Self::Find),
156 "scroll" => Ok(Self::Scroll),
157 "is" => Ok(Self::Is),
158 "download" => Ok(Self::Download),
159
160 "tab_list" => Ok(Self::TabList),
162 "tab_new" => Ok(Self::TabNew),
163 "tab_close" => Ok(Self::TabClose),
164 "tab_select" => Ok(Self::TabSelect),
165
166 "dialog_accept" => Ok(Self::DialogAccept),
168 "dialog_dismiss" => Ok(Self::DialogDismiss),
169
170 "cookies" => Ok(Self::Cookies),
172 "cookies_set" => Ok(Self::CookiesSet),
173 "storage_get" => Ok(Self::StorageGet),
174 "storage_set" => Ok(Self::StorageSet),
175 "network_requests" => Ok(Self::NetworkRequests),
176
177 "set_viewport" => Ok(Self::SetViewport),
179 "set_device" => Ok(Self::SetDevice),
180 "set_geo" => Ok(Self::SetGeo),
181
182 "eval" => Ok(Self::Eval),
184
185 _ => Err(AgentError::ToolError(format!(
186 "Unknown browser action: {}. Valid actions: open, close, snapshot, click, fill, screenshot, wait, wait_for_text, wait_for_url, wait_for_load, wait_for_download, wait_for_fn, wait_for_state, eval, get, get_attr, get_count, get_box, get_styles, back, forward, reload, type, press, hover, select, dblclick, focus, check, uncheck, scrollintoview, drag, upload, pdf, find, scroll, is, download, tab_list, tab_new, tab_close, tab_select, dialog_accept, dialog_dismiss, cookies, cookies_set, storage_get, storage_set, network_requests, set_viewport, set_device, set_geo",
187 s
188 ))),
189 }
190 }
191}
192
193impl BrowserAction {
194 pub fn as_str(&self) -> &'static str {
196 match self {
197 Self::Open => "open",
199 Self::Close => "close",
200 Self::Snapshot => "snapshot",
201 Self::Screenshot => "screenshot",
202 Self::Back => "back",
203 Self::Forward => "forward",
204 Self::Reload => "reload",
205
206 Self::Click => "click",
208 Self::Fill => "fill",
209 Self::Type => "type",
210 Self::Press => "press",
211 Self::Hover => "hover",
212 Self::Select => "select",
213 Self::Dblclick => "dblclick",
214 Self::Focus => "focus",
215 Self::Check => "check",
216 Self::Uncheck => "uncheck",
217 Self::Scrollintoview => "scrollintoview",
218 Self::Drag => "drag",
219 Self::Upload => "upload",
220 Self::Pdf => "pdf",
221
222 Self::Get => "get",
224 Self::GetAttr => "get_attr",
225 Self::GetCount => "get_count",
226 Self::GetBox => "get_box",
227 Self::GetStyles => "get_styles",
228
229 Self::Wait => "wait",
231 Self::WaitForText => "wait_for_text",
232 Self::WaitForUrl => "wait_for_url",
233 Self::WaitForLoad => "wait_for_load",
234 Self::WaitForDownload => "wait_for_download",
235 Self::WaitForFn => "wait_for_fn",
236 Self::WaitForState => "wait_for_state",
237
238 Self::Find => "find",
240 Self::Scroll => "scroll",
241 Self::Is => "is",
242 Self::Download => "download",
243
244 Self::TabList => "tab_list",
246 Self::TabNew => "tab_new",
247 Self::TabClose => "tab_close",
248 Self::TabSelect => "tab_select",
249
250 Self::DialogAccept => "dialog_accept",
252 Self::DialogDismiss => "dialog_dismiss",
253
254 Self::Cookies => "cookies",
256 Self::CookiesSet => "cookies_set",
257 Self::StorageGet => "storage_get",
258 Self::StorageSet => "storage_set",
259 Self::NetworkRequests => "network_requests",
260
261 Self::SetViewport => "set_viewport",
263 Self::SetDevice => "set_device",
264 Self::SetGeo => "set_geo",
265
266 Self::Eval => "eval",
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_from_str_valid_actions() {
278 let test_cases = vec![
279 ("open", BrowserAction::Open),
280 ("close", BrowserAction::Close),
281 ("snapshot", BrowserAction::Snapshot),
282 ("screenshot", BrowserAction::Screenshot),
283 ("back", BrowserAction::Back),
284 ("forward", BrowserAction::Forward),
285 ("reload", BrowserAction::Reload),
286 ("click", BrowserAction::Click),
287 ("fill", BrowserAction::Fill),
288 ("type", BrowserAction::Type),
289 ("press", BrowserAction::Press),
290 ("hover", BrowserAction::Hover),
291 ("select", BrowserAction::Select),
292 ("dblclick", BrowserAction::Dblclick),
293 ("focus", BrowserAction::Focus),
294 ("check", BrowserAction::Check),
295 ("uncheck", BrowserAction::Uncheck),
296 ("scrollintoview", BrowserAction::Scrollintoview),
297 ("drag", BrowserAction::Drag),
298 ("upload", BrowserAction::Upload),
299 ("pdf", BrowserAction::Pdf),
300 ("get", BrowserAction::Get),
301 ("get_attr", BrowserAction::GetAttr),
302 ("get_count", BrowserAction::GetCount),
303 ("get_box", BrowserAction::GetBox),
304 ("get_styles", BrowserAction::GetStyles),
305 ("wait", BrowserAction::Wait),
306 ("wait_for_text", BrowserAction::WaitForText),
307 ("wait_for_url", BrowserAction::WaitForUrl),
308 ("wait_for_load", BrowserAction::WaitForLoad),
309 ("wait_for_download", BrowserAction::WaitForDownload),
310 ("wait_for_fn", BrowserAction::WaitForFn),
311 ("wait_for_state", BrowserAction::WaitForState),
312 ("find", BrowserAction::Find),
313 ("scroll", BrowserAction::Scroll),
314 ("is", BrowserAction::Is),
315 ("download", BrowserAction::Download),
316 ("tab_list", BrowserAction::TabList),
317 ("tab_new", BrowserAction::TabNew),
318 ("tab_close", BrowserAction::TabClose),
319 ("tab_select", BrowserAction::TabSelect),
320 ("dialog_accept", BrowserAction::DialogAccept),
321 ("dialog_dismiss", BrowserAction::DialogDismiss),
322 ("cookies", BrowserAction::Cookies),
323 ("cookies_set", BrowserAction::CookiesSet),
324 ("storage_get", BrowserAction::StorageGet),
325 ("storage_set", BrowserAction::StorageSet),
326 ("network_requests", BrowserAction::NetworkRequests),
327 ("set_viewport", BrowserAction::SetViewport),
328 ("set_device", BrowserAction::SetDevice),
329 ("set_geo", BrowserAction::SetGeo),
330 ("eval", BrowserAction::Eval),
331 ];
332
333 for (s, expected) in test_cases {
334 let result = BrowserAction::from_str(s);
335 assert!(result.is_ok(), "Failed to parse '{}'", s);
336 assert_eq!(result.unwrap(), expected);
337 }
338 }
339
340 #[test]
341 fn test_as_str_roundtrip() {
342 let actions = vec![
343 BrowserAction::Open,
344 BrowserAction::Click,
345 BrowserAction::Wait,
346 BrowserAction::Eval,
347 BrowserAction::Get,
348 BrowserAction::TabList,
349 BrowserAction::Cookies,
350 BrowserAction::SetViewport,
351 BrowserAction::DialogAccept,
352 BrowserAction::NetworkRequests,
353 ];
354
355 for action in actions {
356 let s = action.as_str();
357 let parsed = BrowserAction::from_str(s).unwrap();
358 assert_eq!(action, parsed, "Roundtrip failed for {:?}", action);
359 }
360 }
361
362 #[test]
363 fn test_all_actions_have_string_representation() {
364 let test_actions = vec![
365 BrowserAction::Open,
366 BrowserAction::Close,
367 BrowserAction::Snapshot,
368 BrowserAction::Screenshot,
369 BrowserAction::Back,
370 BrowserAction::Forward,
371 BrowserAction::Reload,
372 BrowserAction::Click,
373 BrowserAction::Fill,
374 BrowserAction::Type,
375 BrowserAction::Press,
376 BrowserAction::Hover,
377 BrowserAction::Select,
378 BrowserAction::Dblclick,
379 BrowserAction::Focus,
380 BrowserAction::Check,
381 BrowserAction::Uncheck,
382 BrowserAction::Scrollintoview,
383 BrowserAction::Drag,
384 BrowserAction::Upload,
385 BrowserAction::Pdf,
386 BrowserAction::Get,
387 BrowserAction::GetAttr,
388 BrowserAction::GetCount,
389 BrowserAction::GetBox,
390 BrowserAction::GetStyles,
391 BrowserAction::Wait,
392 BrowserAction::WaitForText,
393 BrowserAction::WaitForUrl,
394 BrowserAction::WaitForLoad,
395 BrowserAction::WaitForDownload,
396 BrowserAction::WaitForFn,
397 BrowserAction::WaitForState,
398 BrowserAction::Find,
399 BrowserAction::Scroll,
400 BrowserAction::Is,
401 BrowserAction::Download,
402 BrowserAction::TabList,
403 BrowserAction::TabNew,
404 BrowserAction::TabClose,
405 BrowserAction::TabSelect,
406 BrowserAction::DialogAccept,
407 BrowserAction::DialogDismiss,
408 BrowserAction::Cookies,
409 BrowserAction::CookiesSet,
410 BrowserAction::StorageGet,
411 BrowserAction::StorageSet,
412 BrowserAction::NetworkRequests,
413 BrowserAction::SetViewport,
414 BrowserAction::SetDevice,
415 BrowserAction::SetGeo,
416 BrowserAction::Eval,
417 ];
418
419 for action in test_actions {
420 let s = action.as_str();
421 assert!(!s.is_empty(), "Empty string for {:?}", action);
422 assert!(s.is_ascii(), "Non-ASCII string for {:?}: {}", action, s);
423 }
424 }
425
426 #[test]
427 fn test_copy_traits() {
428 let action = BrowserAction::Click;
429 let _copy = action;
430 let _clone = action;
431 }
432
433 #[test]
434 fn test_hash_equality() {
435 use std::collections::HashSet;
436
437 let mut set = HashSet::new();
438 set.insert(BrowserAction::Click);
439 set.insert(BrowserAction::Open);
440 set.insert(BrowserAction::Wait);
441
442 assert_eq!(set.len(), 3);
443 assert!(set.contains(&BrowserAction::Click));
444 assert!(set.contains(&BrowserAction::Open));
445 assert!(set.contains(&BrowserAction::Wait));
446 assert!(!set.contains(&BrowserAction::Close));
447 }
448}