browser_use/tools/
select.rs

1use crate::error::{BrowserError, Result};
2use crate::tools::{Tool, ToolContext, ToolResult};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Parameters for the select tool
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
8pub struct SelectParams {
9    /// Element selector (CSS selector or index)
10    #[serde(flatten)]
11    pub selector: ElementSelector,
12
13    /// Value to select in the dropdown
14    pub value: String,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
18#[serde(untagged)]
19pub enum ElementSelector {
20    /// Select by CSS selector
21    Css {
22        /// CSS selector
23        selector: String,
24    },
25    /// Select by index from DOM tree
26    Index {
27        /// Element index
28        index: usize,
29    },
30}
31
32/// Tool for selecting dropdown options
33#[derive(Default)]
34pub struct SelectTool;
35
36const SELECT_JS: &str = include_str!("select.js");
37
38impl Tool for SelectTool {
39    type Params = SelectParams;
40
41    fn name(&self) -> &str {
42        "select"
43    }
44
45    fn execute_typed(&self, params: SelectParams, context: &mut ToolContext) -> Result<ToolResult> {
46        let css_selector = match params.selector {
47            ElementSelector::Css { selector } => selector,
48            ElementSelector::Index { index } => {
49                let dom = context.get_dom()?;
50                let selector_info = dom.get_selector(index).ok_or_else(|| {
51                    BrowserError::ElementNotFound(format!("No element with index {}", index))
52                })?;
53                selector_info.css_selector.clone()
54            }
55        };
56        let value = params.value;
57
58        let select_config = serde_json::json!({
59            "selector": css_selector,
60            "value": value,
61        });
62        let select_js = SELECT_JS.replace("__SELECT_CONFIG__", &select_config.to_string());
63
64        let result = context
65            .session
66            .tab()
67            .evaluate(&select_js, false)
68            .map_err(|e| BrowserError::ToolExecutionFailed {
69                tool: "select".to_string(),
70                reason: e.to_string(),
71            })?;
72
73        // Parse the JSON string returned by JavaScript
74        let result_json: serde_json::Value = if let Some(serde_json::Value::String(json_str)) =
75            result.value
76        {
77            serde_json::from_str(&json_str)
78                .unwrap_or(serde_json::json!({"success": false, "error": "Failed to parse result"}))
79        } else {
80            result
81                .value
82                .unwrap_or(serde_json::json!({"success": false, "error": "No result returned"}))
83        };
84
85        if result_json["success"].as_bool() == Some(true) {
86            Ok(ToolResult::success_with(serde_json::json!({
87                "selector": css_selector,
88                "value": value,
89                "selectedText": result_json["selectedText"]
90            })))
91        } else {
92            Err(BrowserError::ToolExecutionFailed {
93                tool: "select".to_string(),
94                reason: result_json["error"]
95                    .as_str()
96                    .unwrap_or("Unknown error")
97                    .to_string(),
98            })
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_select_params_css() {
109        let json = serde_json::json!({
110            "selector": "#country-select",
111            "value": "us"
112        });
113
114        let params: SelectParams = serde_json::from_value(json).unwrap();
115        match params.selector {
116            ElementSelector::Css { selector } => assert_eq!(selector, "#country-select"),
117            _ => panic!("Expected CSS selector"),
118        }
119        assert_eq!(params.value, "us");
120    }
121
122    #[test]
123    fn test_select_params_index() {
124        let json = serde_json::json!({
125            "index": 5,
126            "value": "option2"
127        });
128
129        let params: SelectParams = serde_json::from_value(json).unwrap();
130        match params.selector {
131            ElementSelector::Index { index } => assert_eq!(index, 5),
132            _ => panic!("Expected index selector"),
133        }
134        assert_eq!(params.value, "option2");
135    }
136}