I can bind select
This commit is contained in:
@@ -207,7 +207,7 @@ class TestableElement:
|
||||
# Check for explicit association via 'for' attribute
|
||||
label_for = label.get('for')
|
||||
if label_for:
|
||||
input_field = self.element.find('input', id=label_for)
|
||||
input_field = self.element.find(id=label_for)
|
||||
if input_field:
|
||||
input_name = self._get_input_identifier(input_field, unnamed_counter)
|
||||
if input_name.startswith('unnamed_'):
|
||||
@@ -348,6 +348,14 @@ class TestableElement:
|
||||
self.fields[name] = selected_value
|
||||
elif options:
|
||||
self.fields[name] = options[0]['value']
|
||||
|
||||
# Process textarea fields
|
||||
for textarea_field in self.element.find_all('textarea'):
|
||||
name = textarea_field.get('name')
|
||||
if not name:
|
||||
continue
|
||||
|
||||
self.fields[name] = textarea_field.get_text(strip=True)
|
||||
|
||||
@staticmethod
|
||||
def _get_input_identifier(input_field, counter):
|
||||
@@ -602,201 +610,12 @@ class TestableForm(TestableElement):
|
||||
headers=headers,
|
||||
data=self.fields
|
||||
)
|
||||
|
||||
# def _translate(self, field):
|
||||
# """
|
||||
# Translate a given field using a predefined mapping. If the field is not found
|
||||
# in the mapping, the original field is returned unmodified.
|
||||
#
|
||||
# :param field: The field name to be translated.
|
||||
# :type field: str
|
||||
# :return: The translated field name if present in the mapping, or the original
|
||||
# field name if no mapping exists for it.
|
||||
# :rtype: str
|
||||
# """
|
||||
# return self.fields_mapping.get(field, field)
|
||||
#
|
||||
# def _update_fields_mapping(self):
|
||||
# """
|
||||
# Build a mapping between label text and input field names.
|
||||
#
|
||||
# This method finds all labels in the form and associates them with their
|
||||
# corresponding input fields using the following priority order:
|
||||
# 1. Explicit association via 'for' attribute matching input 'id'
|
||||
# 2. Implicit association (label contains the input)
|
||||
# 3. Parent-level association with 'for'/'id'
|
||||
# 4. Proximity association (siblings in same parent)
|
||||
# 5. No label (use input name as key)
|
||||
#
|
||||
# The mapping is stored in self.fields_mapping as {label_text: input_name}.
|
||||
# For inputs without a name, the id is used. If neither exists, a generic
|
||||
# key like "unnamed_0" is generated.
|
||||
# """
|
||||
# self.fields_mapping = {}
|
||||
# processed_inputs = set()
|
||||
# unnamed_counter = 0
|
||||
#
|
||||
# # Get all inputs in the form
|
||||
# all_inputs = self.form.find_all('input')
|
||||
#
|
||||
# # Priority 1 & 2: Explicit association (for/id) and implicit (nested)
|
||||
# for label in self.form.find_all('label'):
|
||||
# label_text = label.get_text(strip=True)
|
||||
#
|
||||
# # Check for explicit association via 'for' attribute
|
||||
# label_for = label.get('for')
|
||||
# if label_for:
|
||||
# input_field = self.form.find('input', id=label_for)
|
||||
# if input_field:
|
||||
# input_name = self._get_input_identifier(input_field, unnamed_counter)
|
||||
# if input_name.startswith('unnamed_'):
|
||||
# unnamed_counter += 1
|
||||
# self.fields_mapping[label_text] = input_name
|
||||
# processed_inputs.add(id(input_field))
|
||||
# continue
|
||||
#
|
||||
# # Check for implicit association (label contains input)
|
||||
# input_field = label.find('input')
|
||||
# if input_field:
|
||||
# input_name = self._get_input_identifier(input_field, unnamed_counter)
|
||||
# if input_name.startswith('unnamed_'):
|
||||
# unnamed_counter += 1
|
||||
# self.fields_mapping[label_text] = input_name
|
||||
# processed_inputs.add(id(input_field))
|
||||
# continue
|
||||
#
|
||||
# # Priority 3 & 4: Parent-level associations
|
||||
# for label in self.form.find_all('label'):
|
||||
# label_text = label.get_text(strip=True)
|
||||
#
|
||||
# # Skip if this label was already processed
|
||||
# if label_text in self.fields_mapping:
|
||||
# continue
|
||||
#
|
||||
# parent = label.parent
|
||||
# if parent:
|
||||
# input_found = False
|
||||
#
|
||||
# # Priority 3: Look for sibling input with matching for/id
|
||||
# label_for = label.get('for')
|
||||
# if label_for:
|
||||
# for sibling in parent.find_all('input'):
|
||||
# if sibling.get('id') == label_for and id(sibling) not in processed_inputs:
|
||||
# input_name = self._get_input_identifier(sibling, unnamed_counter)
|
||||
# if input_name.startswith('unnamed_'):
|
||||
# unnamed_counter += 1
|
||||
# self.fields_mapping[label_text] = input_name
|
||||
# processed_inputs.add(id(sibling))
|
||||
# input_found = True
|
||||
# break
|
||||
#
|
||||
# # Priority 4: Fallback to proximity if no input found yet
|
||||
# if not input_found:
|
||||
# for sibling in parent.find_all('input'):
|
||||
# if id(sibling) not in processed_inputs:
|
||||
# input_name = self._get_input_identifier(sibling, unnamed_counter)
|
||||
# if input_name.startswith('unnamed_'):
|
||||
# unnamed_counter += 1
|
||||
# self.fields_mapping[label_text] = input_name
|
||||
# processed_inputs.add(id(sibling))
|
||||
# break
|
||||
#
|
||||
# # Priority 5: Inputs without labels
|
||||
# for input_field in all_inputs:
|
||||
# if id(input_field) not in processed_inputs:
|
||||
# input_name = self._get_input_identifier(input_field, unnamed_counter)
|
||||
# if input_name.startswith('unnamed_'):
|
||||
# unnamed_counter += 1
|
||||
# self.fields_mapping[input_name] = input_name
|
||||
#
|
||||
# @staticmethod
|
||||
# def _get_input_identifier(input_field, counter):
|
||||
# """
|
||||
# Get the identifier for an input field.
|
||||
#
|
||||
# Args:
|
||||
# input_field: The BeautifulSoup Tag object representing the input.
|
||||
# counter: Current counter for unnamed inputs.
|
||||
#
|
||||
# Returns:
|
||||
# The input name, id, or a generated "unnamed_X" identifier.
|
||||
# """
|
||||
# if input_field.get('name'):
|
||||
# return input_field['name']
|
||||
# elif input_field.get('id'):
|
||||
# return input_field['id']
|
||||
# else:
|
||||
# return f"unnamed_{counter}"
|
||||
#
|
||||
# @staticmethod
|
||||
# def _convert_number(value):
|
||||
# """
|
||||
# Convert a string value to int or float.
|
||||
#
|
||||
# Args:
|
||||
# value: String value to convert.
|
||||
#
|
||||
# Returns:
|
||||
# int, float, or empty string if conversion fails.
|
||||
# """
|
||||
# if not value or value.strip() == '':
|
||||
# return ''
|
||||
#
|
||||
# try:
|
||||
# # Try float first to detect decimal numbers
|
||||
# if '.' in value or 'e' in value.lower():
|
||||
# return float(value)
|
||||
# else:
|
||||
# return int(value)
|
||||
# except ValueError:
|
||||
# return value
|
||||
#
|
||||
# @staticmethod
|
||||
# def _convert_value(value):
|
||||
# """
|
||||
# Analyze and convert a value to its appropriate type.
|
||||
#
|
||||
# Conversion priority:
|
||||
# 1. Boolean keywords (true/false)
|
||||
# 2. Float (contains decimal point)
|
||||
# 3. Int (numeric)
|
||||
# 4. Empty string
|
||||
# 5. String (default)
|
||||
#
|
||||
# Args:
|
||||
# value: String value to convert.
|
||||
#
|
||||
# Returns:
|
||||
# Converted value with appropriate type (bool, float, int, or str).
|
||||
# """
|
||||
# if not value or value.strip() == '':
|
||||
# return ''
|
||||
#
|
||||
# value_lower = value.lower().strip()
|
||||
#
|
||||
# # Check for boolean
|
||||
# if value_lower in ('true', 'false'):
|
||||
# return value_lower == 'true'
|
||||
#
|
||||
# # Check for numeric values
|
||||
# try:
|
||||
# # Check for float (has decimal point or scientific notation)
|
||||
# if '.' in value or 'e' in value_lower:
|
||||
# return float(value)
|
||||
# # Try int
|
||||
# else:
|
||||
# return int(value)
|
||||
# except ValueError:
|
||||
# pass
|
||||
#
|
||||
# # Default to string
|
||||
# return value
|
||||
|
||||
|
||||
class TestableControl(TestableElement):
|
||||
def __init__(self, client, source, tag):
|
||||
super().__init__(client, source, "input")
|
||||
assert len(self.fields) <= 1
|
||||
super().__init__(client, source, tag)
|
||||
assert len(self.fields) == 1
|
||||
self._input_name = next(iter(self.fields))
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user