Module pyseext.grid_helper
Module that contains our GridHelper class.
Expand source code
"""
Module that contains our GridHelper class.
"""
import logging
import random
from typing import Union
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from pyseext.has_referenced_javascript import HasReferencedJavaScript
from pyseext.component_query import ComponentQuery
from pyseext.input_helper import InputHelper
from pyseext.menu_helper import MenuHelper
from pyseext.store_helper import StoreHelper
class GridHelper(HasReferencedJavaScript):
"""A class to help with interacting with Ext grid panels"""
# Public class properties
GRID_CQ: str = "gridpanel"
"""The component query to use to find a grid panel"""
# Private class variables
_GET_COLUMN_HEADER_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getColumnHeader('{grid_cq}', '{column_text_or_data_index}')"
"""The script template to use to call the JavaScript method PySeExt.GridHelper.getColumnHeader
Requires the inserts: {grid_cq}, {column_text_or_data_index}"""
_GET_COLUMN_HEADER_TRIGGER_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getColumnHeaderTrigger('{grid_cq}', '{column_text_or_data_index}')"
"""The script template to use to call the JavaScript method PySeExt.GridHelper.getColumnHeaderTrigger
Requires the inserts: {grid_cq}, {column_text_or_data_index}"""
_CLEAR_SELECTION_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.clearSelection('{grid_cq}')"
"""The script template to use to call the JavaScript method PySeExt.GridHelper.clearSelection
Requires the inserts: {grid_cq}"""
_GET_ROW_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getRow('{grid_cq}', {row_data})"
"""The script template to use to call the JavaScript method PySeExt.GridHelper.getRow
Requires the inserts: {grid_cq}, {row_data}"""
def __init__(self, driver: WebDriver):
"""Initialises an instance of this class
Args:
driver (WebDriver): The webdriver to use
"""
# Instance variables
self._logger = logging.getLogger(__name__)
"""The Logger instance for this class instance"""
self._driver = driver
"""The WebDriver instance for this class instance"""
self._cq = ComponentQuery(driver)
"""The `ComponentQuery` instance for this class instance"""
self._input_helper = InputHelper(driver)
"""The `InputHelper` instance for this class instance"""
self._menu_helper = MenuHelper(driver)
"""The `MenuHelper` instance for this class instance"""
self._store_helper = StoreHelper(driver)
"""The `StoreHelper` instance for this class instance"""
self._action_chains = ActionChains(driver)
"""The ActionChains instance for this class instance"""
# Initialise our base class
super().__init__(driver, self._logger)
def get_column_header(self, grid_cq: str, column_text_or_data_index: str) -> WebElement:
"""Gets the element for the specified column header
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
Returns:
WebElement: The DOM element for the column header
"""
# Check grid can be found and is visible
self._cq.wait_for_single_query_visible(grid_cq)
script = self._GET_COLUMN_HEADER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index)
self.ensure_javascript_loaded()
column_header = self._driver.execute_script(script)
if column_header:
return column_header
raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index)
def is_column_visible(self, grid_cq: str, column_text_or_data_index: str) -> bool:
"""Determines whether the specified column is visible,
Throws a ColumnNotFoundException if the column does not exist.
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
Returns:
True if the column is visible, False otherwise.
"""
return self.get_column_header(grid_cq, column_text_or_data_index).is_displayed()
def is_column_hidden(self, grid_cq: str, column_text_or_data_index: str) -> bool:
"""Determines whether the specified column is hidden.
Throws a ColumnNotFoundException if the column does not exist.
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
Returns:
True if the column is hidden, False otherwise.
"""
return not self.get_column_header(grid_cq, column_text_or_data_index).is_displayed()
def check_columns_are_visible(self, grid_cq: str, column_text_or_data_indexes: list[str]) -> list[WebElement]:
"""Checks that the specified columns are all visible on the specified grid.
Throws a ColumnNotFoundException if the column does not exist.
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check
Returns:
An array of columns that are not visible, if any.
"""
columns_not_visible = []
for column_text_or_data_index in column_text_or_data_indexes:
is_visible = self.is_column_visible(grid_cq, column_text_or_data_index)
if not is_visible:
columns_not_visible.append(column_text_or_data_index)
return columns_not_visible
def check_columns_are_hidden(self, grid_cq: str, column_texts_or_data_indexes: list[str]) -> list[WebElement]:
"""Checks that the specified columns are all hidden on the specified grid.
Throws a ColumnNotFoundException if the column does not exist.
Args:
grid_cq (str): The component query for the owning grid
column_texts_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check
Returns:
An array of columns that are not hidden, if any.
"""
column_not_hidden = []
for column_text_or_data_index in column_texts_or_data_indexes:
is_hidden = self.is_column_hidden(grid_cq, column_text_or_data_index)
if not is_hidden:
column_not_hidden.append(column_text_or_data_index)
return column_not_hidden
def click_column_header(self, grid_cq: str, column_text_or_data_index: str):
"""Clicks on the specified column header.
The column must be visible.
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
"""
column_header = self.get_column_header(grid_cq, column_text_or_data_index)
self._logger.info("Clicking column header '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq)
self._action_chains.move_to_element(column_header)
self._action_chains.click()
self._action_chains.perform()
def get_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str) -> WebElement:
"""Gets the element for the specified column header's trigger
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
Returns:
WebElement: The DOM element for the column header trigger.
"""
# Check grid can be found and is visible
self._cq.wait_for_single_query_visible(grid_cq)
script = self._GET_COLUMN_HEADER_TRIGGER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index)
self.ensure_javascript_loaded()
column_header_trigger = self._driver.execute_script(script)
if column_header_trigger:
return column_header_trigger
raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index)
def click_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str):
"""Clicks on the specified column header's trigger
Args:
grid_cq (str): The component query for the owning grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
"""
# We need to move to the header before the trigger becomes interactable
column_header = self.get_column_header(grid_cq, column_text_or_data_index)
self._action_chains.move_to_element(column_header).perform()
column_header_trigger = self.get_column_header_trigger(grid_cq, column_text_or_data_index)
self._logger.info("Clicking column header trigger '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq)
self._action_chains.move_to_element(column_header_trigger)
self._action_chains.click()
self._action_chains.perform()
def filter_string_column(self, grid_cq: str, column_text_or_data_index: str, filter_value: str, wait_for_store_loaded: bool = True, clear_first: bool = False):
"""Filters a string column on a grid for the specified value.
Args:
grid_cq (str): The component query for the owning grid.
column_text_or_data_index (str): The header text or dataIndex of the grid column.
filter_value (str): The value to filter the column by.
wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True.
clear_first (bool, optional): Indicates whether to clear the filter element first. Defaults to False.
"""
self.click_column_header_trigger(grid_cq, column_text_or_data_index)
self._menu_helper.move_to_menu_item_by_text('Filters')
filter_textbox = self._cq.wait_for_single_query_visible('textfield[emptyText="Enter Filter Text..."]')
if wait_for_store_loaded:
self._store_helper.reset_store_load_count(grid_cq)
self._input_helper.type_into_element(filter_textbox, filter_value, clear_first = clear_first)
if wait_for_store_loaded:
self._store_helper.wait_for_store_loaded(grid_cq)
# Close filter and then column menu
self._input_helper.type_escape()
self._input_helper.type_escape()
def filter_list_column(self, grid_cq: str, column_text_or_data_index: str, filter_values_to_toggle: list[str], wait_for_store_loaded: bool = True):
"""Toggles the values on a list filtered column on a grid.
Args:
grid_cq (str): The component query for the owning grid.
column_text_or_data_index (str): The header text or dataIndex of the grid column.
filter_values_to_toggle (list[str]): The filter values to toggle.
wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True.
"""
self.click_column_header_trigger(grid_cq, column_text_or_data_index)
self._menu_helper.move_to_menu_item_by_text('Filters')
if wait_for_store_loaded:
self._store_helper.reset_store_load_count(grid_cq)
for filter_value_to_toggle in filter_values_to_toggle:
self._menu_helper.click_menu_item_by_text(filter_value_to_toggle)
if wait_for_store_loaded:
self._store_helper.wait_for_store_loaded(grid_cq)
# Close filter and then column menu
self._input_helper.type_escape()
self._input_helper.type_escape()
def filter_number_column(self,
grid_cq: str,
column_text_or_data_index: str,
equal_to: Union[None, float] = None,
less_than: Union[None, float] = None,
greater_than: Union[None, float] = None,
wait_for_store_loaded: bool = True):
"""Filters a number column on a grid for the specified values.
Args:
grid_cq (str): The component query for the owning grid.
column_text_or_data_index (str): The header text or dataIndex of the grid column.
filter_values_to_toggle (list[str]): The filter values to toggle.
wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True.
"""
self.click_column_header_trigger(grid_cq, column_text_or_data_index)
self._menu_helper.move_to_menu_item_by_text('Filters')
if wait_for_store_loaded:
self._store_helper.reset_store_load_count(grid_cq)
filter_textboxes = self._cq.wait_for_query('textfield[emptyText="Enter Number..."]')
# clear_first does not work with these :/
# Double-clicking then either typing or deleting should do it.
self._action_chains.move_to_element(filter_textboxes[0])
self._action_chains.double_click()
self._action_chains.perform()
if less_than is not None:
self._input_helper.type_into_element(filter_textboxes[0], less_than, clear_first = False)
else:
self._input_helper.type_delete()
self._action_chains.move_to_element(filter_textboxes[1])
self._action_chains.double_click()
self._action_chains.perform()
if greater_than is not None:
self._input_helper.type_into_element(filter_textboxes[1], greater_than, clear_first = False)
else:
self._input_helper.type_delete()
self._action_chains.move_to_element(filter_textboxes[2])
self._action_chains.double_click()
self._action_chains.perform()
if equal_to is not None:
self._input_helper.type_into_element(filter_textboxes[2], equal_to, clear_first = False)
else:
self._input_helper.type_delete()
if wait_for_store_loaded:
self._store_helper.wait_for_store_loaded(grid_cq)
# Close filter and then column menu
self._input_helper.type_escape()
self._input_helper.type_escape()
def toggle_column_filter(self, grid_cq: str, column_text_or_data_index: str, wait_for_store_loaded: bool = True):
"""Toggles the filter on a column by clicking on the filters element.
Args:
grid_cq (str): The component query for the owning grid.
column_text_or_data_index (str): The header text or dataIndex of the grid column.
wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True.
"""
self.click_column_header_trigger(grid_cq, column_text_or_data_index)
filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Filters')
if wait_for_store_loaded:
self._store_helper.reset_store_load_count(grid_cq)
self._action_chains.move_to_element(filter_menu_item)
self._action_chains.click()
self._action_chains.perform()
if wait_for_store_loaded:
self._store_helper.wait_for_store_loaded(grid_cq)
# Close filter and then column menu
self._input_helper.type_escape()
self._input_helper.type_escape()
def clear_selection(self, grid_cq: str):
""" Clears the current selection.
Useful if want to quickly refresh a grid without having to process all the events.
This will only work if the grid supports deselection.
Args:
grid_cq (str): The component query for the grid
"""
# Check grid can be found and is visible
self._cq.wait_for_single_query_visible(grid_cq)
self._logger.info("Clearing selection on grid with CQ '%s'", grid_cq)
script = self._CLEAR_SELECTION_TEMPLATE.format(grid_cq=grid_cq)
self.ensure_javascript_loaded()
self._driver.execute_script(script)
def get_row(self, grid_cq: str, row_data: Union[int, dict], should_throw_exception: bool = True) -> WebElement:
""" Gets the element for the row with the specified data or index in the grid.
The grid must be visible.
Args:
grid_cq (str): The component query for the grid
row_data (Union[int, dict]): The row data or index for the record to be found.
should_throw_exception (bool): Indicates whether this method should throw an exception
if the row is not found. Defaults to True.
Returns:
WebElement: The DOM element for the row or None if not found (and not thrown)
"""
# Check grid can be found and is visible
self._cq.wait_for_single_query_visible(grid_cq)
script = self._GET_ROW_TEMPLATE.format(grid_cq=grid_cq, row_data=row_data)
self.ensure_javascript_loaded()
row = self._driver.execute_script(script)
if row or not should_throw_exception:
return row
raise GridHelper.RowNotFoundException(grid_cq, row_data)
def click_row(self, grid_cq: str, row_data: Union[int, dict]):
""" Clicks the row with the specified data or index in the grid.
The grid must be visible.
Args:
grid_cq (str): The component query for the grid
row_data (Union[int, dict]): The row data or index for the record to be found and clicked.
"""
# Check grid can be found and is visible
row = self.get_row(grid_cq, row_data)
self._logger.info("Clicking clicking row '%s' on grid with CQ '%s'", row_data, grid_cq)
self._action_chains.move_to_element(row)
self._action_chains.click()
self._action_chains.perform()
def wait_for_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60) -> WebElement:
"""Waits for the specified row to appear in the grid, reloading the store until
it is found, or until the timeout is hit.
Args:
grid_cq (str): The component query for the grid.
row_data (Union[int, dict]): The row data or index of the record we are waiting for.
timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60.
Returns:
WebElement: The DOM element for the row
"""
WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data))
return self.get_row(grid_cq, row_data)
def wait_to_click_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60):
"""Waits for the specified row to appear in the grid, reloading the store until
it is found, or until the timeout is hit.
Once we have found the row it is clicked.
Args:
grid_cq (str): The component query for the grid.
row_data (Union[int, dict]): The row data or index of the record we are waiting for.
timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60.
"""
WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data))
self.click_row(grid_cq, row_data)
def toggle_columns(self, grid_cq: str, column_text_or_data_index: str, columns_to_toggle: list[str]):
"""Toggles a list of columns on the specified grid.
Any that are visible will be hidden, and any that a currently hidden will be shown.
Args:
grid_cq (str): The component query for the owning grid.
column_text_or_data_index (str): The header text or dataIndex of the grid column to use for the interaction.
columns_to_toggle (list[str]): The list of columns to toggle.
"""
# Use first visible column for our interaction
self.click_column_header_trigger(grid_cq, column_text_or_data_index)
# Get the 'Columns' menu itema and move to it, so that submenu shows.
filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Columns')
self._action_chains.move_to_element(filter_menu_item)
self._action_chains.perform()
for column in columns_to_toggle:
# FIXME: If column is off the bottom of the list this'll blow up...
column_to_click = self._cq.wait_for_single_query_visible(f'menucheckitem[text="{column}"]')
self._action_chains.move_to_element(column_to_click)
self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM))
self._action_chains.click()
self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM))
self._action_chains.perform()
# Close columns submenu, and grid menu.
self._input_helper.type_escape()
self._input_helper.type_escape()
class ColumnNotFoundException(Exception):
"""Exception class thrown when we failed to find the specified column"""
def __init__(self,
grid_cq: str,
column_text_or_data_index: str,
message: str = "Failed to find column with text (or dataIndex) '{column_text_or_data_index}' on grid with CQ '{grid_cq}'."):
"""Initialises an instance of this exception
Args:
grid_cq (str): The CQ used to find the grid
column_text_or_data_index (str): The header text or dataIndex of the grid column
message (str, optional): The exception message. Defaults to "Failed to find column with text (or dataIndex) '{column_text_or_data_index}' on grid with CQ '{grid_cq}'.".
"""
self.message = message
self._grid_cq = grid_cq
self._column_text_or_data_index = column_text_or_data_index
super().__init__(self.message)
def __str__(self):
"""Returns a string representation of this exception"""
return self.message.format(column_text_or_data_index=self._column_text_or_data_index, grid_cq=self._grid_cq)
class RowNotFoundException(Exception):
"""Exception class thrown when we failed to find the specified row"""
def __init__(self,
grid_cq: str,
row_data: Union[int, dict],
message: str = "Failed to find row with data (or index) '{row_data}' on grid with CQ '{grid_cq}'."):
"""Initialises an instance of this exception
Args:
grid_cq (str): The CQ used to find the grid
row_data (Union[int, dict]): The row data or index for the record
message (str, optional): The exception message. Defaults to "Failed to find row with data (or index) '{row_data}' on grid with CQ '{grid_cq}'.".
"""
self.message = message
self._grid_cq = grid_cq
self._row_data = row_data
super().__init__(self.message)
def __str__(self):
"""Returns a string representation of this exception"""
return self.message.format(row_data=self._row_data, grid_cq=self._grid_cq)
class RowFoundExpectation:
""" An expectation for checking that a row has been found"""
def __init__(self, grid_cq: str, row_data: Union[int, dict]):
"""Initialises an instance of this class.
Args:
grid_cq (str): The component query for the grid.
row_data (Union[int, dict]): The row data or index of the record we are waiting for.
"""
self._grid_cq = grid_cq
self._row_data = row_data
def __call__(self, driver):
"""Method that determines whether a row was found.
If the row is not found the grid is refreshed and the load waited for.
"""
grid_helper = GridHelper(driver)
row = grid_helper.get_row(self._grid_cq, self._row_data, False)
if row:
return True
# Trigger a reload, and wait for it to complete
store_helper = StoreHelper(driver)
store_helper.reset_store_load_count(self._grid_cq)
store_helper.trigger_reload(self._grid_cq)
store_helper.wait_for_store_loaded(self._grid_cq)
return False
Classes
class GridHelper (driver: selenium.webdriver.remote.webdriver.WebDriver)
-
A class to help with interacting with Ext grid panels
Initialises an instance of this class
Args
driver
:WebDriver
- The webdriver to use
Expand source code
class GridHelper(HasReferencedJavaScript): """A class to help with interacting with Ext grid panels""" # Public class properties GRID_CQ: str = "gridpanel" """The component query to use to find a grid panel""" # Private class variables _GET_COLUMN_HEADER_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getColumnHeader('{grid_cq}', '{column_text_or_data_index}')" """The script template to use to call the JavaScript method PySeExt.GridHelper.getColumnHeader Requires the inserts: {grid_cq}, {column_text_or_data_index}""" _GET_COLUMN_HEADER_TRIGGER_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getColumnHeaderTrigger('{grid_cq}', '{column_text_or_data_index}')" """The script template to use to call the JavaScript method PySeExt.GridHelper.getColumnHeaderTrigger Requires the inserts: {grid_cq}, {column_text_or_data_index}""" _CLEAR_SELECTION_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.clearSelection('{grid_cq}')" """The script template to use to call the JavaScript method PySeExt.GridHelper.clearSelection Requires the inserts: {grid_cq}""" _GET_ROW_TEMPLATE: str = "return globalThis.PySeExt.GridHelper.getRow('{grid_cq}', {row_data})" """The script template to use to call the JavaScript method PySeExt.GridHelper.getRow Requires the inserts: {grid_cq}, {row_data}""" def __init__(self, driver: WebDriver): """Initialises an instance of this class Args: driver (WebDriver): The webdriver to use """ # Instance variables self._logger = logging.getLogger(__name__) """The Logger instance for this class instance""" self._driver = driver """The WebDriver instance for this class instance""" self._cq = ComponentQuery(driver) """The `ComponentQuery` instance for this class instance""" self._input_helper = InputHelper(driver) """The `InputHelper` instance for this class instance""" self._menu_helper = MenuHelper(driver) """The `MenuHelper` instance for this class instance""" self._store_helper = StoreHelper(driver) """The `StoreHelper` instance for this class instance""" self._action_chains = ActionChains(driver) """The ActionChains instance for this class instance""" # Initialise our base class super().__init__(driver, self._logger) def get_column_header(self, grid_cq: str, column_text_or_data_index: str) -> WebElement: """Gets the element for the specified column header Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: WebElement: The DOM element for the column header """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_COLUMN_HEADER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index) self.ensure_javascript_loaded() column_header = self._driver.execute_script(script) if column_header: return column_header raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index) def is_column_visible(self, grid_cq: str, column_text_or_data_index: str) -> bool: """Determines whether the specified column is visible, Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: True if the column is visible, False otherwise. """ return self.get_column_header(grid_cq, column_text_or_data_index).is_displayed() def is_column_hidden(self, grid_cq: str, column_text_or_data_index: str) -> bool: """Determines whether the specified column is hidden. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: True if the column is hidden, False otherwise. """ return not self.get_column_header(grid_cq, column_text_or_data_index).is_displayed() def check_columns_are_visible(self, grid_cq: str, column_text_or_data_indexes: list[str]) -> list[WebElement]: """Checks that the specified columns are all visible on the specified grid. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check Returns: An array of columns that are not visible, if any. """ columns_not_visible = [] for column_text_or_data_index in column_text_or_data_indexes: is_visible = self.is_column_visible(grid_cq, column_text_or_data_index) if not is_visible: columns_not_visible.append(column_text_or_data_index) return columns_not_visible def check_columns_are_hidden(self, grid_cq: str, column_texts_or_data_indexes: list[str]) -> list[WebElement]: """Checks that the specified columns are all hidden on the specified grid. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_texts_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check Returns: An array of columns that are not hidden, if any. """ column_not_hidden = [] for column_text_or_data_index in column_texts_or_data_indexes: is_hidden = self.is_column_hidden(grid_cq, column_text_or_data_index) if not is_hidden: column_not_hidden.append(column_text_or_data_index) return column_not_hidden def click_column_header(self, grid_cq: str, column_text_or_data_index: str): """Clicks on the specified column header. The column must be visible. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column """ column_header = self.get_column_header(grid_cq, column_text_or_data_index) self._logger.info("Clicking column header '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq) self._action_chains.move_to_element(column_header) self._action_chains.click() self._action_chains.perform() def get_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str) -> WebElement: """Gets the element for the specified column header's trigger Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: WebElement: The DOM element for the column header trigger. """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_COLUMN_HEADER_TRIGGER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index) self.ensure_javascript_loaded() column_header_trigger = self._driver.execute_script(script) if column_header_trigger: return column_header_trigger raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index) def click_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str): """Clicks on the specified column header's trigger Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column """ # We need to move to the header before the trigger becomes interactable column_header = self.get_column_header(grid_cq, column_text_or_data_index) self._action_chains.move_to_element(column_header).perform() column_header_trigger = self.get_column_header_trigger(grid_cq, column_text_or_data_index) self._logger.info("Clicking column header trigger '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq) self._action_chains.move_to_element(column_header_trigger) self._action_chains.click() self._action_chains.perform() def filter_string_column(self, grid_cq: str, column_text_or_data_index: str, filter_value: str, wait_for_store_loaded: bool = True, clear_first: bool = False): """Filters a string column on a grid for the specified value. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_value (str): The value to filter the column by. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. clear_first (bool, optional): Indicates whether to clear the filter element first. Defaults to False. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') filter_textbox = self._cq.wait_for_single_query_visible('textfield[emptyText="Enter Filter Text..."]') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) self._input_helper.type_into_element(filter_textbox, filter_value, clear_first = clear_first) if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape() def filter_list_column(self, grid_cq: str, column_text_or_data_index: str, filter_values_to_toggle: list[str], wait_for_store_loaded: bool = True): """Toggles the values on a list filtered column on a grid. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_values_to_toggle (list[str]): The filter values to toggle. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) for filter_value_to_toggle in filter_values_to_toggle: self._menu_helper.click_menu_item_by_text(filter_value_to_toggle) if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape() def filter_number_column(self, grid_cq: str, column_text_or_data_index: str, equal_to: Union[None, float] = None, less_than: Union[None, float] = None, greater_than: Union[None, float] = None, wait_for_store_loaded: bool = True): """Filters a number column on a grid for the specified values. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_values_to_toggle (list[str]): The filter values to toggle. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) filter_textboxes = self._cq.wait_for_query('textfield[emptyText="Enter Number..."]') # clear_first does not work with these :/ # Double-clicking then either typing or deleting should do it. self._action_chains.move_to_element(filter_textboxes[0]) self._action_chains.double_click() self._action_chains.perform() if less_than is not None: self._input_helper.type_into_element(filter_textboxes[0], less_than, clear_first = False) else: self._input_helper.type_delete() self._action_chains.move_to_element(filter_textboxes[1]) self._action_chains.double_click() self._action_chains.perform() if greater_than is not None: self._input_helper.type_into_element(filter_textboxes[1], greater_than, clear_first = False) else: self._input_helper.type_delete() self._action_chains.move_to_element(filter_textboxes[2]) self._action_chains.double_click() self._action_chains.perform() if equal_to is not None: self._input_helper.type_into_element(filter_textboxes[2], equal_to, clear_first = False) else: self._input_helper.type_delete() if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape() def toggle_column_filter(self, grid_cq: str, column_text_or_data_index: str, wait_for_store_loaded: bool = True): """Toggles the filter on a column by clicking on the filters element. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) self._action_chains.move_to_element(filter_menu_item) self._action_chains.click() self._action_chains.perform() if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape() def clear_selection(self, grid_cq: str): """ Clears the current selection. Useful if want to quickly refresh a grid without having to process all the events. This will only work if the grid supports deselection. Args: grid_cq (str): The component query for the grid """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) self._logger.info("Clearing selection on grid with CQ '%s'", grid_cq) script = self._CLEAR_SELECTION_TEMPLATE.format(grid_cq=grid_cq) self.ensure_javascript_loaded() self._driver.execute_script(script) def get_row(self, grid_cq: str, row_data: Union[int, dict], should_throw_exception: bool = True) -> WebElement: """ Gets the element for the row with the specified data or index in the grid. The grid must be visible. Args: grid_cq (str): The component query for the grid row_data (Union[int, dict]): The row data or index for the record to be found. should_throw_exception (bool): Indicates whether this method should throw an exception if the row is not found. Defaults to True. Returns: WebElement: The DOM element for the row or None if not found (and not thrown) """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_ROW_TEMPLATE.format(grid_cq=grid_cq, row_data=row_data) self.ensure_javascript_loaded() row = self._driver.execute_script(script) if row or not should_throw_exception: return row raise GridHelper.RowNotFoundException(grid_cq, row_data) def click_row(self, grid_cq: str, row_data: Union[int, dict]): """ Clicks the row with the specified data or index in the grid. The grid must be visible. Args: grid_cq (str): The component query for the grid row_data (Union[int, dict]): The row data or index for the record to be found and clicked. """ # Check grid can be found and is visible row = self.get_row(grid_cq, row_data) self._logger.info("Clicking clicking row '%s' on grid with CQ '%s'", row_data, grid_cq) self._action_chains.move_to_element(row) self._action_chains.click() self._action_chains.perform() def wait_for_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60) -> WebElement: """Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit. Args: grid_cq (str): The component query for the grid. row_data (Union[int, dict]): The row data or index of the record we are waiting for. timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60. Returns: WebElement: The DOM element for the row """ WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data)) return self.get_row(grid_cq, row_data) def wait_to_click_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60): """Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit. Once we have found the row it is clicked. Args: grid_cq (str): The component query for the grid. row_data (Union[int, dict]): The row data or index of the record we are waiting for. timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60. """ WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data)) self.click_row(grid_cq, row_data) def toggle_columns(self, grid_cq: str, column_text_or_data_index: str, columns_to_toggle: list[str]): """Toggles a list of columns on the specified grid. Any that are visible will be hidden, and any that a currently hidden will be shown. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column to use for the interaction. columns_to_toggle (list[str]): The list of columns to toggle. """ # Use first visible column for our interaction self.click_column_header_trigger(grid_cq, column_text_or_data_index) # Get the 'Columns' menu itema and move to it, so that submenu shows. filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Columns') self._action_chains.move_to_element(filter_menu_item) self._action_chains.perform() for column in columns_to_toggle: # FIXME: If column is off the bottom of the list this'll blow up... column_to_click = self._cq.wait_for_single_query_visible(f'menucheckitem[text="{column}"]') self._action_chains.move_to_element(column_to_click) self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM)) self._action_chains.click() self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM)) self._action_chains.perform() # Close columns submenu, and grid menu. self._input_helper.type_escape() self._input_helper.type_escape() class ColumnNotFoundException(Exception): """Exception class thrown when we failed to find the specified column""" def __init__(self, grid_cq: str, column_text_or_data_index: str, message: str = "Failed to find column with text (or dataIndex) '{column_text_or_data_index}' on grid with CQ '{grid_cq}'."): """Initialises an instance of this exception Args: grid_cq (str): The CQ used to find the grid column_text_or_data_index (str): The header text or dataIndex of the grid column message (str, optional): The exception message. Defaults to "Failed to find column with text (or dataIndex) '{column_text_or_data_index}' on grid with CQ '{grid_cq}'.". """ self.message = message self._grid_cq = grid_cq self._column_text_or_data_index = column_text_or_data_index super().__init__(self.message) def __str__(self): """Returns a string representation of this exception""" return self.message.format(column_text_or_data_index=self._column_text_or_data_index, grid_cq=self._grid_cq) class RowNotFoundException(Exception): """Exception class thrown when we failed to find the specified row""" def __init__(self, grid_cq: str, row_data: Union[int, dict], message: str = "Failed to find row with data (or index) '{row_data}' on grid with CQ '{grid_cq}'."): """Initialises an instance of this exception Args: grid_cq (str): The CQ used to find the grid row_data (Union[int, dict]): The row data or index for the record message (str, optional): The exception message. Defaults to "Failed to find row with data (or index) '{row_data}' on grid with CQ '{grid_cq}'.". """ self.message = message self._grid_cq = grid_cq self._row_data = row_data super().__init__(self.message) def __str__(self): """Returns a string representation of this exception""" return self.message.format(row_data=self._row_data, grid_cq=self._grid_cq) class RowFoundExpectation: """ An expectation for checking that a row has been found""" def __init__(self, grid_cq: str, row_data: Union[int, dict]): """Initialises an instance of this class. Args: grid_cq (str): The component query for the grid. row_data (Union[int, dict]): The row data or index of the record we are waiting for. """ self._grid_cq = grid_cq self._row_data = row_data def __call__(self, driver): """Method that determines whether a row was found. If the row is not found the grid is refreshed and the load waited for. """ grid_helper = GridHelper(driver) row = grid_helper.get_row(self._grid_cq, self._row_data, False) if row: return True # Trigger a reload, and wait for it to complete store_helper = StoreHelper(driver) store_helper.reset_store_load_count(self._grid_cq) store_helper.trigger_reload(self._grid_cq) store_helper.wait_for_store_loaded(self._grid_cq) return False
Ancestors
Class variables
var ColumnNotFoundException
-
Exception class thrown when we failed to find the specified column
var GRID_CQ : str
-
The component query to use to find a grid panel
var RowFoundExpectation
-
An expectation for checking that a row has been found
var RowNotFoundException
-
Exception class thrown when we failed to find the specified row
Methods
-
Checks that the specified columns are all hidden on the specified grid. Throws a ColumnNotFoundException if the column does not exist.
Args
grid_cq
:str
- The component query for the owning grid
column_texts_or_data_indexes
:list[str]
- An array containing the header text or dataIndex of the grid columns to check
Returns
An array of columns that are not hidden, if any.
Expand source code
def check_columns_are_hidden(self, grid_cq: str, column_texts_or_data_indexes: list[str]) -> list[WebElement]: """Checks that the specified columns are all hidden on the specified grid. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_texts_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check Returns: An array of columns that are not hidden, if any. """ column_not_hidden = [] for column_text_or_data_index in column_texts_or_data_indexes: is_hidden = self.is_column_hidden(grid_cq, column_text_or_data_index) if not is_hidden: column_not_hidden.append(column_text_or_data_index) return column_not_hidden
def check_columns_are_visible(self, grid_cq: str, column_text_or_data_indexes: list[str]) ‑> list[selenium.webdriver.remote.webelement.WebElement]
-
Checks that the specified columns are all visible on the specified grid. Throws a ColumnNotFoundException if the column does not exist.
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_indexes
:list[str]
- An array containing the header text or dataIndex of the grid columns to check
Returns
An array of columns that are not visible, if any.
Expand source code
def check_columns_are_visible(self, grid_cq: str, column_text_or_data_indexes: list[str]) -> list[WebElement]: """Checks that the specified columns are all visible on the specified grid. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_indexes (list[str]): An array containing the header text or dataIndex of the grid columns to check Returns: An array of columns that are not visible, if any. """ columns_not_visible = [] for column_text_or_data_index in column_text_or_data_indexes: is_visible = self.is_column_visible(grid_cq, column_text_or_data_index) if not is_visible: columns_not_visible.append(column_text_or_data_index) return columns_not_visible
def clear_selection(self, grid_cq: str)
-
Clears the current selection.
Useful if want to quickly refresh a grid without having to process all the events. This will only work if the grid supports deselection.
Args
grid_cq
:str
- The component query for the grid
Expand source code
def clear_selection(self, grid_cq: str): """ Clears the current selection. Useful if want to quickly refresh a grid without having to process all the events. This will only work if the grid supports deselection. Args: grid_cq (str): The component query for the grid """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) self._logger.info("Clearing selection on grid with CQ '%s'", grid_cq) script = self._CLEAR_SELECTION_TEMPLATE.format(grid_cq=grid_cq) self.ensure_javascript_loaded() self._driver.execute_script(script)
def click_column_header(self, grid_cq: str, column_text_or_data_index: str)
-
Clicks on the specified column header. The column must be visible.
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Expand source code
def click_column_header(self, grid_cq: str, column_text_or_data_index: str): """Clicks on the specified column header. The column must be visible. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column """ column_header = self.get_column_header(grid_cq, column_text_or_data_index) self._logger.info("Clicking column header '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq) self._action_chains.move_to_element(column_header) self._action_chains.click() self._action_chains.perform()
def click_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str)
-
Clicks on the specified column header's trigger
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Expand source code
def click_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str): """Clicks on the specified column header's trigger Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column """ # We need to move to the header before the trigger becomes interactable column_header = self.get_column_header(grid_cq, column_text_or_data_index) self._action_chains.move_to_element(column_header).perform() column_header_trigger = self.get_column_header_trigger(grid_cq, column_text_or_data_index) self._logger.info("Clicking column header trigger '%s' on grid with CQ '%s'", column_text_or_data_index, grid_cq) self._action_chains.move_to_element(column_header_trigger) self._action_chains.click() self._action_chains.perform()
def click_row(self, grid_cq: str, row_data: Union[int, dict])
-
Clicks the row with the specified data or index in the grid.
The grid must be visible.
Args
grid_cq
:str
- The component query for the grid
row_data
:Union[int, dict]
- The row data or index for the record to be found and clicked.
Expand source code
def click_row(self, grid_cq: str, row_data: Union[int, dict]): """ Clicks the row with the specified data or index in the grid. The grid must be visible. Args: grid_cq (str): The component query for the grid row_data (Union[int, dict]): The row data or index for the record to be found and clicked. """ # Check grid can be found and is visible row = self.get_row(grid_cq, row_data) self._logger.info("Clicking clicking row '%s' on grid with CQ '%s'", row_data, grid_cq) self._action_chains.move_to_element(row) self._action_chains.click() self._action_chains.perform()
def filter_list_column(self, grid_cq: str, column_text_or_data_index: str, filter_values_to_toggle: list[str], wait_for_store_loaded: bool = True)
-
Toggles the values on a list filtered column on a grid.
Args
grid_cq
:str
- The component query for the owning grid.
column_text_or_data_index
:str
- The header text or dataIndex of the grid column.
filter_values_to_toggle
:list[str]
- The filter values to toggle.
wait_for_store_loaded
:bool
, optional- Indicates whether to wait for the store to load. Defaults to True.
Expand source code
def filter_list_column(self, grid_cq: str, column_text_or_data_index: str, filter_values_to_toggle: list[str], wait_for_store_loaded: bool = True): """Toggles the values on a list filtered column on a grid. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_values_to_toggle (list[str]): The filter values to toggle. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) for filter_value_to_toggle in filter_values_to_toggle: self._menu_helper.click_menu_item_by_text(filter_value_to_toggle) if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape()
def filter_number_column(self, grid_cq: str, column_text_or_data_index: str, equal_to: Optional[None] = None, less_than: Optional[None] = None, greater_than: Optional[None] = None, wait_for_store_loaded: bool = True)
-
Filters a number column on a grid for the specified values.
Args
grid_cq
:str
- The component query for the owning grid.
column_text_or_data_index
:str
- The header text or dataIndex of the grid column.
filter_values_to_toggle
:list[str]
- The filter values to toggle.
wait_for_store_loaded
:bool
, optional- Indicates whether to wait for the store to load. Defaults to True.
Expand source code
def filter_number_column(self, grid_cq: str, column_text_or_data_index: str, equal_to: Union[None, float] = None, less_than: Union[None, float] = None, greater_than: Union[None, float] = None, wait_for_store_loaded: bool = True): """Filters a number column on a grid for the specified values. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_values_to_toggle (list[str]): The filter values to toggle. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) filter_textboxes = self._cq.wait_for_query('textfield[emptyText="Enter Number..."]') # clear_first does not work with these :/ # Double-clicking then either typing or deleting should do it. self._action_chains.move_to_element(filter_textboxes[0]) self._action_chains.double_click() self._action_chains.perform() if less_than is not None: self._input_helper.type_into_element(filter_textboxes[0], less_than, clear_first = False) else: self._input_helper.type_delete() self._action_chains.move_to_element(filter_textboxes[1]) self._action_chains.double_click() self._action_chains.perform() if greater_than is not None: self._input_helper.type_into_element(filter_textboxes[1], greater_than, clear_first = False) else: self._input_helper.type_delete() self._action_chains.move_to_element(filter_textboxes[2]) self._action_chains.double_click() self._action_chains.perform() if equal_to is not None: self._input_helper.type_into_element(filter_textboxes[2], equal_to, clear_first = False) else: self._input_helper.type_delete() if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape()
def filter_string_column(self, grid_cq: str, column_text_or_data_index: str, filter_value: str, wait_for_store_loaded: bool = True, clear_first: bool = False)
-
Filters a string column on a grid for the specified value.
Args
grid_cq
:str
- The component query for the owning grid.
column_text_or_data_index
:str
- The header text or dataIndex of the grid column.
filter_value
:str
- The value to filter the column by.
wait_for_store_loaded
:bool
, optional- Indicates whether to wait for the store to load. Defaults to True.
clear_first
:bool
, optional- Indicates whether to clear the filter element first. Defaults to False.
Expand source code
def filter_string_column(self, grid_cq: str, column_text_or_data_index: str, filter_value: str, wait_for_store_loaded: bool = True, clear_first: bool = False): """Filters a string column on a grid for the specified value. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. filter_value (str): The value to filter the column by. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. clear_first (bool, optional): Indicates whether to clear the filter element first. Defaults to False. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) self._menu_helper.move_to_menu_item_by_text('Filters') filter_textbox = self._cq.wait_for_single_query_visible('textfield[emptyText="Enter Filter Text..."]') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) self._input_helper.type_into_element(filter_textbox, filter_value, clear_first = clear_first) if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape()
def get_column_header(self, grid_cq: str, column_text_or_data_index: str) ‑> selenium.webdriver.remote.webelement.WebElement
-
Gets the element for the specified column header
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Returns
WebElement
- The DOM element for the column header
Expand source code
def get_column_header(self, grid_cq: str, column_text_or_data_index: str) -> WebElement: """Gets the element for the specified column header Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: WebElement: The DOM element for the column header """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_COLUMN_HEADER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index) self.ensure_javascript_loaded() column_header = self._driver.execute_script(script) if column_header: return column_header raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index)
def get_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str) ‑> selenium.webdriver.remote.webelement.WebElement
-
Gets the element for the specified column header's trigger
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Returns
WebElement
- The DOM element for the column header trigger.
Expand source code
def get_column_header_trigger(self, grid_cq: str, column_text_or_data_index: str) -> WebElement: """Gets the element for the specified column header's trigger Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: WebElement: The DOM element for the column header trigger. """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_COLUMN_HEADER_TRIGGER_TEMPLATE.format(grid_cq=grid_cq, column_text_or_data_index=column_text_or_data_index) self.ensure_javascript_loaded() column_header_trigger = self._driver.execute_script(script) if column_header_trigger: return column_header_trigger raise GridHelper.ColumnNotFoundException(grid_cq, column_text_or_data_index)
def get_row(self, grid_cq: str, row_data: Union[int, dict], should_throw_exception: bool = True) ‑> selenium.webdriver.remote.webelement.WebElement
-
Gets the element for the row with the specified data or index in the grid.
The grid must be visible.
Args
grid_cq
:str
- The component query for the grid
row_data
:Union[int, dict]
- The row data or index for the record to be found.
should_throw_exception
:bool
- Indicates whether this method should throw an exception if the row is not found. Defaults to True.
Returns
WebElement
- The DOM element for the row or None if not found (and not thrown)
Expand source code
def get_row(self, grid_cq: str, row_data: Union[int, dict], should_throw_exception: bool = True) -> WebElement: """ Gets the element for the row with the specified data or index in the grid. The grid must be visible. Args: grid_cq (str): The component query for the grid row_data (Union[int, dict]): The row data or index for the record to be found. should_throw_exception (bool): Indicates whether this method should throw an exception if the row is not found. Defaults to True. Returns: WebElement: The DOM element for the row or None if not found (and not thrown) """ # Check grid can be found and is visible self._cq.wait_for_single_query_visible(grid_cq) script = self._GET_ROW_TEMPLATE.format(grid_cq=grid_cq, row_data=row_data) self.ensure_javascript_loaded() row = self._driver.execute_script(script) if row or not should_throw_exception: return row raise GridHelper.RowNotFoundException(grid_cq, row_data)
-
Determines whether the specified column is hidden. Throws a ColumnNotFoundException if the column does not exist.
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Returns
True if the column is hidden, False otherwise.
Expand source code
def is_column_hidden(self, grid_cq: str, column_text_or_data_index: str) -> bool: """Determines whether the specified column is hidden. Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: True if the column is hidden, False otherwise. """ return not self.get_column_header(grid_cq, column_text_or_data_index).is_displayed()
def is_column_visible(self, grid_cq: str, column_text_or_data_index: str) ‑> bool
-
Determines whether the specified column is visible, Throws a ColumnNotFoundException if the column does not exist.
Args
grid_cq
:str
- The component query for the owning grid
column_text_or_data_index
:str
- The header text or dataIndex of the grid column
Returns
True if the column is visible, False otherwise.
Expand source code
def is_column_visible(self, grid_cq: str, column_text_or_data_index: str) -> bool: """Determines whether the specified column is visible, Throws a ColumnNotFoundException if the column does not exist. Args: grid_cq (str): The component query for the owning grid column_text_or_data_index (str): The header text or dataIndex of the grid column Returns: True if the column is visible, False otherwise. """ return self.get_column_header(grid_cq, column_text_or_data_index).is_displayed()
def toggle_column_filter(self, grid_cq: str, column_text_or_data_index: str, wait_for_store_loaded: bool = True)
-
Toggles the filter on a column by clicking on the filters element.
Args
grid_cq
:str
- The component query for the owning grid.
column_text_or_data_index
:str
- The header text or dataIndex of the grid column.
wait_for_store_loaded
:bool
, optional- Indicates whether to wait for the store to load. Defaults to True.
Expand source code
def toggle_column_filter(self, grid_cq: str, column_text_or_data_index: str, wait_for_store_loaded: bool = True): """Toggles the filter on a column by clicking on the filters element. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column. wait_for_store_loaded (bool, optional): Indicates whether to wait for the store to load. Defaults to True. """ self.click_column_header_trigger(grid_cq, column_text_or_data_index) filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Filters') if wait_for_store_loaded: self._store_helper.reset_store_load_count(grid_cq) self._action_chains.move_to_element(filter_menu_item) self._action_chains.click() self._action_chains.perform() if wait_for_store_loaded: self._store_helper.wait_for_store_loaded(grid_cq) # Close filter and then column menu self._input_helper.type_escape() self._input_helper.type_escape()
def toggle_columns(self, grid_cq: str, column_text_or_data_index: str, columns_to_toggle: list[str])
-
Toggles a list of columns on the specified grid. Any that are visible will be hidden, and any that a currently hidden will be shown.
Args
grid_cq
:str
- The component query for the owning grid.
column_text_or_data_index
:str
- The header text or dataIndex of the grid column to use for the interaction.
columns_to_toggle
:list[str]
- The list of columns to toggle.
Expand source code
def toggle_columns(self, grid_cq: str, column_text_or_data_index: str, columns_to_toggle: list[str]): """Toggles a list of columns on the specified grid. Any that are visible will be hidden, and any that a currently hidden will be shown. Args: grid_cq (str): The component query for the owning grid. column_text_or_data_index (str): The header text or dataIndex of the grid column to use for the interaction. columns_to_toggle (list[str]): The list of columns to toggle. """ # Use first visible column for our interaction self.click_column_header_trigger(grid_cq, column_text_or_data_index) # Get the 'Columns' menu itema and move to it, so that submenu shows. filter_menu_item = self._menu_helper.try_get_menu_item_by_text('Columns') self._action_chains.move_to_element(filter_menu_item) self._action_chains.perform() for column in columns_to_toggle: # FIXME: If column is off the bottom of the list this'll blow up... column_to_click = self._cq.wait_for_single_query_visible(f'menucheckitem[text="{column}"]') self._action_chains.move_to_element(column_to_click) self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM)) self._action_chains.click() self._action_chains.pause(random.uniform(self._input_helper.INPUT_SLEEP_MINIMUM, self._input_helper.INPUT_SLEEP_MAXIMUM)) self._action_chains.perform() # Close columns submenu, and grid menu. self._input_helper.type_escape() self._input_helper.type_escape()
def wait_for_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60) ‑> selenium.webdriver.remote.webelement.WebElement
-
Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit.
Args
grid_cq
:str
- The component query for the grid.
row_data
:Union[int, dict]
- The row data or index of the record we are waiting for.
timeout
:int
, optional- The number of seconds to wait for the row before erroring. Defaults to 60.
Returns
WebElement
- The DOM element for the row
Expand source code
def wait_for_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60) -> WebElement: """Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit. Args: grid_cq (str): The component query for the grid. row_data (Union[int, dict]): The row data or index of the record we are waiting for. timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60. Returns: WebElement: The DOM element for the row """ WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data)) return self.get_row(grid_cq, row_data)
def wait_to_click_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60)
-
Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit. Once we have found the row it is clicked.
Args
grid_cq
:str
- The component query for the grid.
row_data
:Union[int, dict]
- The row data or index of the record we are waiting for.
timeout
:int
, optional- The number of seconds to wait for the row before erroring. Defaults to 60.
Expand source code
def wait_to_click_row(self, grid_cq: str, row_data: Union[int, dict], timeout: float = 60): """Waits for the specified row to appear in the grid, reloading the store until it is found, or until the timeout is hit. Once we have found the row it is clicked. Args: grid_cq (str): The component query for the grid. row_data (Union[int, dict]): The row data or index of the record we are waiting for. timeout (int, optional): The number of seconds to wait for the row before erroring. Defaults to 60. """ WebDriverWait(self._driver, timeout).until(GridHelper.RowFoundExpectation(grid_cq, row_data)) self.click_row(grid_cq, row_data)
Inherited members