diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dae5bcd3..76f7b3f0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,8 @@ v3.1.3 *Release date: In development* +- Fix `PageElements` class initialization when custom page element classes don't have all optional attributes + v3.1.2 ------ @@ -22,6 +24,7 @@ v3.1.1 - Upgrade Sphinx version from 4.* to 7.* to fix readthedocs theme format - Upgrade readthedocs-sphinx-search to 0.3.2 to fix security vulnerability - Do not log warning messages when toolium system properties are used +- Allow to initialize a `PageElements` class with webview attributes v3.1.0 ------ diff --git a/toolium/pageelements/page_elements.py b/toolium/pageelements/page_elements.py index 13bcb4b2..60b36862 100644 --- a/toolium/pageelements/page_elements.py +++ b/toolium/pageelements/page_elements.py @@ -15,7 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. """ -from typing import List, Any +import inspect +from typing import Any, List from toolium.driver_wrapper import DriverWrappersPool from toolium.pageelements.button_page_element import Button @@ -120,11 +121,13 @@ def page_elements(self) -> List[Any]: self._page_elements = [] for order, web_element in enumerate(self.web_elements): # Create multiple PageElement with original locator and order - page_element =\ - self.page_element_class(self.locator[0], self.locator[1], parent=self.parent, - order=order, webview=self.webview, - webview_context_selection_callback=self.webview_context_selection_callback, - webview_csc_args=self.webview_csc_args) + # Optional parameters are passed only if they are defined in the PageElement constructor + signature = inspect.signature(self.page_element_class.__init__) + opt_names = ['parent', 'webview', 'webview_context_selection_callback', 'webview_csc_args'] + opt_params = {name: getattr(self, name) for name in opt_names if name in signature.parameters} + if 'order' in signature.parameters: + opt_params['order'] = order + page_element = self.page_element_class(self.locator[0], self.locator[1], **opt_params) page_element.reset_object(self.driver_wrapper) page_element._web_element = web_element self._page_elements.append(page_element) diff --git a/toolium/test/pageelements/test_page_elements.py b/toolium/test/pageelements/test_page_elements.py index c53bd575..eab46ad0 100644 --- a/toolium/test/pageelements/test_page_elements.py +++ b/toolium/test/pageelements/test_page_elements.py @@ -36,13 +36,43 @@ def init_page_elements(self): self.links = PageElements(By.XPATH, '//a') self.inputs_with_parent = PageElements(By.XPATH, '//input', parent=(By.ID, 'parent')) self.inputs_with_webview = PageElements(By.XPATH, '//input', webview=True) - self.inputs_with_webview_callback = \ - PageElements(By.XPATH, '//input', webview_context_selection_callback=lambda a, b: (a, b), - webview_csc_args=['WEBVIEW_fake.other', "CDwindow-0123456789"], webview=True) + self.inputs_with_webview_callback = PageElements(By.XPATH, '//input', + webview_context_selection_callback=lambda a, b: (a, b), + webview_csc_args=['WEBVIEW_fake.other', "CDwindow-0123456789"], + webview=True) self.parent_webview = PageElement(By.XPATH, '//parent', webview=True) self.inputs_with_webview_parent = PageElements(By.XPATH, '//input', parent=self.parent_webview, webview=True) +class CustomElementAllAttributes(PageElement): + def __init__(self, by, value, parent=None, order=None, wait=False, shadowroot=None, webview=False, + webview_context_selection_callback=None, webview_csc_args=None): + super(CustomElementAllAttributes, self).__init__(by, value, parent, order, wait, shadowroot, webview, + webview_context_selection_callback, webview_csc_args) + + +class CustomElementSomeAttributes(PageElement): + def __init__(self, by, value, parent=None, order=None, wait=False, shadowroot=None): + super(CustomElementSomeAttributes, self).__init__(by, value, parent, order, wait, shadowroot) + + +class CustomElementMandatoryAttributes(PageElement): + def __init__(self, by, value): + super(CustomElementMandatoryAttributes, self).__init__(by, value) + + +class LoginWithPageElementsPageObject(PageObject): + def init_page_elements(self): + self.all_optional_attrs = PageElements(By.XPATH, '//input', page_element_class=CustomElementAllAttributes, + webview_context_selection_callback=lambda a, b: (a, b), + webview_csc_args=['WEBVIEW_fake.other', "CDwindow-0123456789"], + webview=True) + self.some_optional_attrs = PageElements(By.XPATH, '//input', page_element_class=CustomElementSomeAttributes, + parent=(By.ID, 'parent')) + self.only_mandatory_attrs = PageElements(By.XPATH, '//input', + page_element_class=CustomElementMandatoryAttributes) + + @pytest.fixture def driver_wrapper(): # Reset wrappers pool values @@ -171,13 +201,81 @@ def test_reset_object(driver_wrapper): assert page_element_21._web_element is not None +def test_get_page_elements_custom_element_class_all_optional(driver_wrapper): + driver_wrapper.driver.find_elements.return_value = child_elements + page_elements = LoginWithPageElementsPageObject().all_optional_attrs.page_elements + + # Check that find_elements has been called just one time + driver_wrapper.driver.find_elements.assert_called_once_with(By.XPATH, '//input') + driver_wrapper.driver.find_element.assert_not_called() + + # Check that the response is a list of 2 CustomElementAllFields with the expected web element + assert len(page_elements) == 2 + assert isinstance(page_elements[0], CustomElementAllAttributes) + assert page_elements[0]._web_element == child_elements[0] + assert isinstance(page_elements[1], CustomElementAllAttributes) + assert page_elements[1]._web_element == child_elements[1] + + # Check that the optional attributes are set correctly + assert page_elements[0].order == 0 + assert page_elements[0].webview is True + assert page_elements[0].webview_context_selection_callback + assert page_elements[0].webview_csc_args == ['WEBVIEW_fake.other', "CDwindow-0123456789"] + assert page_elements[1].order == 1 + assert page_elements[1].webview is True + assert page_elements[1].webview_context_selection_callback + assert page_elements[1].webview_csc_args == ['WEBVIEW_fake.other', "CDwindow-0123456789"] + + +def test_get_page_elements_custom_element_class_some_optional(driver_wrapper): + # Create a mock element + mock_element = mock.MagicMock(spec=WebElement) + mock_element.find_elements.return_value = child_elements + + driver_wrapper.driver.find_element.return_value = mock_element + page_elements = LoginWithPageElementsPageObject().some_optional_attrs.page_elements + + # Check that find_elements has been called just one time + driver_wrapper.driver.find_element.assert_called_once_with(By.ID, 'parent') + mock_element.find_elements.assert_called_once_with(By.XPATH, '//input') + + # Check that the response is a list of 2 CustomElementSomeAttributes with the expected web element + assert len(page_elements) == 2 + assert isinstance(page_elements[0], CustomElementSomeAttributes) + assert page_elements[0]._web_element == child_elements[0] + assert isinstance(page_elements[1], CustomElementSomeAttributes) + assert page_elements[1]._web_element == child_elements[1] + + # Check that the optional attributes are set correctly + assert page_elements[0].order == 0 + assert page_elements[0].parent == (By.ID, 'parent') + assert page_elements[1].order == 1 + assert page_elements[0].parent == (By.ID, 'parent') + + +def test_get_page_elements_custom_element_class_only_mandatory(driver_wrapper): + driver_wrapper.driver.find_elements.return_value = child_elements + page_elements = LoginWithPageElementsPageObject().only_mandatory_attrs.page_elements + + # Check that find_elements has been called just one time + driver_wrapper.driver.find_elements.assert_called_once_with(By.XPATH, '//input') + driver_wrapper.driver.find_element.assert_not_called() + + # Check that the response is a list of 2 CustomElementMandatoryAttributes with the expected web element + assert len(page_elements) == 2 + assert isinstance(page_elements[0], CustomElementMandatoryAttributes) + assert page_elements[0]._web_element == child_elements[0] + assert isinstance(page_elements[1], CustomElementMandatoryAttributes) + assert page_elements[1]._web_element == child_elements[1] + + def test_get_page_elements_without_webview(driver_wrapper): driver_wrapper.driver.find_elements.return_value = child_elements page_elements = LoginPageObject().inputs.page_elements # Check webview attribute is set to false by default in child elements - assert not page_elements[0].webview - assert not page_elements[1].webview + assert page_elements[0].webview is False + assert page_elements[1].webview is False def test_get_page_elements_with_webview(driver_wrapper): @@ -186,8 +284,8 @@ def test_get_page_elements_with_webview(driver_wrapper): # Check webview attribute is set to true in child element when a Pagelements element # is created with the webview attribute - assert page_elements[0].webview - assert page_elements[1].webview + assert page_elements[0].webview is True + assert page_elements[1].webview is True def test_get_page_elements_with_context_selection_callback_provided(driver_wrapper): @@ -196,9 +294,9 @@ def test_get_page_elements_with_context_selection_callback_provided(driver_wrapp # Check context selection callback provided is set correctly to pageelements assert page_elements[0].webview_context_selection_callback - assert page_elements[0].webview_csc_args + assert page_elements[0].webview_csc_args == ['WEBVIEW_fake.other', "CDwindow-0123456789"] assert page_elements[1].webview_context_selection_callback - assert page_elements[1].webview_csc_args + assert page_elements[1].webview_csc_args == ['WEBVIEW_fake.other', "CDwindow-0123456789"] def test_mobile_automatic_context_selection_switch_to_new_webview_context_in_pagelements_without_parent(driver_wrapper):