Module pyseext.field_helper
Module that contains our FieldHelper class.
Expand source code
"""
Module that contains our FieldHelper class.
"""
import logging
from typing import Union, Any
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from pyseext.component_query import ComponentQuery
from pyseext.has_referenced_javascript import HasReferencedJavaScript
from pyseext.core import Core
from pyseext.input_helper import InputHelper
from pyseext.store_helper import StoreHelper
class FieldHelper(HasReferencedJavaScript):
"""A class to help with interacting with Ext fields"""
# Class variables
_FIND_FIELD_INPUT_ELEMENT_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.findFieldInputElement('{form_cq}', '{name}')"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.findFieldInputElement
Requires the inserts: {form_cq}, {name}"""
_GET_FIELD_XTYPE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldXType('{form_cq}', '{name}')"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldXType
Requires the inserts: {form_cq}, {name}"""
_GET_FIELD_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldValue('{form_cq}', '{name}')"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldValue
Requires the inserts: {form_cq}, {name}"""
_GET_FIELD_DISPLAY_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldDisplayValue('{form_cq}', '{name}')"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldDisplayValue
Requires the inserts: {form_cq}, {name}"""
_SET_FIELD_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.setFieldValue('{form_cq}', '{name}', {value})"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.setFieldValue
Requires the inserts: {form_cq}, {name}, {value}"""
_IS_REMOTELY_FILTERED_COMBOBOX_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.isRemotelyFilteredComboBox('{form_cq}', '{name}')"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.isRemotelyFilteredComboBox
Requires the inserts: {form_cq}, {name}"""
_FOCUS_FIELD_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.focusField('{form_cq}', {index_or_name})"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.focusField
Requires the inserts: {form_cq}, {index_or_name}"""
_DOES_FIELD_HAVE_FOCUS_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.doesFieldHaveFocus('{form_cq}', {index_or_name})"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.doesFieldHaveFocus
Requires the inserts: {form_cq}, {index_or_name}"""
_SELECT_COMBOBOX_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.selectComboBoxValue('{form_cq}', '{name}', {data})"
"""The script template to use to call the JavaScript method PySeExt.FieldHelper.selectComboBoxValue
Requires the inserts: {form_cq}, {name}, {data}"""
def __init__(self, driver: WebDriver):
"""Initialises an instance of this class
Args:
driver (WebDriver): The webdriver to use
"""
self._logger = logging.getLogger(__name__)
"""The Logger instance for this class instance"""
self._driver = driver
"""The WebDriver instance for this class instance"""
self._action_chains = ActionChains(driver)
"""The ActionChains instance for this class instance"""
self._core = Core(driver)
"""The `Core` instance for this class instance"""
self._input_helper = InputHelper(driver)
"""The `InputHelper` instance for this class instance"""
self._store_helper = StoreHelper(driver)
"""The `StoreHelper` instance for this class instance"""
self._cq = ComponentQuery(driver)
"""The `ComponentQuery` instance for this class instance"""
# Initialise our base class
super().__init__(driver, self._logger)
def find_field_input_element(self, form_cq: str, name: str) -> WebElement:
"""Attempts to get a field by name from the specified form panel
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
WebElement: The field's input element DOM element, or None if not found.
"""
field_cq = self.get_field_component_query(form_cq, name)
# Append enabled test
field_cq = field_cq + '{isDisabled()===false}'
# And wait until available
self._cq.wait_for_single_query(field_cq)
# Now get its input element
script = self._FIND_FIELD_INPUT_ELEMENT_TEMPLATE.format(form_cq=form_cq, name=name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def get_field_xtype(self, form_cq: str, name: str) -> str:
"""Attempts to get the xtype of a field by name from the specified form panel
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
str: The xtype of the field, or None if not found.
"""
script = self._GET_FIELD_XTYPE_TEMPLATE.format(form_cq=form_cq, name=name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def is_field_a_combobox(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a combobox (or a subclass of it).
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a combobox, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.ComboBox', self.get_field_component_query(form_cq, name))
def is_field_a_text_field(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a text field (or a subclass of it).
Note that text areas, number fields, and date fields all inherit from text field.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a text field, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.Text', self.get_field_component_query(form_cq, name))
def is_field_a_number_field(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a number field (or a subclass of it).
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a number field, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.Number', self.get_field_component_query(form_cq, name))
def is_field_a_date_field(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a date field (or a subclass of it).
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a date field, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.Date', self.get_field_component_query(form_cq, name))
def is_field_a_checkbox(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a checkbox (or a subclass of it).
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a checkbox, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.Checkbox', self.get_field_component_query(form_cq, name))
def is_field_a_radio_field(self, form_cq: str, name: str) -> bool:
"""Determine whether the field (by name) on the specified form panel is a radio field (or a subclass of it).
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field is a radio field, False otherwise.
"""
return self._cq.is_component_instance_of_class('Ext.form.field.Radio', self.get_field_component_query(form_cq, name))
def get_field_value(self, form_cq: str, name: str) -> Any:
"""Attempts to get the value of a field by name from the specified form panel
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
Any: The value of the field, or None if not found.
"""
script = self._GET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def get_field_display_value(self, form_cq: str, name: str) -> Any:
"""Attempts to get the display value of a field by name from the specified form panel.
Supported by comboboxes and display fields.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
Any: The display value of the field, or None if not found or if the field does not have a display value.
"""
script = self._GET_FIELD_DISPLAY_VALUE_TEMPLATE.format(form_cq=form_cq, name=name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def set_field_value(self, form_cq: str, name: str, value: Union[dict, float, str]):
"""Sets the value for a field.
If the field can be typed into by a user, and the value is typeable, then that is the approach taken.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field.
name (str): The name of the field.
value (Union[dict, float, str]): The value for the field.
If the value is a number or string then it is typed into the field where possible,
otherwise the value is set directly.
If the value is a dictionary then either it, or it's value member is taken to be model
data to select in a combobox.
If the combobox is remotely filtered then it is expected that both a value member and
a filterText member is specified, where the filterText is typed into the combobox, then
the value selected.
If the value is supplied as a dictionary and the field is not a store holder then
an exception is thrown.
"""
# Only used for radiogroup controls now, but xtype still useful to throw a better error if field not found.
field_xtype = self.get_field_xtype(form_cq, name)
if field_xtype:
# Field found!
field_value = self._core.try_get_object_member(value, 'value', value)
# Now need to set it's value
if self.is_field_a_combobox(form_cq, name):
is_combo_remote = self._is_field_remotely_filtered_combobox(form_cq, name)
is_value_a_dict = isinstance(field_value, dict)
if is_combo_remote:
if not is_value_a_dict:
# We want to type into the combobox, to filter it, and wait for it to load.
# Once loaded we expect to have a single value that will end up selected.
# Reset the store load count
combobox_cq = self.get_field_component_query(form_cq, name)
self._store_helper.reset_store_load_count(combobox_cq)
# Filter it
field = self.find_field_input_element(form_cq, name)
self._input_helper.type_into_element(field, field_value, disable_realistic_typing=True)
# Wait for the store to load
self._store_helper.wait_for_store_loaded(combobox_cq)
# Often (especially when local) the load count will be incremented several times
# before the store load has actually triggered and completed.
# To guard against this, wait for any pending Ajax calls to complete, then check
# if the store has loaded
self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1)
# FIXME: Does the store have a count of one?
# .....: Do we really care? If multiple then the top one will be highlighted...
# Seems we need to tab off or sometimes the value will not stick?!
self._input_helper.type_tab()
else:
# We are expecting some filter text
filter_text = self._core.try_get_object_member(value, 'filterText')
if not filter_text:
raise Core.ArgumentException("value", "We were expecting the argument '{name}' to have a 'filterText' member, but it is missing.")
# Reset the store load count
combobox_cq = self.get_field_component_query(form_cq, name)
self._store_helper.reset_store_load_count(combobox_cq)
# Filter it
field = self.find_field_input_element(form_cq, name)
self._input_helper.type_into_element(field, filter_text, disable_realistic_typing=True)
# Wait for the store to load
self._store_helper.wait_for_store_loaded(combobox_cq)
# Often (especially when local) the load count will be incremented several times
# before the store load has actually triggered and completed.
# To guard against this, wait for any pending Ajax calls to complete.
self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1)
was_value_selected = self.select_combobox_value(form_cq, name, field_value)
if not was_value_selected:
raise FieldHelper.RecordNotFoundException(form_cq, name, field_value)
else:
if not is_value_a_dict:
# We can just type into the combobox
field = self.find_field_input_element(form_cq, name)
self._input_helper.type_into_element(field, field_value)
# Seems we need to tab off or sometimes the value will not stick?!
self._input_helper.type_tab()
else:
was_value_selected = self.select_combobox_value(form_cq, name, field_value)
if not was_value_selected:
raise FieldHelper.RecordNotFoundException(form_cq, name, field_value)
elif self.is_field_a_text_field(form_cq, name):
# Field can be typed into
field = self.find_field_input_element(form_cq, name)
self._input_helper.type_into_element(field, field_value)
# Still using xtype for testing for radiogroup controls, since name tends to be
# shared between group and contained radios, meaning check fails.
# Pretty unlikely to ever subclass a radiogroup class anyway, and if did would almost
# certainly have the same suffix (we do).
elif (field_xtype.endswith('radiogroup') or
self.is_field_a_checkbox(form_cq, name) or
self.is_field_a_radio_field(form_cq, name)):
# FIXME: We could click on the elements here, after checking whether they
# .....: are already set to the value we want.
# .....: For a radio group, can get child controls using getBoxes(query),
# .....: so could click on children if wanted!
# Directly set the value on the field
self.set_field_value_directly(form_cq, name, field_value)
else:
raise FieldHelper.UnsupportedFieldXTypeException(form_cq, name, field_xtype)
else:
raise FieldHelper.FieldNotFoundException(form_cq, name)
def set_field_value_directly(self, form_cq: str, name: str, value: Union[int, str]):
"""Sets the value for a field using Ext's setValue method.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
value (Union[int, str]): The value for the field.
"""
# If value is a string then we want to quote it in our script
if isinstance(value, str):
value = f"'{value}'"
# If value is a boolean we want to force it to lowercase
if isinstance(value, bool):
value = str(value).lower()
script = self._SET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, value=value)
self.ensure_javascript_loaded()
self._driver.execute_script(script)
def focus_field(self, form_cq: str, index_or_name: Union[int, str], check_has_focus_after: bool = True):
"""Method to focus on a field on a form by (zero-based) index or name.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
index_or_name (Union[int, str]): The zero-based index or name of the field to focus.
check_has_focus_after (bool): Indicates whether to check that the field is focused afterwards. Defaults to True.
"""
# If value is a string then we want to quote it in our script
if isinstance(index_or_name, str):
index_or_name = f"'{index_or_name}'"
script = self._FOCUS_FIELD_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name)
self.ensure_javascript_loaded()
self._logger.info("Focusing field %s on form '%s'", index_or_name, form_cq)
self._driver.execute_script(script)
if check_has_focus_after:
has_focus = self.does_field_have_focus(form_cq, index_or_name)
if not has_focus:
self._logger.debug("Field %s on form '%s' does not have focus!", index_or_name, form_cq)
# Try again (can't hurt)
self._driver.execute_script(script)
else:
self._logger.debug("Field %s on form '%s' has focus!", index_or_name, form_cq)
def does_field_have_focus(self, form_cq: str, index_or_name: Union[int, str]) -> bool:
"""Determines whether focus is on a field on a form by (zero-based) index or name.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
index_or_name (Union[int, str]): The zero-based index or name of the field.
Returns:
bool: True if the field has focus, False otherwise.
"""
# If value is a string then we want to quote it in our script
if isinstance(index_or_name, str):
index_or_name = f"'{index_or_name}'"
script = self._DOES_FIELD_HAVE_FOCUS_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def wait_until_field_has_focus(self, form_cq: str, index_or_name: Union[int, str], timeout: float = 10):
"""Waits until the focus is on a field on a form by (zero-based) index or name.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
index_or_name (Union[int, str]): The zero-based index or name of the field.
timeout (float): Number of seconds before timing out (default 10)
"""
WebDriverWait(self._driver, timeout).until(FieldHelper.FieldHasFocusExpectation(form_cq, index_or_name))
def get_field_component_query(self, form_cq: str, name: str):
"""Builds the component query for a field on a form.
This is useful, since allows you to interact with a field using component query, so can
utilise the methods in StoreHelper, say.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field.
"""
# Need to use component rather than field since need to be able to query for radiogroup and checkbox group.
return f'{form_cq} component[name="{name}"]'
def check_field_value(self, form_cq: str, name: str, value: Any) -> bool:
"""Method that checks that the value of the specified field is that specified.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field.
name (str): The name of the field
value (Any): The value that we expect the field to have.
Returns:
bool: True if the value of the field matches the expected, False otherwise.
"""
field_value = self.get_field_value(form_cq, name)
return field_value == value
def select_combobox_value(self, form_cq: str, name: str, data: dict) -> bool:
"""Selects a value on a combobox field by finding a record with the specified data.
All members of the data object must match a record in the store.
Waits until the combobox has finished loading first, and ensures that the select
event is fired on the combobox if the record is found.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
data (dict): The data to find in the store and select.
Returns:
True if the value was found and selected, False otherwise.
"""
self._store_helper.wait_for_store_loaded(self.get_field_component_query(form_cq, name))
script = self._SELECT_COMBOBOX_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, data=data)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
def _is_field_remotely_filtered_combobox(self, form_cq: str, name: str) -> bool:
"""Attempts to find a field by name from the specified form panel, and determine whether
it is a remotely filtered combobox.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
name (str): The name of the field
Returns:
bool: True if the field was found, and is a remotely filtered combobox. False otherwise.
"""
script = self._IS_REMOTELY_FILTERED_COMBOBOX_TEMPLATE.format(form_cq=form_cq, name=name)
self.ensure_javascript_loaded()
return self._driver.execute_script(script)
class FieldNotFoundException(Exception):
"""Exception class thrown when we failed to find the specified field"""
def __init__(self, form_cq: str, name: str, message: str = "Failed to find field named '{name}' on form with CQ '{form_cq}'."):
"""Initialises an instance of this exception
Args:
form_cq (str): The CQ used to find the form
name (str): The name of the field
message (str, optional): The exception message. Defaults to "Failed to find field named '{name}' on form with CQ '{form_cq}'.".
"""
self.message = message
self._form_cq = form_cq
self._name = name
super().__init__(self.message)
def __str__(self):
"""Returns a string representation of this exception"""
return self.message.format(name=self._name, form_cq=self._form_cq)
class UnsupportedFieldXTypeException(Exception):
"""Exception class thrown when we have been asked to perform an action that is not
supported for the given field xtype.
"""
def __init__(self,
form_cq: str,
name: str,
xtype: str,
message: str = "The field named '{name}' on form with CQ '{form_cq}' is of an xtype '{xtype}' which is not supported for the requested operation."):
"""Initialises an instance of this exception
Args:
form_cq (str): The CQ used to find the form
name (str): The name of the field
xtype (str): The xtype of the field.
message (str, optional): The exception message.
Defaults to "The field named '{name}' on form with CQ '{form_cq}' is of an xtype '{xtype}' which is not supported for the requested operation."
"""
self.message = message
self._form_cq = form_cq
self._name = name
self._xtype = xtype
super().__init__(self.message)
def __str__(self):
"""Returns a string representation of this exception"""
return self.message.format(name=self._name, form_cq=self._form_cq, xtype=self._xtype)
class RecordNotFoundException(Exception):
"""Exception class thrown when we failed to find the specified record in the a combobox"""
def __init__(self, form_cq: str, name: str, data: dict, message: str = "Failed to find record with data {data} in combobox named '{name}' on form with CQ '{form_cq}'."):
"""Initialises an instance of this exception
Args:
form_cq (str): The CQ used to find the form
name (str): The name of the combobox
data (dict): The data for the record we were looking for.
message (str, optional): The exception message. Defaults to "Failed to find record with data {data} in combobox named '{name}' on form with CQ '{form_cq}'.".
"""
self.message = message
self._form_cq = form_cq
self._name = name
self._data = data
super().__init__(self.message)
def __str__(self):
"""Returns a string representation of this exception"""
return self.message.format(data=self._data, name=self._name, form_cq=self._form_cq)
class FieldHasFocusExpectation:
""" An expectation for checking that the specified field has focus"""
def __init__(self, form_cq: str, index_or_name: Union[int, str]):
"""Initialises an instance of this class.
Args:
form_cq (str): The component query that identifies the form panel in which to look for the field
index_or_name (Union[int, str]): The zero-based index or name of the field.
"""
self._form_cq = form_cq
self._index_or_name = index_or_name
def __call__(self, driver):
"""Method that determines whether a CQ is found
"""
return FieldHelper(driver).does_field_have_focus(self._form_cq, self._index_or_name)
Classes
class FieldHelper (driver: selenium.webdriver.remote.webdriver.WebDriver)
-
A class to help with interacting with Ext fields
Initialises an instance of this class
Args
driver
:WebDriver
- The webdriver to use
Expand source code
class FieldHelper(HasReferencedJavaScript): """A class to help with interacting with Ext fields""" # Class variables _FIND_FIELD_INPUT_ELEMENT_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.findFieldInputElement('{form_cq}', '{name}')" """The script template to use to call the JavaScript method PySeExt.FieldHelper.findFieldInputElement Requires the inserts: {form_cq}, {name}""" _GET_FIELD_XTYPE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldXType('{form_cq}', '{name}')" """The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldXType Requires the inserts: {form_cq}, {name}""" _GET_FIELD_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldValue('{form_cq}', '{name}')" """The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldValue Requires the inserts: {form_cq}, {name}""" _GET_FIELD_DISPLAY_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.getFieldDisplayValue('{form_cq}', '{name}')" """The script template to use to call the JavaScript method PySeExt.FieldHelper.getFieldDisplayValue Requires the inserts: {form_cq}, {name}""" _SET_FIELD_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.setFieldValue('{form_cq}', '{name}', {value})" """The script template to use to call the JavaScript method PySeExt.FieldHelper.setFieldValue Requires the inserts: {form_cq}, {name}, {value}""" _IS_REMOTELY_FILTERED_COMBOBOX_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.isRemotelyFilteredComboBox('{form_cq}', '{name}')" """The script template to use to call the JavaScript method PySeExt.FieldHelper.isRemotelyFilteredComboBox Requires the inserts: {form_cq}, {name}""" _FOCUS_FIELD_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.focusField('{form_cq}', {index_or_name})" """The script template to use to call the JavaScript method PySeExt.FieldHelper.focusField Requires the inserts: {form_cq}, {index_or_name}""" _DOES_FIELD_HAVE_FOCUS_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.doesFieldHaveFocus('{form_cq}', {index_or_name})" """The script template to use to call the JavaScript method PySeExt.FieldHelper.doesFieldHaveFocus Requires the inserts: {form_cq}, {index_or_name}""" _SELECT_COMBOBOX_VALUE_TEMPLATE: str = "return globalThis.PySeExt.FieldHelper.selectComboBoxValue('{form_cq}', '{name}', {data})" """The script template to use to call the JavaScript method PySeExt.FieldHelper.selectComboBoxValue Requires the inserts: {form_cq}, {name}, {data}""" def __init__(self, driver: WebDriver): """Initialises an instance of this class Args: driver (WebDriver): The webdriver to use """ self._logger = logging.getLogger(__name__) """The Logger instance for this class instance""" self._driver = driver """The WebDriver instance for this class instance""" self._action_chains = ActionChains(driver) """The ActionChains instance for this class instance""" self._core = Core(driver) """The `Core` instance for this class instance""" self._input_helper = InputHelper(driver) """The `InputHelper` instance for this class instance""" self._store_helper = StoreHelper(driver) """The `StoreHelper` instance for this class instance""" self._cq = ComponentQuery(driver) """The `ComponentQuery` instance for this class instance""" # Initialise our base class super().__init__(driver, self._logger) def find_field_input_element(self, form_cq: str, name: str) -> WebElement: """Attempts to get a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: WebElement: The field's input element DOM element, or None if not found. """ field_cq = self.get_field_component_query(form_cq, name) # Append enabled test field_cq = field_cq + '{isDisabled()===false}' # And wait until available self._cq.wait_for_single_query(field_cq) # Now get its input element script = self._FIND_FIELD_INPUT_ELEMENT_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script) def get_field_xtype(self, form_cq: str, name: str) -> str: """Attempts to get the xtype of a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: str: The xtype of the field, or None if not found. """ script = self._GET_FIELD_XTYPE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script) def is_field_a_combobox(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a combobox (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a combobox, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.ComboBox', self.get_field_component_query(form_cq, name)) def is_field_a_text_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a text field (or a subclass of it). Note that text areas, number fields, and date fields all inherit from text field. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a text field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Text', self.get_field_component_query(form_cq, name)) def is_field_a_number_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a number field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a number field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Number', self.get_field_component_query(form_cq, name)) def is_field_a_date_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a date field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a date field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Date', self.get_field_component_query(form_cq, name)) def is_field_a_checkbox(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a checkbox (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a checkbox, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Checkbox', self.get_field_component_query(form_cq, name)) def is_field_a_radio_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a radio field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a radio field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Radio', self.get_field_component_query(form_cq, name)) def get_field_value(self, form_cq: str, name: str) -> Any: """Attempts to get the value of a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: Any: The value of the field, or None if not found. """ script = self._GET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script) def get_field_display_value(self, form_cq: str, name: str) -> Any: """Attempts to get the display value of a field by name from the specified form panel. Supported by comboboxes and display fields. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: Any: The display value of the field, or None if not found or if the field does not have a display value. """ script = self._GET_FIELD_DISPLAY_VALUE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script) def set_field_value(self, form_cq: str, name: str, value: Union[dict, float, str]): """Sets the value for a field. If the field can be typed into by a user, and the value is typeable, then that is the approach taken. Args: form_cq (str): The component query that identifies the form panel in which to look for the field. name (str): The name of the field. value (Union[dict, float, str]): The value for the field. If the value is a number or string then it is typed into the field where possible, otherwise the value is set directly. If the value is a dictionary then either it, or it's value member is taken to be model data to select in a combobox. If the combobox is remotely filtered then it is expected that both a value member and a filterText member is specified, where the filterText is typed into the combobox, then the value selected. If the value is supplied as a dictionary and the field is not a store holder then an exception is thrown. """ # Only used for radiogroup controls now, but xtype still useful to throw a better error if field not found. field_xtype = self.get_field_xtype(form_cq, name) if field_xtype: # Field found! field_value = self._core.try_get_object_member(value, 'value', value) # Now need to set it's value if self.is_field_a_combobox(form_cq, name): is_combo_remote = self._is_field_remotely_filtered_combobox(form_cq, name) is_value_a_dict = isinstance(field_value, dict) if is_combo_remote: if not is_value_a_dict: # We want to type into the combobox, to filter it, and wait for it to load. # Once loaded we expect to have a single value that will end up selected. # Reset the store load count combobox_cq = self.get_field_component_query(form_cq, name) self._store_helper.reset_store_load_count(combobox_cq) # Filter it field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value, disable_realistic_typing=True) # Wait for the store to load self._store_helper.wait_for_store_loaded(combobox_cq) # Often (especially when local) the load count will be incremented several times # before the store load has actually triggered and completed. # To guard against this, wait for any pending Ajax calls to complete, then check # if the store has loaded self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1) # FIXME: Does the store have a count of one? # .....: Do we really care? If multiple then the top one will be highlighted... # Seems we need to tab off or sometimes the value will not stick?! self._input_helper.type_tab() else: # We are expecting some filter text filter_text = self._core.try_get_object_member(value, 'filterText') if not filter_text: raise Core.ArgumentException("value", "We were expecting the argument '{name}' to have a 'filterText' member, but it is missing.") # Reset the store load count combobox_cq = self.get_field_component_query(form_cq, name) self._store_helper.reset_store_load_count(combobox_cq) # Filter it field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, filter_text, disable_realistic_typing=True) # Wait for the store to load self._store_helper.wait_for_store_loaded(combobox_cq) # Often (especially when local) the load count will be incremented several times # before the store load has actually triggered and completed. # To guard against this, wait for any pending Ajax calls to complete. self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1) was_value_selected = self.select_combobox_value(form_cq, name, field_value) if not was_value_selected: raise FieldHelper.RecordNotFoundException(form_cq, name, field_value) else: if not is_value_a_dict: # We can just type into the combobox field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value) # Seems we need to tab off or sometimes the value will not stick?! self._input_helper.type_tab() else: was_value_selected = self.select_combobox_value(form_cq, name, field_value) if not was_value_selected: raise FieldHelper.RecordNotFoundException(form_cq, name, field_value) elif self.is_field_a_text_field(form_cq, name): # Field can be typed into field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value) # Still using xtype for testing for radiogroup controls, since name tends to be # shared between group and contained radios, meaning check fails. # Pretty unlikely to ever subclass a radiogroup class anyway, and if did would almost # certainly have the same suffix (we do). elif (field_xtype.endswith('radiogroup') or self.is_field_a_checkbox(form_cq, name) or self.is_field_a_radio_field(form_cq, name)): # FIXME: We could click on the elements here, after checking whether they # .....: are already set to the value we want. # .....: For a radio group, can get child controls using getBoxes(query), # .....: so could click on children if wanted! # Directly set the value on the field self.set_field_value_directly(form_cq, name, field_value) else: raise FieldHelper.UnsupportedFieldXTypeException(form_cq, name, field_xtype) else: raise FieldHelper.FieldNotFoundException(form_cq, name) def set_field_value_directly(self, form_cq: str, name: str, value: Union[int, str]): """Sets the value for a field using Ext's setValue method. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field value (Union[int, str]): The value for the field. """ # If value is a string then we want to quote it in our script if isinstance(value, str): value = f"'{value}'" # If value is a boolean we want to force it to lowercase if isinstance(value, bool): value = str(value).lower() script = self._SET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, value=value) self.ensure_javascript_loaded() self._driver.execute_script(script) def focus_field(self, form_cq: str, index_or_name: Union[int, str], check_has_focus_after: bool = True): """Method to focus on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field to focus. check_has_focus_after (bool): Indicates whether to check that the field is focused afterwards. Defaults to True. """ # If value is a string then we want to quote it in our script if isinstance(index_or_name, str): index_or_name = f"'{index_or_name}'" script = self._FOCUS_FIELD_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name) self.ensure_javascript_loaded() self._logger.info("Focusing field %s on form '%s'", index_or_name, form_cq) self._driver.execute_script(script) if check_has_focus_after: has_focus = self.does_field_have_focus(form_cq, index_or_name) if not has_focus: self._logger.debug("Field %s on form '%s' does not have focus!", index_or_name, form_cq) # Try again (can't hurt) self._driver.execute_script(script) else: self._logger.debug("Field %s on form '%s' has focus!", index_or_name, form_cq) def does_field_have_focus(self, form_cq: str, index_or_name: Union[int, str]) -> bool: """Determines whether focus is on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field. Returns: bool: True if the field has focus, False otherwise. """ # If value is a string then we want to quote it in our script if isinstance(index_or_name, str): index_or_name = f"'{index_or_name}'" script = self._DOES_FIELD_HAVE_FOCUS_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name) self.ensure_javascript_loaded() return self._driver.execute_script(script) def wait_until_field_has_focus(self, form_cq: str, index_or_name: Union[int, str], timeout: float = 10): """Waits until the focus is on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field. timeout (float): Number of seconds before timing out (default 10) """ WebDriverWait(self._driver, timeout).until(FieldHelper.FieldHasFocusExpectation(form_cq, index_or_name)) def get_field_component_query(self, form_cq: str, name: str): """Builds the component query for a field on a form. This is useful, since allows you to interact with a field using component query, so can utilise the methods in StoreHelper, say. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field. """ # Need to use component rather than field since need to be able to query for radiogroup and checkbox group. return f'{form_cq} component[name="{name}"]' def check_field_value(self, form_cq: str, name: str, value: Any) -> bool: """Method that checks that the value of the specified field is that specified. Args: form_cq (str): The component query that identifies the form panel in which to look for the field. name (str): The name of the field value (Any): The value that we expect the field to have. Returns: bool: True if the value of the field matches the expected, False otherwise. """ field_value = self.get_field_value(form_cq, name) return field_value == value def select_combobox_value(self, form_cq: str, name: str, data: dict) -> bool: """Selects a value on a combobox field by finding a record with the specified data. All members of the data object must match a record in the store. Waits until the combobox has finished loading first, and ensures that the select event is fired on the combobox if the record is found. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field data (dict): The data to find in the store and select. Returns: True if the value was found and selected, False otherwise. """ self._store_helper.wait_for_store_loaded(self.get_field_component_query(form_cq, name)) script = self._SELECT_COMBOBOX_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, data=data) self.ensure_javascript_loaded() return self._driver.execute_script(script) def _is_field_remotely_filtered_combobox(self, form_cq: str, name: str) -> bool: """Attempts to find a field by name from the specified form panel, and determine whether it is a remotely filtered combobox. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field was found, and is a remotely filtered combobox. False otherwise. """ script = self._IS_REMOTELY_FILTERED_COMBOBOX_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script) class FieldNotFoundException(Exception): """Exception class thrown when we failed to find the specified field""" def __init__(self, form_cq: str, name: str, message: str = "Failed to find field named '{name}' on form with CQ '{form_cq}'."): """Initialises an instance of this exception Args: form_cq (str): The CQ used to find the form name (str): The name of the field message (str, optional): The exception message. Defaults to "Failed to find field named '{name}' on form with CQ '{form_cq}'.". """ self.message = message self._form_cq = form_cq self._name = name super().__init__(self.message) def __str__(self): """Returns a string representation of this exception""" return self.message.format(name=self._name, form_cq=self._form_cq) class UnsupportedFieldXTypeException(Exception): """Exception class thrown when we have been asked to perform an action that is not supported for the given field xtype. """ def __init__(self, form_cq: str, name: str, xtype: str, message: str = "The field named '{name}' on form with CQ '{form_cq}' is of an xtype '{xtype}' which is not supported for the requested operation."): """Initialises an instance of this exception Args: form_cq (str): The CQ used to find the form name (str): The name of the field xtype (str): The xtype of the field. message (str, optional): The exception message. Defaults to "The field named '{name}' on form with CQ '{form_cq}' is of an xtype '{xtype}' which is not supported for the requested operation." """ self.message = message self._form_cq = form_cq self._name = name self._xtype = xtype super().__init__(self.message) def __str__(self): """Returns a string representation of this exception""" return self.message.format(name=self._name, form_cq=self._form_cq, xtype=self._xtype) class RecordNotFoundException(Exception): """Exception class thrown when we failed to find the specified record in the a combobox""" def __init__(self, form_cq: str, name: str, data: dict, message: str = "Failed to find record with data {data} in combobox named '{name}' on form with CQ '{form_cq}'."): """Initialises an instance of this exception Args: form_cq (str): The CQ used to find the form name (str): The name of the combobox data (dict): The data for the record we were looking for. message (str, optional): The exception message. Defaults to "Failed to find record with data {data} in combobox named '{name}' on form with CQ '{form_cq}'.". """ self.message = message self._form_cq = form_cq self._name = name self._data = data super().__init__(self.message) def __str__(self): """Returns a string representation of this exception""" return self.message.format(data=self._data, name=self._name, form_cq=self._form_cq) class FieldHasFocusExpectation: """ An expectation for checking that the specified field has focus""" def __init__(self, form_cq: str, index_or_name: Union[int, str]): """Initialises an instance of this class. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field. """ self._form_cq = form_cq self._index_or_name = index_or_name def __call__(self, driver): """Method that determines whether a CQ is found """ return FieldHelper(driver).does_field_have_focus(self._form_cq, self._index_or_name)
Ancestors
Class variables
var FieldHasFocusExpectation
-
An expectation for checking that the specified field has focus
var FieldNotFoundException
-
Exception class thrown when we failed to find the specified field
var RecordNotFoundException
-
Exception class thrown when we failed to find the specified record in the a combobox
var UnsupportedFieldXTypeException
-
Exception class thrown when we have been asked to perform an action that is not supported for the given field xtype.
Methods
def check_field_value(self, form_cq: str, name: str, value: Any) ‑> bool
-
Method that checks that the value of the specified field is that specified.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field.
name
:str
- The name of the field
value
:Any
- The value that we expect the field to have.
Returns
bool
- True if the value of the field matches the expected, False otherwise.
Expand source code
def check_field_value(self, form_cq: str, name: str, value: Any) -> bool: """Method that checks that the value of the specified field is that specified. Args: form_cq (str): The component query that identifies the form panel in which to look for the field. name (str): The name of the field value (Any): The value that we expect the field to have. Returns: bool: True if the value of the field matches the expected, False otherwise. """ field_value = self.get_field_value(form_cq, name) return field_value == value
def does_field_have_focus(self, form_cq: str, index_or_name: Union[int, str]) ‑> bool
-
Determines whether focus is on a field on a form by (zero-based) index or name.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
index_or_name
:Union[int, str]
- The zero-based index or name of the field.
Returns
bool
- True if the field has focus, False otherwise.
Expand source code
def does_field_have_focus(self, form_cq: str, index_or_name: Union[int, str]) -> bool: """Determines whether focus is on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field. Returns: bool: True if the field has focus, False otherwise. """ # If value is a string then we want to quote it in our script if isinstance(index_or_name, str): index_or_name = f"'{index_or_name}'" script = self._DOES_FIELD_HAVE_FOCUS_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def find_field_input_element(self, form_cq: str, name: str) ‑> selenium.webdriver.remote.webelement.WebElement
-
Attempts to get a field by name from the specified form panel
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
WebElement
- The field's input element DOM element, or None if not found.
Expand source code
def find_field_input_element(self, form_cq: str, name: str) -> WebElement: """Attempts to get a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: WebElement: The field's input element DOM element, or None if not found. """ field_cq = self.get_field_component_query(form_cq, name) # Append enabled test field_cq = field_cq + '{isDisabled()===false}' # And wait until available self._cq.wait_for_single_query(field_cq) # Now get its input element script = self._FIND_FIELD_INPUT_ELEMENT_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def focus_field(self, form_cq: str, index_or_name: Union[int, str], check_has_focus_after: bool = True)
-
Method to focus on a field on a form by (zero-based) index or name.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
index_or_name
:Union[int, str]
- The zero-based index or name of the field to focus.
check_has_focus_after
:bool
- Indicates whether to check that the field is focused afterwards. Defaults to True.
Expand source code
def focus_field(self, form_cq: str, index_or_name: Union[int, str], check_has_focus_after: bool = True): """Method to focus on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field to focus. check_has_focus_after (bool): Indicates whether to check that the field is focused afterwards. Defaults to True. """ # If value is a string then we want to quote it in our script if isinstance(index_or_name, str): index_or_name = f"'{index_or_name}'" script = self._FOCUS_FIELD_TEMPLATE.format(form_cq=form_cq, index_or_name=index_or_name) self.ensure_javascript_loaded() self._logger.info("Focusing field %s on form '%s'", index_or_name, form_cq) self._driver.execute_script(script) if check_has_focus_after: has_focus = self.does_field_have_focus(form_cq, index_or_name) if not has_focus: self._logger.debug("Field %s on form '%s' does not have focus!", index_or_name, form_cq) # Try again (can't hurt) self._driver.execute_script(script) else: self._logger.debug("Field %s on form '%s' has focus!", index_or_name, form_cq)
def get_field_component_query(self, form_cq: str, name: str)
-
Builds the component query for a field on a form.
This is useful, since allows you to interact with a field using component query, so can utilise the methods in StoreHelper, say.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field.
Expand source code
def get_field_component_query(self, form_cq: str, name: str): """Builds the component query for a field on a form. This is useful, since allows you to interact with a field using component query, so can utilise the methods in StoreHelper, say. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field. """ # Need to use component rather than field since need to be able to query for radiogroup and checkbox group. return f'{form_cq} component[name="{name}"]'
def get_field_display_value(self, form_cq: str, name: str) ‑> Any
-
Attempts to get the display value of a field by name from the specified form panel.
Supported by comboboxes and display fields.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
Any
- The display value of the field, or None if not found or if the field does not have a display value.
Expand source code
def get_field_display_value(self, form_cq: str, name: str) -> Any: """Attempts to get the display value of a field by name from the specified form panel. Supported by comboboxes and display fields. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: Any: The display value of the field, or None if not found or if the field does not have a display value. """ script = self._GET_FIELD_DISPLAY_VALUE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def get_field_value(self, form_cq: str, name: str) ‑> Any
-
Attempts to get the value of a field by name from the specified form panel
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
Any
- The value of the field, or None if not found.
Expand source code
def get_field_value(self, form_cq: str, name: str) -> Any: """Attempts to get the value of a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: Any: The value of the field, or None if not found. """ script = self._GET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def get_field_xtype(self, form_cq: str, name: str) ‑> str
-
Attempts to get the xtype of a field by name from the specified form panel
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
str
- The xtype of the field, or None if not found.
Expand source code
def get_field_xtype(self, form_cq: str, name: str) -> str: """Attempts to get the xtype of a field by name from the specified form panel Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: str: The xtype of the field, or None if not found. """ script = self._GET_FIELD_XTYPE_TEMPLATE.format(form_cq=form_cq, name=name) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def is_field_a_checkbox(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a checkbox (or a subclass of it).
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a checkbox, False otherwise.
Expand source code
def is_field_a_checkbox(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a checkbox (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a checkbox, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Checkbox', self.get_field_component_query(form_cq, name))
def is_field_a_combobox(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a combobox (or a subclass of it).
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a combobox, False otherwise.
Expand source code
def is_field_a_combobox(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a combobox (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a combobox, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.ComboBox', self.get_field_component_query(form_cq, name))
def is_field_a_date_field(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a date field (or a subclass of it).
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a date field, False otherwise.
Expand source code
def is_field_a_date_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a date field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a date field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Date', self.get_field_component_query(form_cq, name))
def is_field_a_number_field(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a number field (or a subclass of it).
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a number field, False otherwise.
Expand source code
def is_field_a_number_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a number field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a number field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Number', self.get_field_component_query(form_cq, name))
def is_field_a_radio_field(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a radio field (or a subclass of it).
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a radio field, False otherwise.
Expand source code
def is_field_a_radio_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a radio field (or a subclass of it). Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a radio field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Radio', self.get_field_component_query(form_cq, name))
def is_field_a_text_field(self, form_cq: str, name: str) ‑> bool
-
Determine whether the field (by name) on the specified form panel is a text field (or a subclass of it). Note that text areas, number fields, and date fields all inherit from text field.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
Returns
bool
- True if the field is a text field, False otherwise.
Expand source code
def is_field_a_text_field(self, form_cq: str, name: str) -> bool: """Determine whether the field (by name) on the specified form panel is a text field (or a subclass of it). Note that text areas, number fields, and date fields all inherit from text field. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field Returns: bool: True if the field is a text field, False otherwise. """ return self._cq.is_component_instance_of_class('Ext.form.field.Text', self.get_field_component_query(form_cq, name))
def select_combobox_value(self, form_cq: str, name: str, data: dict) ‑> bool
-
Selects a value on a combobox field by finding a record with the specified data. All members of the data object must match a record in the store.
Waits until the combobox has finished loading first, and ensures that the select event is fired on the combobox if the record is found.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
data
:dict
- The data to find in the store and select.
Returns
True if the value was found and selected, False otherwise.
Expand source code
def select_combobox_value(self, form_cq: str, name: str, data: dict) -> bool: """Selects a value on a combobox field by finding a record with the specified data. All members of the data object must match a record in the store. Waits until the combobox has finished loading first, and ensures that the select event is fired on the combobox if the record is found. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field data (dict): The data to find in the store and select. Returns: True if the value was found and selected, False otherwise. """ self._store_helper.wait_for_store_loaded(self.get_field_component_query(form_cq, name)) script = self._SELECT_COMBOBOX_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, data=data) self.ensure_javascript_loaded() return self._driver.execute_script(script)
def set_field_value(self, form_cq: str, name: str, value: Union[dict, float, str])
-
Sets the value for a field.
If the field can be typed into by a user, and the value is typeable, then that is the approach taken.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field.
name
:str
- The name of the field.
value
:Union[dict, float, str]
- The value for the field.
If the value is a number or string then it is typed into the field where possible, otherwise the value is set directly. If the value is a dictionary then either it, or it's value member is taken to be model data to select in a combobox. If the combobox is remotely filtered then it is expected that both a value member and a filterText member is specified, where the filterText is typed into the combobox, then the value selected. If the value is supplied as a dictionary and the field is not a store holder then an exception is thrown.
Expand source code
def set_field_value(self, form_cq: str, name: str, value: Union[dict, float, str]): """Sets the value for a field. If the field can be typed into by a user, and the value is typeable, then that is the approach taken. Args: form_cq (str): The component query that identifies the form panel in which to look for the field. name (str): The name of the field. value (Union[dict, float, str]): The value for the field. If the value is a number or string then it is typed into the field where possible, otherwise the value is set directly. If the value is a dictionary then either it, or it's value member is taken to be model data to select in a combobox. If the combobox is remotely filtered then it is expected that both a value member and a filterText member is specified, where the filterText is typed into the combobox, then the value selected. If the value is supplied as a dictionary and the field is not a store holder then an exception is thrown. """ # Only used for radiogroup controls now, but xtype still useful to throw a better error if field not found. field_xtype = self.get_field_xtype(form_cq, name) if field_xtype: # Field found! field_value = self._core.try_get_object_member(value, 'value', value) # Now need to set it's value if self.is_field_a_combobox(form_cq, name): is_combo_remote = self._is_field_remotely_filtered_combobox(form_cq, name) is_value_a_dict = isinstance(field_value, dict) if is_combo_remote: if not is_value_a_dict: # We want to type into the combobox, to filter it, and wait for it to load. # Once loaded we expect to have a single value that will end up selected. # Reset the store load count combobox_cq = self.get_field_component_query(form_cq, name) self._store_helper.reset_store_load_count(combobox_cq) # Filter it field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value, disable_realistic_typing=True) # Wait for the store to load self._store_helper.wait_for_store_loaded(combobox_cq) # Often (especially when local) the load count will be incremented several times # before the store load has actually triggered and completed. # To guard against this, wait for any pending Ajax calls to complete, then check # if the store has loaded self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1) # FIXME: Does the store have a count of one? # .....: Do we really care? If multiple then the top one will be highlighted... # Seems we need to tab off or sometimes the value will not stick?! self._input_helper.type_tab() else: # We are expecting some filter text filter_text = self._core.try_get_object_member(value, 'filterText') if not filter_text: raise Core.ArgumentException("value", "We were expecting the argument '{name}' to have a 'filterText' member, but it is missing.") # Reset the store load count combobox_cq = self.get_field_component_query(form_cq, name) self._store_helper.reset_store_load_count(combobox_cq) # Filter it field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, filter_text, disable_realistic_typing=True) # Wait for the store to load self._store_helper.wait_for_store_loaded(combobox_cq) # Often (especially when local) the load count will be incremented several times # before the store load has actually triggered and completed. # To guard against this, wait for any pending Ajax calls to complete. self._core.wait_for_no_ajax_requests_in_progress(recheck_time_if_false=1) was_value_selected = self.select_combobox_value(form_cq, name, field_value) if not was_value_selected: raise FieldHelper.RecordNotFoundException(form_cq, name, field_value) else: if not is_value_a_dict: # We can just type into the combobox field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value) # Seems we need to tab off or sometimes the value will not stick?! self._input_helper.type_tab() else: was_value_selected = self.select_combobox_value(form_cq, name, field_value) if not was_value_selected: raise FieldHelper.RecordNotFoundException(form_cq, name, field_value) elif self.is_field_a_text_field(form_cq, name): # Field can be typed into field = self.find_field_input_element(form_cq, name) self._input_helper.type_into_element(field, field_value) # Still using xtype for testing for radiogroup controls, since name tends to be # shared between group and contained radios, meaning check fails. # Pretty unlikely to ever subclass a radiogroup class anyway, and if did would almost # certainly have the same suffix (we do). elif (field_xtype.endswith('radiogroup') or self.is_field_a_checkbox(form_cq, name) or self.is_field_a_radio_field(form_cq, name)): # FIXME: We could click on the elements here, after checking whether they # .....: are already set to the value we want. # .....: For a radio group, can get child controls using getBoxes(query), # .....: so could click on children if wanted! # Directly set the value on the field self.set_field_value_directly(form_cq, name, field_value) else: raise FieldHelper.UnsupportedFieldXTypeException(form_cq, name, field_xtype) else: raise FieldHelper.FieldNotFoundException(form_cq, name)
def set_field_value_directly(self, form_cq: str, name: str, value: Union[int, str])
-
Sets the value for a field using Ext's setValue method.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
name
:str
- The name of the field
value
:Union[int, str]
- The value for the field.
Expand source code
def set_field_value_directly(self, form_cq: str, name: str, value: Union[int, str]): """Sets the value for a field using Ext's setValue method. Args: form_cq (str): The component query that identifies the form panel in which to look for the field name (str): The name of the field value (Union[int, str]): The value for the field. """ # If value is a string then we want to quote it in our script if isinstance(value, str): value = f"'{value}'" # If value is a boolean we want to force it to lowercase if isinstance(value, bool): value = str(value).lower() script = self._SET_FIELD_VALUE_TEMPLATE.format(form_cq=form_cq, name=name, value=value) self.ensure_javascript_loaded() self._driver.execute_script(script)
def wait_until_field_has_focus(self, form_cq: str, index_or_name: Union[int, str], timeout: float = 10)
-
Waits until the focus is on a field on a form by (zero-based) index or name.
Args
form_cq
:str
- The component query that identifies the form panel in which to look for the field
index_or_name
:Union[int, str]
- The zero-based index or name of the field.
timeout
:float
- Number of seconds before timing out (default 10)
Expand source code
def wait_until_field_has_focus(self, form_cq: str, index_or_name: Union[int, str], timeout: float = 10): """Waits until the focus is on a field on a form by (zero-based) index or name. Args: form_cq (str): The component query that identifies the form panel in which to look for the field index_or_name (Union[int, str]): The zero-based index or name of the field. timeout (float): Number of seconds before timing out (default 10) """ WebDriverWait(self._driver, timeout).until(FieldHelper.FieldHasFocusExpectation(form_cq, index_or_name))
Inherited members