Merge pull request #439 from Br1an67/feat/issue-108-web-form-handling

feat: add support for select, textarea, and file upload in web forms
This commit is contained in:
Martin
2026-03-02 19:45:10 +01:00
committed by GitHub
2 changed files with 65 additions and 3 deletions
+34 -3
View File
@@ -2,7 +2,7 @@ from selenium import webdriver
from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.action_chains import ActionChains
@@ -530,7 +530,16 @@ class Browser:
if input_type in ["hidden", "submit", "button", "image"] or not element["displayed"]: if input_type in ["hidden", "submit", "button", "image"] or not element["displayed"]:
continue continue
input_name = element.get("text") or element.get("id") or input_type input_name = element.get("text") or element.get("id") or input_type
if input_type == "checkbox" or input_type == "radio": if input_type == "select":
options = element.get("options", [])
options_str = ", ".join(opt["text"] for opt in options if opt["text"])
selected = next((opt["text"] for opt in options if opt.get("selected")), "")
form_strings.append(f"[{input_name}](select: {selected}) options: [{options_str}]")
elif input_type == "textarea":
form_strings.append(f"[{input_name}]("")")
elif input_type == "file":
form_strings.append(f"[{input_name}](file: )")
elif input_type == "checkbox" or input_type == "radio":
try: try:
checked_status = "checked" if element.is_selected() else "unchecked" checked_status = "checked" if element.is_selected() else "unchecked"
except Exception as e: except Exception as e:
@@ -694,7 +703,29 @@ class Browser:
self.logger.warning(f"Element '{name}' is not interactable (not displayed or disabled)") self.logger.warning(f"Element '{name}' is not interactable (not displayed or disabled)")
continue continue
input_type = (element.get_attribute("type") or "text").lower() input_type = (element.get_attribute("type") or "text").lower()
if input_type in ["checkbox", "radio"]: tag_name = (element.tag_name or "").lower()
if tag_name == "select":
try:
select = Select(element)
select.select_by_visible_text(value)
self.logger.info(f"Selected '{value}' for {name}")
except Exception:
try:
select.select_by_value(value)
self.logger.info(f"Selected value '{value}' for {name}")
except Exception as sel_e:
self.logger.warning(f"Could not select '{value}' for {name}: {sel_e}")
elif tag_name == "textarea":
element.clear()
element.send_keys(value)
self.logger.info(f"Filled textarea {name}")
elif input_type == "file":
if os.path.isabs(value) and os.path.exists(value):
element.send_keys(value)
self.logger.info(f"Uploaded file '{value}' for {name}")
else:
self.logger.warning(f"File not found: {value}")
elif input_type in ["checkbox", "radio"]:
is_checked = element.is_selected() is_checked = element.is_selected()
should_be_checked = value.lower() == "checked" should_be_checked = value.lower() == "checked"
+31
View File
@@ -11,6 +11,37 @@ function findInputs(element, result = []) {
displayed: isElementDisplayed(input) displayed: isElementDisplayed(input)
}); });
}); });
// Find all <select> elements (dropdowns / multi-choice)
const selects = element.querySelectorAll('select');
selects.forEach(select => {
const options = Array.from(select.options).map(opt => ({
value: opt.value,
text: opt.textContent.trim(),
selected: opt.selected
}));
result.push({
tagName: select.tagName,
text: select.name || '',
type: 'select',
class: select.className || '',
xpath: getXPath(select),
displayed: isElementDisplayed(select),
multiple: select.multiple,
options: options
});
});
// Find all <textarea> elements
const textareas = element.querySelectorAll('textarea');
textareas.forEach(textarea => {
result.push({
tagName: textarea.tagName,
text: textarea.name || '',
type: 'textarea',
class: textarea.className || '',
xpath: getXPath(textarea),
displayed: isElementDisplayed(textarea)
});
});
const allElements = element.querySelectorAll('*'); const allElements = element.querySelectorAll('*');
allElements.forEach(el => { allElements.forEach(el => {
if (el.shadowRoot) { if (el.shadowRoot) {