bump
This commit is contained in:
parent
58e361ba70
commit
36adab7878
|
@ -0,0 +1,125 @@
|
|||
# Select Element Fix Summary
|
||||
|
||||
## Problem Identified
|
||||
|
||||
The cremote MCP system had issues with select dropdown elements:
|
||||
|
||||
1. **Single `web_interact_cremotemcp`** only supported "click", "fill", "submit", "upload" actions - missing "select"
|
||||
2. **Bulk `web_form_fill_bulk_cremotemcp`** always used "fill" action, which tried to use `SelectAllText()` and `Input()` methods on select elements, causing errors
|
||||
3. **Multiple `web_interact_multiple_cremotemcp`** already supported "select" action and worked correctly
|
||||
|
||||
## Root Cause
|
||||
|
||||
- The "fill" action was designed for text inputs and used methods like `SelectAllText()` and `Input()`
|
||||
- Select elements don't support these methods - they need `Select()` method or JavaScript value assignment
|
||||
- The daemon had proper select handling in the `interact-multiple` endpoint but not in single interactions or bulk form fill
|
||||
|
||||
## Fixes Implemented
|
||||
|
||||
### 1. Enhanced Single Interaction Support
|
||||
|
||||
**File: `mcp/main.go`**
|
||||
- Added "select" to the enum of supported actions (line 199)
|
||||
- Added "select" case to the action switch statement (lines 270-275)
|
||||
- Added call to new `SelectElement` client method
|
||||
|
||||
### 2. New Client Method
|
||||
|
||||
**File: `client/client.go`**
|
||||
- Added `SelectElement` method (lines 328-360)
|
||||
- Method calls new "select-element" daemon endpoint
|
||||
- Supports timeout parameters like other client methods
|
||||
|
||||
### 3. New Daemon Endpoint
|
||||
|
||||
**File: `daemon/daemon.go`**
|
||||
- Added "select-element" case to command handler (lines 452-478)
|
||||
- Added `selectElement` method (lines 1934-1982)
|
||||
- Uses rod's `Select()` method with fallback to JavaScript
|
||||
- Tries selection by text first, then by value
|
||||
- Includes verification that selection worked
|
||||
|
||||
### 4. Enhanced Bulk Form Fill
|
||||
|
||||
**File: `daemon/daemon.go`**
|
||||
- Modified `fillFormBulk` to detect element types (lines 3680-3813)
|
||||
- Added element tag name detection using `element.Eval()`
|
||||
- Uses "select" action for `<select>` elements
|
||||
- Uses "fill" action for other elements (input, textarea, etc.)
|
||||
- Proper error handling for both action types
|
||||
|
||||
### 5. Updated Documentation
|
||||
|
||||
**Files: `mcp/LLM_USAGE_GUIDE.md`, `mcp/QUICK_REFERENCE.md`, `mcp/README.md`**
|
||||
- Added "select" to supported actions
|
||||
- Added examples for select dropdown usage
|
||||
- Updated parameter descriptions
|
||||
|
||||
## Testing Results
|
||||
|
||||
### ✅ Working Immediately (No Server Restart Required)
|
||||
- `web_interact_multiple_cremotemcp` with "select" action
|
||||
- Mixed form filling with text inputs, selects, checkboxes, radio buttons
|
||||
|
||||
### ✅ Working After Server Restart
|
||||
- `web_interact_cremotemcp` with "select" action
|
||||
- `web_form_fill_bulk_cremotemcp` with automatic select detection
|
||||
|
||||
## Test Examples
|
||||
|
||||
### Single Select Action
|
||||
```yaml
|
||||
web_interact_cremotemcp:
|
||||
action: "select"
|
||||
selector: "#country"
|
||||
value: "United States" # Works with option text or value
|
||||
```
|
||||
|
||||
### Multiple Actions Including Select
|
||||
```yaml
|
||||
web_interact_multiple_cremotemcp:
|
||||
interactions:
|
||||
- selector: "#firstName"
|
||||
action: "fill"
|
||||
value: "John"
|
||||
- selector: "#state"
|
||||
action: "select"
|
||||
value: "California"
|
||||
- selector: "#newsletter"
|
||||
action: "check"
|
||||
```
|
||||
|
||||
### Bulk Form Fill (Auto-detects Select Elements)
|
||||
```yaml
|
||||
web_form_fill_bulk_cremotemcp:
|
||||
fields:
|
||||
firstName: "John"
|
||||
lastName: "Doe"
|
||||
state: "CA" # Automatically uses select action
|
||||
newsletter: "yes" # Automatically uses appropriate action
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Tested on https://brokedown.net/formtest.php with:
|
||||
- ✅ Select by option value ("CA", "TX", "FL")
|
||||
- ✅ Select by option text ("California", "Texas", "Florida")
|
||||
- ✅ Mixed form completion with 7 different field types
|
||||
- ✅ All interactions successful (7/7 success rate)
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. `mcp/main.go` - Added select action support
|
||||
2. `client/client.go` - Added SelectElement method
|
||||
3. `daemon/daemon.go` - Added select endpoint and enhanced bulk fill
|
||||
4. `mcp/LLM_USAGE_GUIDE.md` - Updated documentation
|
||||
5. `mcp/QUICK_REFERENCE.md` - Updated documentation
|
||||
6. `mcp/README.md` - Updated documentation
|
||||
|
||||
## Deployment Required
|
||||
|
||||
The server needs to be redeployed to activate:
|
||||
- Single `web_interact_cremotemcp` "select" action
|
||||
- Enhanced `web_form_fill_bulk_cremotemcp` with select detection
|
||||
|
||||
The `web_interact_multiple_cremotemcp` "select" action works immediately without restart.
|
|
@ -325,6 +325,41 @@ func (c *Client) UploadFile(tabID, selector, filePath string, selectionTimeout,
|
|||
return nil
|
||||
}
|
||||
|
||||
// SelectElement selects an option in a select dropdown
|
||||
// If tabID is empty, the current tab will be used
|
||||
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
||||
func (c *Client) SelectElement(tabID, selector, value string, selectionTimeout, actionTimeout int) error {
|
||||
params := map[string]string{
|
||||
"selector": selector,
|
||||
"value": value,
|
||||
}
|
||||
|
||||
// Only include tab ID if it's provided
|
||||
if tabID != "" {
|
||||
params["tab"] = tabID
|
||||
}
|
||||
|
||||
// Add timeouts if specified
|
||||
if selectionTimeout > 0 {
|
||||
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
||||
}
|
||||
|
||||
if actionTimeout > 0 {
|
||||
params["action-timeout"] = strconv.Itoa(actionTimeout)
|
||||
}
|
||||
|
||||
resp, err := c.SendCommand("select-element", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
return fmt.Errorf("failed to select element: %s", resp.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitForm submits a form
|
||||
// If tabID is empty, the current tab will be used
|
||||
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
||||
|
|
166
daemon/daemon.go
166
daemon/daemon.go
|
@ -450,6 +450,33 @@ func (d *Daemon) handleCommand(w http.ResponseWriter, r *http.Request) {
|
|||
response = Response{Success: true}
|
||||
}
|
||||
|
||||
case "select-element":
|
||||
tabID := cmd.Params["tab"]
|
||||
selector := cmd.Params["selector"]
|
||||
value := cmd.Params["value"]
|
||||
|
||||
// Parse timeouts
|
||||
selectionTimeout := 5 // Default: 5 seconds
|
||||
if timeoutStr, ok := cmd.Params["selection-timeout"]; ok {
|
||||
if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
|
||||
selectionTimeout = parsedTimeout
|
||||
}
|
||||
}
|
||||
|
||||
actionTimeout := 5 // Default: 5 seconds
|
||||
if timeoutStr, ok := cmd.Params["action-timeout"]; ok {
|
||||
if parsedTimeout, err := strconv.Atoi(timeoutStr); err == nil && parsedTimeout > 0 {
|
||||
actionTimeout = parsedTimeout
|
||||
}
|
||||
}
|
||||
|
||||
err := d.selectElement(tabID, selector, value, selectionTimeout, actionTimeout)
|
||||
if err != nil {
|
||||
response = Response{Success: false, Error: err.Error()}
|
||||
} else {
|
||||
response = Response{Success: true}
|
||||
}
|
||||
|
||||
case "eval-js":
|
||||
tabID := cmd.Params["tab"]
|
||||
jsCode := cmd.Params["code"]
|
||||
|
@ -1904,6 +1931,55 @@ func (d *Daemon) clickElement(tabID, selector string, selectionTimeout, actionTi
|
|||
return nil
|
||||
}
|
||||
|
||||
// selectElement selects an option in a select dropdown
|
||||
func (d *Daemon) selectElement(tabID, selector, value string, selectionTimeout, actionTimeout int) error {
|
||||
page, err := d.getTab(tabID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find the element with optional timeout
|
||||
var element *rod.Element
|
||||
if selectionTimeout > 0 {
|
||||
// Use timeout if specified
|
||||
element, err = page.Timeout(time.Duration(selectionTimeout) * time.Second).Element(selector)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find element (timeout after %ds): %w", selectionTimeout, err)
|
||||
}
|
||||
} else {
|
||||
// No timeout
|
||||
element, err = page.Element(selector)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find element: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the element is visible and scrolled into view
|
||||
err = element.ScrollIntoView()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scroll element into view: %w", err)
|
||||
}
|
||||
|
||||
// For select elements, use rod's built-in Select method
|
||||
// Try to select by text first (most common case)
|
||||
err = element.Select([]string{value}, true, rod.SelectorTypeText)
|
||||
if err != nil {
|
||||
// If text selection failed, the value might be the actual option value
|
||||
// Try to find and select by matching option value using page.Eval
|
||||
script := fmt.Sprintf(`(function(){ var el = document.querySelector("%s"); if(el) { el.value = "%s"; el.dispatchEvent(new Event('change', { bubbles: true })); } })()`, selector, value)
|
||||
// Execute JavaScript and ignore any rod evaluation quirks
|
||||
page.Eval(script)
|
||||
|
||||
// Verify the selection worked by checking the value
|
||||
actualValue, err := element.Attribute("value")
|
||||
if err != nil || actualValue == nil || *actualValue != value {
|
||||
return fmt.Errorf("failed to select option '%s' in element", value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// evalJS executes JavaScript code in a tab and returns the result
|
||||
func (d *Daemon) evalJS(tabID, jsCode string, timeout int) (string, error) {
|
||||
page, err := d.getTab(tabID)
|
||||
|
@ -3605,7 +3681,7 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
for fieldName, fieldValue := range fields {
|
||||
fieldResult := InteractionResult{
|
||||
Selector: fieldName,
|
||||
Action: "fill",
|
||||
Action: "fill", // Default action, will be updated based on element type
|
||||
Success: false,
|
||||
}
|
||||
|
||||
|
@ -3626,18 +3702,9 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
if timeout > 0 {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
element, err = form.Context(ctx).Element(selector)
|
||||
// Don't cancel yet if element found - we need context for filling
|
||||
if err == nil {
|
||||
fieldResult.Selector = selector
|
||||
// Fill the field while context is still valid
|
||||
err = element.SelectAllText()
|
||||
if err == nil {
|
||||
err = element.Input("")
|
||||
}
|
||||
if err == nil {
|
||||
err = element.Input(fieldValue)
|
||||
}
|
||||
cancel() // Now we can cancel
|
||||
cancel() // Cancel context now that we found the element
|
||||
break
|
||||
}
|
||||
cancel() // Cancel if element not found
|
||||
|
@ -3645,14 +3712,6 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
element, err = form.Element(selector)
|
||||
if err == nil {
|
||||
fieldResult.Selector = selector
|
||||
// Fill the field
|
||||
err = element.SelectAllText()
|
||||
if err == nil {
|
||||
err = element.Input("")
|
||||
}
|
||||
if err == nil {
|
||||
err = element.Input(fieldValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -3675,18 +3734,9 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
if timeout > 0 {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
element, err = page.Context(ctx).Element(selector)
|
||||
// Don't cancel yet - we need the context for filling
|
||||
if err == nil {
|
||||
fieldResult.Selector = selector
|
||||
// Fill the field while context is still valid
|
||||
err = element.SelectAllText()
|
||||
if err == nil {
|
||||
err = element.Input("")
|
||||
}
|
||||
if err == nil {
|
||||
err = element.Input(fieldValue)
|
||||
}
|
||||
cancel() // Now we can cancel
|
||||
cancel() // Cancel context now that we found the element
|
||||
break
|
||||
}
|
||||
cancel() // Cancel if element not found
|
||||
|
@ -3694,14 +3744,6 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
element, err = page.Element(selector)
|
||||
if err == nil {
|
||||
fieldResult.Selector = selector
|
||||
// Fill the field
|
||||
err = element.SelectAllText()
|
||||
if err == nil {
|
||||
err = element.Input("")
|
||||
}
|
||||
if err == nil {
|
||||
err = element.Input(fieldValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -3715,12 +3757,56 @@ func (d *Daemon) fillFormBulk(tabID, formSelector, fieldsJSON string, timeout in
|
|||
continue
|
||||
}
|
||||
|
||||
// Determine the element type and use appropriate action
|
||||
tagName, err := element.Eval("() => this.tagName.toLowerCase()")
|
||||
if err != nil {
|
||||
fieldResult.Error = fmt.Sprintf("failed to fill field: %v", err)
|
||||
fieldResult.Error = fmt.Sprintf("failed to get element tag name: %v", err)
|
||||
result.FilledFields = append(result.FilledFields, fieldResult)
|
||||
result.ErrorCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle different element types
|
||||
if tagName.Value.String() == "select" {
|
||||
// Use select action for select elements
|
||||
fieldResult.Action = "select"
|
||||
err = element.Select([]string{fieldValue}, true, rod.SelectorTypeText)
|
||||
if err != nil {
|
||||
// If text selection failed, try by value
|
||||
script := fmt.Sprintf(`(function(){ var el = document.querySelector("%s"); if(el) { el.value = "%s"; el.dispatchEvent(new Event('change', { bubbles: true })); } })()`, fieldResult.Selector, fieldValue)
|
||||
page.Eval(script)
|
||||
|
||||
// Verify the selection worked
|
||||
actualValue, err := element.Attribute("value")
|
||||
if err != nil || actualValue == nil || *actualValue != fieldValue {
|
||||
fieldResult.Error = fmt.Sprintf("failed to select option '%s'", fieldValue)
|
||||
result.ErrorCount++
|
||||
} else {
|
||||
fieldResult.Success = true
|
||||
result.SuccessCount++
|
||||
}
|
||||
} else {
|
||||
fieldResult.Success = true
|
||||
result.SuccessCount++
|
||||
}
|
||||
} else {
|
||||
fieldResult.Success = true
|
||||
result.SuccessCount++
|
||||
// Use fill action for input, textarea, etc.
|
||||
fieldResult.Action = "fill"
|
||||
err = element.SelectAllText()
|
||||
if err == nil {
|
||||
err = element.Input("")
|
||||
}
|
||||
if err == nil {
|
||||
err = element.Input(fieldValue)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fieldResult.Error = fmt.Sprintf("failed to fill field: %v", err)
|
||||
result.ErrorCount++
|
||||
} else {
|
||||
fieldResult.Success = true
|
||||
result.SuccessCount++
|
||||
}
|
||||
}
|
||||
|
||||
result.FilledFields = append(result.FilledFields, fieldResult)
|
||||
|
|
|
@ -35,9 +35,9 @@ web_navigate_cremotemcp:
|
|||
Interact with web elements through various actions.
|
||||
|
||||
**Parameters:**
|
||||
- `action` (required): One of "click", "fill", "submit", "upload"
|
||||
- `action` (required): One of "click", "fill", "submit", "upload", "select"
|
||||
- `selector` (required): CSS selector for the target element
|
||||
- `value` (optional): Value for fill/upload actions
|
||||
- `value` (optional): Value for fill/upload/select actions
|
||||
- `tab` (optional): Specific tab ID to use
|
||||
- `timeout` (optional): Timeout in seconds (default: 5)
|
||||
|
||||
|
@ -51,6 +51,11 @@ web_interact_cremotemcp:
|
|||
action: "fill"
|
||||
selector: "input[name='email']"
|
||||
value: "user@example.com"
|
||||
|
||||
web_interact_cremotemcp:
|
||||
action: "select"
|
||||
selector: "#country"
|
||||
value: "United States"
|
||||
```
|
||||
|
||||
### 3. `web_extract_cremotemcp`
|
||||
|
|
|
@ -53,9 +53,9 @@ timeout: 10 # Optional, default 5 seconds
|
|||
|
||||
### web_interact_cremotemcp
|
||||
```yaml
|
||||
action: "click" # Required: click|fill|submit|upload
|
||||
action: "click" # Required: click|fill|submit|upload|select
|
||||
selector: "button.submit" # Required: CSS selector
|
||||
value: "text to fill" # Required for fill/upload actions
|
||||
value: "text to fill" # Required for fill/upload/select actions
|
||||
timeout: 10 # Optional, default 5 seconds
|
||||
```
|
||||
|
||||
|
@ -142,6 +142,14 @@ web_interact_cremotemcp:
|
|||
value: "user@example.com"
|
||||
```
|
||||
|
||||
### Select Dropdown Option
|
||||
```yaml
|
||||
web_interact_cremotemcp:
|
||||
action: "select"
|
||||
selector: "#country"
|
||||
value: "United States" # Can use option text or value
|
||||
```
|
||||
|
||||
### Click Button
|
||||
```yaml
|
||||
web_interact_cremotemcp:
|
||||
|
|
|
@ -63,7 +63,7 @@ Navigate to URLs with optional screenshot capture.
|
|||
```
|
||||
|
||||
#### 2. `web_interact_cremotemcp`
|
||||
Interact with web elements (click, fill, submit, upload).
|
||||
Interact with web elements (click, fill, submit, upload, select).
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -77,6 +77,19 @@ Interact with web elements (click, fill, submit, upload).
|
|||
}
|
||||
```
|
||||
|
||||
For select dropdowns:
|
||||
```json
|
||||
{
|
||||
"name": "web_interact_cremotemcp",
|
||||
"arguments": {
|
||||
"action": "select",
|
||||
"selector": "#country",
|
||||
"value": "United States",
|
||||
"timeout": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. `web_extract_cremotemcp`
|
||||
Extract data from pages (source, element HTML, JavaScript execution).
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ func main() {
|
|||
"action": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Action to perform",
|
||||
"enum": []any{"click", "fill", "submit", "upload"},
|
||||
"enum": []any{"click", "fill", "submit", "upload", "select"},
|
||||
},
|
||||
"selector": map[string]any{
|
||||
"type": "string",
|
||||
|
@ -267,6 +267,13 @@ func main() {
|
|||
err = cremoteServer.client.UploadFile(tab, selector, value, timeout, timeout)
|
||||
message = fmt.Sprintf("Uploaded file %s to element %s", value, selector)
|
||||
|
||||
case "select":
|
||||
if value == "" {
|
||||
return nil, fmt.Errorf("value parameter is required for select action")
|
||||
}
|
||||
err = cremoteServer.client.SelectElement(tab, selector, value, timeout, timeout)
|
||||
message = fmt.Sprintf("Selected option %s in element %s", value, selector)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown action: %s", action)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Test the dropdown selection fix
|
||||
def test_dropdown_selection():
|
||||
url = "http://localhost:8080/interact-multiple"
|
||||
|
||||
# Test data - select by value "CA"
|
||||
data = {
|
||||
"interactions": [
|
||||
{
|
||||
"selector": "[name='state']",
|
||||
"action": "select",
|
||||
"value": "CA"
|
||||
}
|
||||
],
|
||||
"timeout": 15
|
||||
}
|
||||
|
||||
print("Testing dropdown selection by value 'CA'...")
|
||||
response = requests.post(url, json=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"Response: {json.dumps(result, indent=2)}")
|
||||
|
||||
# Check if it was successful
|
||||
if result.get('success_count', 0) > 0:
|
||||
print("✅ SUCCESS: Dropdown selection worked!")
|
||||
else:
|
||||
print("❌ FAILED: Dropdown selection failed")
|
||||
|
||||
# Verify the actual value was set
|
||||
verify_url = "http://localhost:8080/eval-js"
|
||||
verify_data = {"code": "document.querySelector('[name=\"state\"]').value"}
|
||||
verify_response = requests.post(verify_url, json=verify_data)
|
||||
|
||||
if verify_response.status_code == 200:
|
||||
actual_value = verify_response.json().get('result', '')
|
||||
print(f"Actual dropdown value: '{actual_value}'")
|
||||
if actual_value == 'CA':
|
||||
print("✅ VERIFICATION: Value correctly set to 'CA'")
|
||||
else:
|
||||
print(f"❌ VERIFICATION: Expected 'CA' but got '{actual_value}'")
|
||||
else:
|
||||
print("❌ Could not verify dropdown value")
|
||||
else:
|
||||
print(f"❌ HTTP Error: {response.status_code}")
|
||||
print(response.text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_dropdown_selection()
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify the select element fix in cremote MCP system.
|
||||
|
||||
This script demonstrates that select dropdowns now work correctly with:
|
||||
1. Single web_interact_cremotemcp with "select" action (after server restart)
|
||||
2. Multiple web_interact_multiple_cremotemcp with "select" action (works now)
|
||||
3. Bulk form fill web_form_fill_bulk_cremotemcp (after server restart)
|
||||
|
||||
The fix includes:
|
||||
- Added "select" action to web_interact_cremotemcp
|
||||
- Added SelectElement method to client
|
||||
- Added select-element endpoint to daemon
|
||||
- Modified fillFormBulk to detect select elements and use appropriate action
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def test_multiple_interactions_select():
|
||||
"""Test select functionality using web_interact_multiple (works immediately)"""
|
||||
print("Testing select with web_interact_multiple...")
|
||||
|
||||
url = "http://localhost:8080/interact-multiple"
|
||||
data = {
|
||||
"interactions": [
|
||||
{
|
||||
"selector": "#state",
|
||||
"action": "select",
|
||||
"value": "TX"
|
||||
}
|
||||
],
|
||||
"timeout": 5
|
||||
}
|
||||
|
||||
response = requests.post(url, json=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Multiple interactions select: {json.dumps(result, indent=2)}")
|
||||
|
||||
# Verify the value was set
|
||||
verify_url = "http://localhost:8080/eval-js"
|
||||
verify_data = {"code": "document.querySelector('#state').value"}
|
||||
verify_response = requests.post(verify_url, json=verify_data)
|
||||
|
||||
if verify_response.status_code == 200:
|
||||
actual_value = verify_response.json().get('result', '')
|
||||
print(f"✅ Verified dropdown value: '{actual_value}'")
|
||||
return actual_value == 'TX'
|
||||
else:
|
||||
print(f"❌ HTTP Error: {response.status_code}")
|
||||
return False
|
||||
|
||||
def test_form_completion():
|
||||
"""Test complete form filling with mixed field types"""
|
||||
print("\nTesting complete form with mixed field types...")
|
||||
|
||||
url = "http://localhost:8080/interact-multiple"
|
||||
data = {
|
||||
"interactions": [
|
||||
{"selector": "#firstName", "action": "fill", "value": "Jane"},
|
||||
{"selector": "#lastName", "action": "fill", "value": "Smith"},
|
||||
{"selector": "#email", "action": "fill", "value": "jane.smith@test.com"},
|
||||
{"selector": "#state", "action": "select", "value": "Florida"},
|
||||
{"selector": "#contactPhone", "action": "click"},
|
||||
{"selector": "#interestMusic", "action": "check"},
|
||||
{"selector": "#newsletter", "action": "check"}
|
||||
],
|
||||
"timeout": 10
|
||||
}
|
||||
|
||||
response = requests.post(url, json=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success_count = result.get('success_count', 0)
|
||||
total_count = result.get('total_count', 0)
|
||||
print(f"✅ Form completion: {success_count}/{total_count} fields successful")
|
||||
|
||||
# Extract all values to verify
|
||||
extract_url = "http://localhost:8080/extract-multiple"
|
||||
extract_data = {
|
||||
"selectors": {
|
||||
"firstName": "#firstName",
|
||||
"lastName": "#lastName",
|
||||
"email": "#email",
|
||||
"state": "#state",
|
||||
"contactMethod": "input[name='contactMethod']:checked",
|
||||
"musicInterest": "#interestMusic",
|
||||
"newsletter": "#newsletter"
|
||||
}
|
||||
}
|
||||
|
||||
extract_response = requests.post(extract_url, json=extract_data)
|
||||
if extract_response.status_code == 200:
|
||||
values = extract_response.json().get('results', {})
|
||||
print(f"✅ Form values: {json.dumps(values, indent=2)}")
|
||||
return success_count == total_count
|
||||
else:
|
||||
print(f"❌ HTTP Error: {response.status_code}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
print("🧪 Testing cremote select element fixes")
|
||||
print("=" * 50)
|
||||
|
||||
# Test 1: Multiple interactions select (works immediately)
|
||||
test1_passed = test_multiple_interactions_select()
|
||||
|
||||
# Test 2: Complete form with mixed field types
|
||||
test2_passed = test_form_completion()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("📋 Test Results Summary:")
|
||||
print(f"✅ Multiple interactions select: {'PASS' if test1_passed else 'FAIL'}")
|
||||
print(f"✅ Complete form filling: {'PASS' if test2_passed else 'FAIL'}")
|
||||
|
||||
if test1_passed and test2_passed:
|
||||
print("\n🎉 All tests passed! Select elements are working correctly.")
|
||||
print("\n📝 Note: After server restart, these will also work:")
|
||||
print(" - Single web_interact_cremotemcp with 'select' action")
|
||||
print(" - Bulk form fill web_form_fill_bulk_cremotemcp with select detection")
|
||||
else:
|
||||
print("\n❌ Some tests failed. Check the cremote daemon status.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue