1pub fn format_activity_message(tool_name: &str, args: &serde_json::Value) -> String {
7 match tool_name {
8 "file_read" => format_file_read(args),
9 "file_write" => format_file_write(args),
10 "file_edit" => format_file_edit(args),
11 "bash" => format_bash(args),
12 "git_status" => "Checking git status...".to_string(),
13 "git_diff" => "Checking git diff...".to_string(),
14 "git_log" => "Checking git log...".to_string(),
15 "git_add" => "Staging files...".to_string(),
16 "git_commit" => "Creating commit...".to_string(),
17 "git_push" => "Pushing to remote...".to_string(),
18 "git_pull" => "Pulling from remote...".to_string(),
19 "git_clone" => format_git_clone(args),
20 "grep" => format_grep(args),
21 "ast_grep" => format_ast_grep(args),
22 "lsp" => format_lsp(args),
23 "browser" => format_browser(args),
24 _ => format!("Executing {}...", tool_name),
25 }
26}
27
28fn format_file_read(args: &serde_json::Value) -> String {
30 args.get("path")
31 .and_then(|p| p.as_str())
32 .map(|p| format!("Reading {}...", truncate_path(p, 150)))
33 .unwrap_or_else(|| "Reading file...".to_string())
34}
35
36fn format_file_write(args: &serde_json::Value) -> String {
38 args.get("path")
39 .and_then(|p| p.as_str())
40 .map(|p| format!("Writing {}...", truncate_path(p, 150)))
41 .unwrap_or_else(|| "Writing file...".to_string())
42}
43
44fn format_file_edit(args: &serde_json::Value) -> String {
46 args.get("path")
47 .and_then(|p| p.as_str())
48 .map(|p| format!("Editing {}...", truncate_path(p, 150)))
49 .unwrap_or_else(|| "Editing file...".to_string())
50}
51
52fn format_bash(args: &serde_json::Value) -> String {
54 args.get("command")
55 .and_then(|c| c.as_str())
56 .map(|c| format!("Running {}...", truncate_command(c, 150)))
57 .unwrap_or_else(|| "Executing command...".to_string())
58}
59
60fn format_git_clone(args: &serde_json::Value) -> String {
62 args.get("url")
63 .and_then(|u| u.as_str())
64 .map(|u| format!("Cloning {}...", truncate_path(u, 150)))
65 .unwrap_or_else(|| "Cloning repository...".to_string())
66}
67
68fn format_grep(args: &serde_json::Value) -> String {
70 args.get("pattern")
71 .and_then(|p| p.as_str())
72 .map(|p| format!("Searching for '{}'...", truncate_command(p, 150)))
73 .unwrap_or_else(|| "Searching...".to_string())
74}
75
76fn format_ast_grep(args: &serde_json::Value) -> String {
78 let command = args
79 .get("command")
80 .and_then(|c| c.as_str())
81 .unwrap_or("search");
82
83 match command {
84 "replace" => {
85 let pattern = args.get("pattern").and_then(|p| p.as_str()).unwrap_or("?");
86 let rewrite = args.get("rewrite").and_then(|r| r.as_str()).unwrap_or("?");
87 format!(
88 "AST replacing '{}' → '{}'...",
89 truncate_command(pattern, 80),
90 truncate_command(rewrite, 80)
91 )
92 }
93 "scan" => {
94 let rule = args
95 .get("rule")
96 .and_then(|r| r.as_str())
97 .map(|r| truncate_command(r, 100));
98 let inline = args
99 .get("inline_rules")
100 .and_then(|r| r.as_str())
101 .map(|r| truncate_command(r, 100));
102 match (rule, inline) {
103 (Some(r), _) => format!("AST scanning with rule '{}'...", r),
104 (_, Some(i)) => format!("AST scanning with inline rule '{}'...", i),
105 _ => "AST scanning...".to_string(),
106 }
107 }
108 _ => args
109 .get("pattern")
110 .and_then(|p| p.as_str())
111 .map(|p| format!("AST searching '{}'...", truncate_command(p, 150)))
112 .unwrap_or_else(|| "AST searching...".to_string()),
113 }
114}
115
116fn format_lsp(args: &serde_json::Value) -> String {
118 args.get("command")
119 .and_then(|c| c.as_str())
120 .map(|c| format!("Running LSP {}...", c))
121 .unwrap_or_else(|| "Running LSP...".to_string())
122}
123
124fn format_browser(args: &serde_json::Value) -> String {
125 let action = args
126 .get("action")
127 .and_then(|a| a.as_str())
128 .unwrap_or("unknown");
129
130 let detail = match action {
131 "open" => args
132 .get("url")
133 .and_then(|u| u.as_str())
134 .map(|u| format!("opening {}", truncate_path(u, 80)))
135 .unwrap_or_else(|| "opening URL".to_string()),
136 "close" => "closing browser".to_string(),
137 "snapshot" => "taking DOM snapshot".to_string(),
138 "screenshot" => args
139 .get("path")
140 .and_then(|p| p.as_str())
141 .map(|p| format!("taking screenshot to {}", truncate_path(p, 60)))
142 .unwrap_or_else(|| "taking screenshot".to_string()),
143 "back" => "navigating back".to_string(),
144 "forward" => "navigating forward".to_string(),
145 "reload" => "reloading page".to_string(),
146
147 "click" => args
148 .get("selector")
149 .and_then(|s| s.as_str())
150 .map(|s| format!("clicking {}", truncate_command(s, 80)))
151 .unwrap_or_else(|| "clicking element".to_string()),
152 "dblclick" => args
153 .get("selector")
154 .and_then(|s| s.as_str())
155 .map(|s| format!("double-clicking {}", truncate_command(s, 80)))
156 .unwrap_or_else(|| "double-clicking element".to_string()),
157 "hover" => args
158 .get("selector")
159 .and_then(|s| s.as_str())
160 .map(|s| format!("hovering {}", truncate_command(s, 80)))
161 .unwrap_or_else(|| "hovering element".to_string()),
162 "focus" => args
163 .get("selector")
164 .and_then(|s| s.as_str())
165 .map(|s| format!("focusing {}", truncate_command(s, 80)))
166 .unwrap_or_else(|| "focusing element".to_string()),
167 "scrollintoview" => args
168 .get("selector")
169 .and_then(|s| s.as_str())
170 .map(|s| format!("scrolling to {}", truncate_command(s, 80)))
171 .unwrap_or_else(|| "scrolling element into view".to_string()),
172
173 "fill" => {
174 let selector = args.get("selector").and_then(|s| s.as_str());
175 let text = args.get("text").and_then(|t| t.as_str());
176 match (selector, text) {
177 (Some(s), Some(t)) => format!(
178 "filling {} with \"{}\"",
179 truncate_command(s, 40),
180 truncate_command(t, 40)
181 ),
182 (Some(s), None) => format!("filling {}", truncate_command(s, 80)),
183 _ => "filling input".to_string(),
184 }
185 }
186 "type" => args
187 .get("selector")
188 .and_then(|s| s.as_str())
189 .map(|s| format!("typing into {}", truncate_command(s, 80)))
190 .unwrap_or_else(|| "typing".to_string()),
191 "press" => args
192 .get("key")
193 .and_then(|k| k.as_str())
194 .map(|k| format!("pressing {}", k))
195 .unwrap_or_else(|| "pressing key".to_string()),
196 "select" => {
197 let selector = args.get("selector").and_then(|s| s.as_str());
198 let value = args.get("value").and_then(|v| v.as_str());
199 match (selector, value) {
200 (Some(s), Some(v)) => format!(
201 "selecting \"{}\" in {}",
202 truncate_command(v, 40),
203 truncate_command(s, 40)
204 ),
205 (Some(s), None) => format!("selecting option in {}", truncate_command(s, 80)),
206 _ => "selecting option".to_string(),
207 }
208 }
209 "check" => args
210 .get("selector")
211 .and_then(|s| s.as_str())
212 .map(|s| format!("checking {}", truncate_command(s, 80)))
213 .unwrap_or_else(|| "checking checkbox".to_string()),
214 "uncheck" => args
215 .get("selector")
216 .and_then(|s| s.as_str())
217 .map(|s| format!("unchecking {}", truncate_command(s, 80)))
218 .unwrap_or_else(|| "unchecking checkbox".to_string()),
219 "drag" => {
220 let source = args.get("source").and_then(|s| s.as_str());
221 let target = args.get("target").and_then(|t| t.as_str());
222 match (source, target) {
223 (Some(s), Some(t)) => format!(
224 "dragging {} to {}",
225 truncate_command(s, 40),
226 truncate_command(t, 40)
227 ),
228 _ => "dragging element".to_string(),
229 }
230 }
231 "upload" => {
232 let selector = args.get("selector").and_then(|s| s.as_str());
233 let path = args.get("path").and_then(|p| p.as_str());
234 match (selector, path) {
235 (Some(s), Some(p)) => format!(
236 "uploading {} to {}",
237 truncate_path(p, 40),
238 truncate_command(s, 40)
239 ),
240 _ => "uploading file".to_string(),
241 }
242 }
243 "pdf" => args
244 .get("path")
245 .and_then(|p| p.as_str())
246 .map(|p| format!("saving PDF to {}", truncate_path(p, 60)))
247 .unwrap_or_else(|| "saving PDF".to_string()),
248
249 "get" => args
250 .get("selector")
251 .and_then(|s| s.as_str())
252 .map(|s| format!("getting element {}", truncate_command(s, 80)))
253 .unwrap_or_else(|| "getting element".to_string()),
254 "get_attr" => {
255 let selector = args.get("selector").and_then(|s| s.as_str());
256 let attr = args.get("attr").and_then(|a| a.as_str());
257 match (selector, attr) {
258 (Some(s), Some(a)) => {
259 format!("getting {} attribute from {}", a, truncate_command(s, 60))
260 }
261 _ => "getting attribute".to_string(),
262 }
263 }
264 "get_count" => args
265 .get("selector")
266 .and_then(|s| s.as_str())
267 .map(|s| format!("counting {}", truncate_command(s, 80)))
268 .unwrap_or_else(|| "counting elements".to_string()),
269 "get_box" => args
270 .get("selector")
271 .and_then(|s| s.as_str())
272 .map(|s| format!("getting bounding box of {}", truncate_command(s, 60)))
273 .unwrap_or_else(|| "getting bounding box".to_string()),
274 "get_styles" => args
275 .get("selector")
276 .and_then(|s| s.as_str())
277 .map(|s| format!("getting styles of {}", truncate_command(s, 80)))
278 .unwrap_or_else(|| "getting styles".to_string()),
279
280 "wait" => args
281 .get("ms")
282 .and_then(|m| m.as_u64())
283 .map(|m| format!("waiting {}ms", m))
284 .unwrap_or_else(|| "waiting".to_string()),
285 "wait_for_text" => args
286 .get("text")
287 .and_then(|t| t.as_str())
288 .map(|t| format!("waiting for text \"{}\"", truncate_command(t, 60)))
289 .unwrap_or_else(|| "waiting for text".to_string()),
290 "wait_for_url" => args
291 .get("url")
292 .and_then(|u| u.as_str())
293 .map(|u| format!("waiting for URL {}", truncate_path(u, 60)))
294 .unwrap_or_else(|| "waiting for URL".to_string()),
295 "wait_for_load" => "waiting for page load".to_string(),
296 "wait_for_download" => "waiting for download".to_string(),
297 "wait_for_fn" => "waiting for function result".to_string(),
298 "wait_for_state" => args
299 .get("state")
300 .and_then(|s| s.as_str())
301 .map(|s| format!("waiting for {} state", s))
302 .unwrap_or_else(|| "waiting for state".to_string()),
303
304 "find" => args
305 .get("selector")
306 .and_then(|s| s.as_str())
307 .map(|s| format!("finding {}", truncate_command(s, 80)))
308 .unwrap_or_else(|| "finding element".to_string()),
309 "scroll" => {
310 let x = args.get("x").and_then(|v| v.as_i64());
311 let y = args.get("y").and_then(|v| v.as_i64());
312 match (x, y) {
313 (Some(x), Some(y)) => format!("scrolling to ({}, {})", x, y),
314 _ => "scrolling".to_string(),
315 }
316 }
317 "is" => {
318 let selector = args.get("selector").and_then(|s| s.as_str());
319 let state = args.get("state").and_then(|s| s.as_str());
320 match (selector, state) {
321 (Some(s), Some(st)) => {
322 format!("checking if {} is {}", truncate_command(s, 60), st)
323 }
324 _ => "checking element state".to_string(),
325 }
326 }
327 "download" => args
328 .get("url")
329 .and_then(|u| u.as_str())
330 .map(|u| format!("downloading {}", truncate_path(u, 60)))
331 .unwrap_or_else(|| "downloading".to_string()),
332
333 "tab_list" => "listing tabs".to_string(),
334 "tab_new" => args
335 .get("url")
336 .and_then(|u| u.as_str())
337 .map(|u| format!("opening new tab {}", truncate_path(u, 60)))
338 .unwrap_or_else(|| "opening new tab".to_string()),
339 "tab_close" => args
340 .get("index")
341 .and_then(|i| i.as_u64())
342 .map(|i| format!("closing tab {}", i))
343 .unwrap_or_else(|| "closing tab".to_string()),
344 "tab_select" => args
345 .get("index")
346 .and_then(|i| i.as_u64())
347 .map(|i| format!("selecting tab {}", i))
348 .unwrap_or_else(|| "selecting tab".to_string()),
349
350 "dialog_accept" => args
351 .get("text")
352 .and_then(|t| t.as_str())
353 .map(|t| format!("accepting dialog with \"{}\"", truncate_command(t, 40)))
354 .unwrap_or_else(|| "accepting dialog".to_string()),
355 "dialog_dismiss" => "dismissing dialog".to_string(),
356
357 "cookies" => args
358 .get("url")
359 .and_then(|u| u.as_str())
360 .map(|u| format!("getting cookies for {}", truncate_path(u, 60)))
361 .unwrap_or_else(|| "getting cookies".to_string()),
362 "cookies_set" => "setting cookies".to_string(),
363 "storage_get" => args
364 .get("key")
365 .and_then(|k| k.as_str())
366 .map(|k| format!("getting storage \"{}\"", truncate_command(k, 40)))
367 .unwrap_or_else(|| "getting from storage".to_string()),
368 "storage_set" => args
369 .get("key")
370 .and_then(|k| k.as_str())
371 .map(|k| format!("setting storage \"{}\"", truncate_command(k, 40)))
372 .unwrap_or_else(|| "setting storage".to_string()),
373 "network_requests" => "getting network requests".to_string(),
374
375 "set_viewport" => {
376 let width = args.get("width").and_then(|w| w.as_u64());
377 let height = args.get("height").and_then(|h| h.as_u64());
378 match (width, height) {
379 (Some(w), Some(h)) => format!("setting viewport to {}x{}", w, h),
380 _ => "setting viewport".to_string(),
381 }
382 }
383 "set_device" => args
384 .get("device")
385 .and_then(|d| d.as_str())
386 .map(|d| format!("setting device to {}", d))
387 .unwrap_or_else(|| "setting device".to_string()),
388 "set_geo" => {
389 let lat = args.get("latitude").and_then(|l| l.as_f64());
390 let lon = args.get("longitude").and_then(|l| l.as_f64());
391 match (lat, lon) {
392 (Some(lat), Some(lon)) => format!("setting geolocation to ({}, {})", lat, lon),
393 _ => "setting geolocation".to_string(),
394 }
395 }
396
397 "eval" => args
398 .get("script")
399 .and_then(|s| s.as_str())
400 .map(|s| format!("evaluating {}", truncate_command(s, 60)))
401 .unwrap_or_else(|| "evaluating script".to_string()),
402
403 _ => action.to_string(),
404 };
405
406 format!("Executing browser: {}...", detail)
407}
408
409fn truncate_path(s: &str, max_len: usize) -> String {
411 let char_count = s.chars().count();
412 if char_count <= max_len {
413 s.to_string()
414 } else {
415 let skip = char_count.saturating_sub(max_len - 3);
416 let truncated: String = s.chars().skip(skip).collect();
417 format!("...{truncated}")
418 }
419}
420
421fn truncate_command(s: &str, max_len: usize) -> String {
423 let char_count = s.chars().count();
424 if char_count <= max_len {
425 s.to_string()
426 } else {
427 let truncated: String = s.chars().take(max_len.saturating_sub(3)).collect();
428 format!("{truncated}...")
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use serde_json::json;
436
437 #[test]
438 fn test_format_file_read() {
439 let args = json!({"path": "/some/long/path/to/file.txt"});
440 let msg = format_activity_message("file_read", &args);
441 assert!(msg.contains("Reading"));
442 }
443
444 #[test]
445 fn test_format_bash() {
446 let args = json!({"command": "echo hello"});
447 let msg = format_activity_message("bash", &args);
448 assert!(msg.contains("Running"));
449 }
450
451 #[test]
452 fn test_format_unknown_tool() {
453 let args = json!({});
454 let msg = format_activity_message("unknown_tool", &args);
455 assert_eq!(msg, "Executing unknown_tool...");
456 }
457
458 #[test]
459 fn test_truncate_path() {
460 assert_eq!(truncate_path("short", 10), "short");
461 assert_eq!(
462 truncate_path("very_long_path_to_file.txt", 14),
463 "...to_file.txt"
464 );
465 }
466
467 #[test]
468 fn test_truncate_path_utf8() {
469 let multi_byte = "════════════════════════════════════════════════";
470 let result = truncate_path(multi_byte, 10);
471 assert!(result.starts_with("..."));
472 assert!(result.chars().count() <= 10);
473 }
474
475 #[test]
476 fn test_truncate_command() {
477 assert_eq!(truncate_command("short", 10), "short");
478 assert_eq!(truncate_command("very_long_command_here", 10), "very_lo...");
479 }
480
481 #[test]
482 fn test_truncate_command_utf8() {
483 let multi_byte = "════════════════════════════════════════════════";
484 let result = truncate_command(multi_byte, 10);
485 assert!(result.ends_with("..."));
486 assert!(result.chars().count() <= 10);
487 }
488
489 #[test]
490 fn test_git_commands() {
491 assert_eq!(
492 format_activity_message("git_status", &json!({})),
493 "Checking git status..."
494 );
495 assert_eq!(
496 format_activity_message("git_diff", &json!({})),
497 "Checking git diff..."
498 );
499 assert_eq!(
500 format_activity_message("git_add", &json!({})),
501 "Staging files..."
502 );
503 assert_eq!(
504 format_activity_message("git_commit", &json!({})),
505 "Creating commit..."
506 );
507 }
508
509 #[test]
510 fn test_file_operations() {
511 let args = json!({"path": "/test.txt"});
512 assert!(format_activity_message("file_read", &args).contains("Reading"));
513 assert!(format_activity_message("file_write", &args).contains("Writing"));
514 assert!(format_activity_message("file_edit", &args).contains("Editing"));
515 }
516
517 #[test]
518 fn test_browser_navigation() {
519 assert_eq!(
520 format_activity_message(
521 "browser",
522 &json!({"action": "open", "url": "https://example.com"})
523 ),
524 "Executing browser: opening https://example.com..."
525 );
526 assert_eq!(
527 format_activity_message("browser", &json!({"action": "close"})),
528 "Executing browser: closing browser..."
529 );
530 assert_eq!(
531 format_activity_message(
532 "browser",
533 &json!({"action": "screenshot", "path": "/tmp/shot.png"})
534 ),
535 "Executing browser: taking screenshot to /tmp/shot.png..."
536 );
537 assert_eq!(
538 format_activity_message("browser", &json!({"action": "back"})),
539 "Executing browser: navigating back..."
540 );
541 assert_eq!(
542 format_activity_message("browser", &json!({"action": "reload"})),
543 "Executing browser: reloading page..."
544 );
545 }
546
547 #[test]
548 fn test_browser_interaction() {
549 assert_eq!(
550 format_activity_message(
551 "browser",
552 &json!({"action": "click", "selector": "#submit"})
553 ),
554 "Executing browser: clicking #submit..."
555 );
556 assert_eq!(
557 format_activity_message(
558 "browser",
559 &json!({"action": "fill", "selector": "#email", "text": "test@example.com"})
560 ),
561 "Executing browser: filling #email with \"test@example.com\"..."
562 );
563 assert_eq!(
564 format_activity_message("browser", &json!({"action": "press", "key": "Enter"})),
565 "Executing browser: pressing Enter..."
566 );
567 assert_eq!(
568 format_activity_message("browser", &json!({"action": "hover", "selector": ".menu"})),
569 "Executing browser: hovering .menu..."
570 );
571 assert_eq!(
572 format_activity_message("browser", &json!({"action": "check", "selector": "#terms"})),
573 "Executing browser: checking #terms..."
574 );
575 assert_eq!(
576 format_activity_message(
577 "browser",
578 &json!({"action": "uncheck", "selector": "#newsletter"})
579 ),
580 "Executing browser: unchecking #newsletter..."
581 );
582 }
583
584 #[test]
585 fn test_browser_wait() {
586 assert_eq!(
587 format_activity_message("browser", &json!({"action": "wait", "ms": 1000})),
588 "Executing browser: waiting 1000ms..."
589 );
590 assert_eq!(
591 format_activity_message(
592 "browser",
593 &json!({"action": "wait_for_text", "text": "Login"})
594 ),
595 "Executing browser: waiting for text \"Login\"..."
596 );
597 assert_eq!(
598 format_activity_message(
599 "browser",
600 &json!({"action": "wait_for_url", "url": "/dashboard"})
601 ),
602 "Executing browser: waiting for URL /dashboard..."
603 );
604 assert_eq!(
605 format_activity_message("browser", &json!({"action": "wait_for_load"})),
606 "Executing browser: waiting for page load..."
607 );
608 }
609
610 #[test]
611 fn test_browser_tabs() {
612 assert_eq!(
613 format_activity_message("browser", &json!({"action": "tab_list"})),
614 "Executing browser: listing tabs..."
615 );
616 assert_eq!(
617 format_activity_message(
618 "browser",
619 &json!({"action": "tab_new", "url": "https://example.com"})
620 ),
621 "Executing browser: opening new tab https://example.com..."
622 );
623 assert_eq!(
624 format_activity_message("browser", &json!({"action": "tab_select", "index": 2})),
625 "Executing browser: selecting tab 2..."
626 );
627 assert_eq!(
628 format_activity_message("browser", &json!({"action": "tab_close", "index": 1})),
629 "Executing browser: closing tab 1..."
630 );
631 }
632
633 #[test]
634 fn test_browser_query() {
635 assert_eq!(
636 format_activity_message("browser", &json!({"action": "get", "selector": ".item"})),
637 "Executing browser: getting element .item..."
638 );
639 assert_eq!(
640 format_activity_message(
641 "browser",
642 &json!({"action": "get_attr", "selector": "a", "attr": "href"})
643 ),
644 "Executing browser: getting href attribute from a..."
645 );
646 assert_eq!(
647 format_activity_message(
648 "browser",
649 &json!({"action": "get_count", "selector": ".items"})
650 ),
651 "Executing browser: counting .items..."
652 );
653 }
654
655 #[test]
656 fn test_browser_settings() {
657 assert_eq!(
658 format_activity_message(
659 "browser",
660 &json!({"action": "set_viewport", "width": 1920, "height": 1080})
661 ),
662 "Executing browser: setting viewport to 1920x1080..."
663 );
664 assert_eq!(
665 format_activity_message(
666 "browser",
667 &json!({"action": "set_device", "device": "iPhone 12"})
668 ),
669 "Executing browser: setting device to iPhone 12..."
670 );
671 assert_eq!(
672 format_activity_message(
673 "browser",
674 &json!({"action": "set_geo", "latitude": 37.7749, "longitude": -122.4194})
675 ),
676 "Executing browser: setting geolocation to (37.7749, -122.4194)..."
677 );
678 }
679
680 #[test]
681 fn test_browser_dialog() {
682 assert_eq!(
683 format_activity_message("browser", &json!({"action": "dialog_accept", "text": "OK"})),
684 "Executing browser: accepting dialog with \"OK\"..."
685 );
686 assert_eq!(
687 format_activity_message("browser", &json!({"action": "dialog_dismiss"})),
688 "Executing browser: dismissing dialog..."
689 );
690 }
691
692 #[test]
693 fn test_browser_storage() {
694 assert_eq!(
695 format_activity_message(
696 "browser",
697 &json!({"action": "cookies", "url": "https://example.com"})
698 ),
699 "Executing browser: getting cookies for https://example.com..."
700 );
701 assert_eq!(
702 format_activity_message("browser", &json!({"action": "storage_get", "key": "token"})),
703 "Executing browser: getting storage \"token\"..."
704 );
705 assert_eq!(
706 format_activity_message(
707 "browser",
708 &json!({"action": "storage_set", "key": "session"})
709 ),
710 "Executing browser: setting storage \"session\"..."
711 );
712 }
713
714 #[test]
715 fn test_browser_eval() {
716 assert_eq!(
717 format_activity_message(
718 "browser",
719 &json!({"action": "eval", "script": "return 1 + 1"})
720 ),
721 "Executing browser: evaluating return 1 + 1..."
722 );
723 }
724
725 #[test]
726 fn test_browser_unknown_action() {
727 assert_eq!(
728 format_activity_message("browser", &json!({"action": "unknown_action"})),
729 "Executing browser: unknown_action..."
730 );
731 }
732
733 #[test]
734 fn test_browser_missing_args() {
735 assert_eq!(
736 format_activity_message("browser", &json!({"action": "open"})),
737 "Executing browser: opening URL..."
738 );
739 assert_eq!(
740 format_activity_message("browser", &json!({"action": "click"})),
741 "Executing browser: clicking element..."
742 );
743 assert_eq!(
744 format_activity_message("browser", &json!({"action": "wait"})),
745 "Executing browser: waiting..."
746 );
747 }
748
749 #[test]
750 fn test_browser_navigation_extras() {
751 assert_eq!(
752 format_activity_message("browser", &json!({"action": "snapshot"})),
753 "Executing browser: taking DOM snapshot..."
754 );
755 assert_eq!(
756 format_activity_message("browser", &json!({"action": "forward"})),
757 "Executing browser: navigating forward..."
758 );
759 }
760
761 #[test]
762 fn test_browser_interaction_extras() {
763 assert_eq!(
764 format_activity_message(
765 "browser",
766 &json!({"action": "dblclick", "selector": "#btn"})
767 ),
768 "Executing browser: double-clicking #btn..."
769 );
770 assert_eq!(
771 format_activity_message("browser", &json!({"action": "focus", "selector": "#input"})),
772 "Executing browser: focusing #input..."
773 );
774 assert_eq!(
775 format_activity_message(
776 "browser",
777 &json!({"action": "scrollintoview", "selector": "#footer"})
778 ),
779 "Executing browser: scrolling to #footer..."
780 );
781 assert_eq!(
782 format_activity_message("browser", &json!({"action": "type", "selector": "#search"})),
783 "Executing browser: typing into #search..."
784 );
785 assert_eq!(
786 format_activity_message(
787 "browser",
788 &json!({"action": "select", "selector": "#country", "value": "BR"})
789 ),
790 "Executing browser: selecting \"BR\" in #country..."
791 );
792 assert_eq!(
793 format_activity_message(
794 "browser",
795 &json!({"action": "drag", "source": "#item", "target": "#dropzone"})
796 ),
797 "Executing browser: dragging #item to #dropzone..."
798 );
799 assert_eq!(
800 format_activity_message(
801 "browser",
802 &json!({"action": "upload", "selector": "#file", "path": "/tmp/file.txt"})
803 ),
804 "Executing browser: uploading /tmp/file.txt to #file..."
805 );
806 assert_eq!(
807 format_activity_message(
808 "browser",
809 &json!({"action": "pdf", "path": "/tmp/page.pdf"})
810 ),
811 "Executing browser: saving PDF to /tmp/page.pdf..."
812 );
813 }
814
815 #[test]
816 fn test_browser_query_extras() {
817 assert_eq!(
818 format_activity_message(
819 "browser",
820 &json!({"action": "get_box", "selector": "#modal"})
821 ),
822 "Executing browser: getting bounding box of #modal..."
823 );
824 assert_eq!(
825 format_activity_message(
826 "browser",
827 &json!({"action": "get_styles", "selector": ".button"})
828 ),
829 "Executing browser: getting styles of .button..."
830 );
831 }
832
833 #[test]
834 fn test_browser_wait_extras() {
835 assert_eq!(
836 format_activity_message("browser", &json!({"action": "wait_for_download"})),
837 "Executing browser: waiting for download..."
838 );
839 assert_eq!(
840 format_activity_message("browser", &json!({"action": "wait_for_fn"})),
841 "Executing browser: waiting for function result..."
842 );
843 assert_eq!(
844 format_activity_message(
845 "browser",
846 &json!({"action": "wait_for_state", "state": "visible"})
847 ),
848 "Executing browser: waiting for visible state..."
849 );
850 }
851
852 #[test]
853 fn test_browser_state() {
854 assert_eq!(
855 format_activity_message("browser", &json!({"action": "find", "selector": ".item"})),
856 "Executing browser: finding .item..."
857 );
858 assert_eq!(
859 format_activity_message("browser", &json!({"action": "scroll", "x": 0, "y": 500})),
860 "Executing browser: scrolling to (0, 500)..."
861 );
862 assert_eq!(
863 format_activity_message(
864 "browser",
865 &json!({"action": "is", "selector": "#btn", "state": "visible"})
866 ),
867 "Executing browser: checking if #btn is visible..."
868 );
869 assert_eq!(
870 format_activity_message(
871 "browser",
872 &json!({"action": "download", "url": "https://example.com/file.zip"})
873 ),
874 "Executing browser: downloading https://example.com/file.zip..."
875 );
876 }
877
878 #[test]
879 fn test_browser_storage_network() {
880 assert_eq!(
881 format_activity_message("browser", &json!({"action": "cookies_set"})),
882 "Executing browser: setting cookies..."
883 );
884 assert_eq!(
885 format_activity_message("browser", &json!({"action": "network_requests"})),
886 "Executing browser: getting network requests..."
887 );
888 }
889}