diff --git a/fastrpa/__init__.py b/fastrpa/__init__.py index 1bc5e04..800676b 100644 --- a/fastrpa/__init__.py +++ b/fastrpa/__init__.py @@ -3,6 +3,8 @@ Element, InputElement, FileInputElement, + RadioInputElement, + CheckboxElement, SelectElement, ListElement, ButtonElement, @@ -17,6 +19,8 @@ 'Element', 'InputElement', 'FileInputElement', + 'RadioInputElement', + 'CheckboxElement', 'SelectElement', 'ListElement', 'ButtonElement', diff --git a/fastrpa/app.py b/fastrpa/app.py index 4b9a753..97e0308 100644 --- a/fastrpa/app.py +++ b/fastrpa/app.py @@ -18,6 +18,7 @@ from fastrpa.core.tabs import Tabs from fastrpa.exceptions import ElementNotCompatibleException from fastrpa.core.elements import ( + CheckboxElement, Element, ImageElement, InputElement, @@ -25,6 +26,7 @@ ButtonElement, FormElement, ListElement, + RadioInputElement, TableElement, SelectElement, ) @@ -117,15 +119,21 @@ def input(self, xpath: str, wait: bool = True) -> InputElement: def file_input(self, xpath: str, wait: bool = True) -> FileInputElement: return self._specific_element(xpath, FileInputElement, wait) + def radio_input(self, xpath: str, wait: bool = True) -> RadioInputElement: + return self._specific_element(xpath, RadioInputElement, wait) + + def checkbox(self, xpath: str, wait: bool = True) -> CheckboxElement: + return self._specific_element(xpath, CheckboxElement, wait) + + def select(self, xpath: str, wait: bool = True) -> SelectElement: + return self._specific_element(xpath, SelectElement, wait) + def button(self, xpath: str, wait: bool = True) -> ButtonElement: return self._specific_element(xpath, ButtonElement, wait) def form(self, xpath: str, wait: bool = True) -> FormElement: return self._specific_element(xpath, FormElement, wait) - def select(self, xpath: str, wait: bool = True) -> SelectElement: - return self._specific_element(xpath, SelectElement, wait) - def list(self, xpath: str, wait: bool = True) -> ListElement: return self._specific_element(xpath, ListElement, wait) diff --git a/fastrpa/core/elements.py b/fastrpa/core/elements.py index bbdbf99..562e5fb 100644 --- a/fastrpa/core/elements.py +++ b/fastrpa/core/elements.py @@ -153,6 +153,85 @@ def print(self): print_list(f'{identifier}', self.options) +class RadioInputElement(Element): + @property + def radio_sources(self) -> list[WebElement]: + name = self.attribute('name') + xpath = f'//input[@type="radio"][@name="{name}"][not(@disabled)]' + return find_elements(self.webdriver, xpath) + + @property + def options_values(self) -> list[str]: + return [ + value + for element in self.radio_sources + if (value := element.get_attribute('value')) + ] + + @property + def options_labels(self) -> list[str]: + return [ + find_element(self.webdriver, xpath).text + for element in self.radio_sources + if (xpath := f'//label[@for="{element.get_attribute("id")}"]') + ] + + @property + def options(self) -> dict[str, str]: + return { + value: label + for value, label in zip(self.options_values, self.options_labels) + } + + def select(self, label: str | None = None, value: str | None = None): + if label: + index = self.options_labels.index(label) + element = self.radio_sources[index] + self.actions.move_to_element(element) + self.actions.click(element) + self.actions.perform() + + elif value: + index = self.options_values.index(value) + element = self.radio_sources[index] + self.actions.move_to_element(element) + self.actions.click(element) + self.actions.perform() + else: + raise ValueError('You must provide at least "label" or "value"!') + + def has_option(self, label: str | None = None, value: str | None = None): + if label: + return label in self.options_labels + elif value: + return value in self.options_values + raise ValueError('You must provide at least "label" or "value"!') + + def __contains__(self, key: Any) -> bool: + return self.has_option(label=key) or self.has_option(value=key) + + def print(self): + identifier = f'[@id="{self.id}"]' if self.id else self.xpath + print_list(f'{identifier}', self.options) + + +class CheckboxElement(Element): + + @property + def is_checked(self) -> bool: + return self.source.is_selected() + + def check(self): + if not self.is_checked: + self.click() + + def uncheck(self): + if self.is_checked: + self.click() + + def switch(self): + self.click() + class ListElement(Element): @property def items_sources(self) -> list[WebElement]: diff --git a/fastrpa/core/elements_factory.py b/fastrpa/core/elements_factory.py index 19af03f..b393756 100644 --- a/fastrpa/core/elements_factory.py +++ b/fastrpa/core/elements_factory.py @@ -7,12 +7,14 @@ from fastrpa.core.elements import ( ButtonElement, + CheckboxElement, Element, FileInputElement, FormElement, ImageElement, InputElement, ListElement, + RadioInputElement, SelectElement, TableElement, ) @@ -40,6 +42,22 @@ def element_class(self, element: WebElement) -> Type[Element]: ] ): return FileInputElement + + elif all( + [ + element.tag_name == 'input', + element.get_attribute('type') == 'radio', + ] + ): + return RadioInputElement + + elif all( + [ + element.tag_name == 'input', + element.get_attribute('type') == 'checkbox', + ] + ): + return CheckboxElement elif element.tag_name in ['input', 'textarea']: return InputElement diff --git a/fastrpa/utils.py b/fastrpa/utils.py index f3c2a06..6c0f46a 100644 --- a/fastrpa/utils.py +++ b/fastrpa/utils.py @@ -66,7 +66,7 @@ def print_list(name: str, values_dict: dict[str | None, str | None]): rich_tree = Tree(name.replace('[', '\[')) for id, value in values_dict.items(): - rich_tree.add(f'[{id}] {value}') + rich_tree.add(f'\[{id}] {value}') Console().print(rich_tree) except ImportError: