def test_get_closest_element_rounding_simulate_py2_rounding(): '''Chromedriver gives element coordinates with one decimal precision like x: 28.5. Selenium rounds these to integer. This rounding causes uncertainty when calculating the closest element. Also, rounding function was changed in Py3 and it now uses banker's rounding where it rounds to even number. These two tests simulate real life case where Python 2 worked but Python 3 did not. I have modified get_closest_element function to handle elements with similar distances as if they are same. ''' locator_element = MagicMock() locator_element.location = {'x': 29, 'y': 328} locator_element.size = {'width': 337, 'height': 31} locator_element.get_attribute.return_value = 'foo' cand1 = MagicMock() cand1.location = {'x': 370, 'y': 302} cand1.size = {'width': 96, 'height': 22} cand1.get_attribute.return_value = 'foo' cand2 = MagicMock() cand2.location = {'x': 370, 'y': 332} cand2.size = {'width': 96, 'height': 22} cand2.get_attribute.return_value = 'foo' cand3 = MagicMock() cand3.location = {'x': 370, 'y': 363} cand3.size = {'width': 96, 'height': 22} cand3.get_attribute.return_value = 'foo' assert get_closest_element(locator_element, [cand1, cand2, cand3]) == cand2 assert get_closest_element(locator_element, [cand1, cand3, cand2]) == cand2 assert get_closest_element(locator_element, [cand3, cand1, cand2]) == cand2
def get_checkbox_by_locator(locator, anchor): """Get checkbox element. Parameters ---------- locator : str Either text that points to the checkbox or direct xpath to the checkbox. If using direct XPath then add prefix xpath=. anchor : str Using if locator is not an XPath. Returns ------- WebElement """ if locator.startswith("xpath=") or locator.startswith("//"): checkbox_element = element.get_unique_element_by_xpath(locator) # TODO: Check that the element is actually a checkbox else: # No prefix given text_element = text.get_text_using_anchor(locator, anchor) xpath = '//input[@type="checkbox"]|//*[@role="checkbox"]' checkbox_elements = element.get_webelements_in_active_area( xpath, stay_in_current_frame=True) checkbox_element = element.get_closest_element(text_element, checkbox_elements) return checkbox_element, None
def test_get_closest_element_two_candidates(): locator_element = MagicMock() locator_element.location = {'x': 28, 'y': 328} locator_element.size = {'width': 337, 'height': 31} locator_element.get_attribute.return_value = 'foo' cand1 = MagicMock() cand1.location = {'x': 370, 'y': 332} cand1.size = {'width': 96, 'height': 22} cand1.get_attribute.return_value = 'foo' cand2 = MagicMock() cand2.location = {'x': 370, 'y': 432} cand2.size = {'width': 96, 'height': 22} cand2.get_attribute.return_value = 'foo' assert get_closest_element(locator_element, [cand1, cand2]) == cand1 assert get_closest_element(locator_element, [cand2, cand1]) == cand1
def get_element_using_anchor(elements, anchor, **kwargs): """Determine correct element from list of elements using anchor. Parameters ---------- elements : :obj:`list` of :obj:`WebElement` anchor Returns ------- WebElement """ if anchor is None: # Element was not unique and anchor was not used. raise QWebValueError( 'Found {} elements. Use anchor to determine which is wanted'. format(len(elements))) # Select by index unless anchor type is text if anchor.isdigit() and kwargs.get("anchor_type", "auto").lower() != "text": anchor = int(anchor) - 1 if anchor < len(elements): return elements[anchor] raise QWebInstanceDoesNotExistError( 'Found {} elements. Given anchor was {}'.format( len(elements), anchor + 1)) if isinstance(anchor, str): # Get closest element to anchor kwargs['stay_in_current_frame'] = True anchor_element = None if CONFIG['MultipleAnchors']: anchor_elements = [] logger.debug( 'Multiple anchors enabled, trying to find first exact match') try: anchor_elements = _get_exact_text_element(anchor, **kwargs) except NoSuchFrameException: logger.debug('Got no such frame from get exact text') if len(anchor_elements) > 0: # Using first exact match as anchor anchor_element = anchor_elements[0] else: # No exact matches found, trying to find partial anchor_elements = get_text_elements(anchor, **kwargs) if len(anchor_elements) > 0: logger.debug( 'No exact match found, using first partial match') anchor_element = anchor_elements[0] else: anchor_element = get_unique_text_element(anchor, **kwargs) return element.get_closest_element(anchor_element, elements) raise TypeError("Unknown argument type {}".format(type(anchor)))
def get_dropdown_element_by_locator(locator, anchor): """Find dropdown element. Parameters ---------- locator : str Text that locates the input field. The input field that is closest to the text is selected. Also one can use xpath by adding xpath= prefix and then the xpath. Error is raised if the xpath matches to multiple elements. anchor : str Text near the input field's locator element. If the page contains many places where the locator is then anchor is used to get the one that is closest to it. """ if locator.startswith("xpath=") or locator.startswith("//"): dropdown_element = element.get_unique_element_by_xpath(locator) else: # Search using text # First we look through all select elements' options, matching locator matches = [] elements = _get_all_dropdown_elements() for dd_element in elements: options = [x.text for x in Select(dd_element).options] if locator in options: logger.debug("Found dropdown with options %s" % options) matches.append(dd_element) if matches: correct_element = text.get_element_using_anchor(matches, anchor) return correct_element # Then we try to find the element using attributes and text dropdown_xpath = ( # pylint: disable=line-too-long '//select[normalize-space(@placeholder)="{0}" or normalize-space(@value)="{0}" or normalize-space(text())="{0}"]' .format(locator)) dropdown_elements = element.get_webelements_in_active_area( dropdown_xpath) if len(dropdown_elements) == 1: dropdown_element = dropdown_elements[0] elif not dropdown_elements: # Find dropdown element using locator locator_element = text.get_text_using_anchor(locator, anchor) dropdown_elements = _get_all_dropdown_elements( stay_in_current_frame=True) dropdown_element = element.get_closest_element( locator_element, dropdown_elements) else: # Found many logger.debug("found many, using anchor") dropdown_element = text.get_element_using_anchor( dropdown_elements, anchor) return dropdown_element
def test_get_closest_element_rounding_simulate_py3_rounding(): locator_element = MagicMock() locator_element.location = {'x': 28, 'y': 328} locator_element.size = {'width': 337, 'height': 31} locator_element.get_attribute.return_value = 'foo' cand1 = MagicMock() cand1.location = {'x': 370, 'y': 302} cand1.size = {'width': 96, 'height': 22} cand1.get_attribute.return_value = 'foo' cand2 = MagicMock() cand2.location = {'x': 370, 'y': 332} cand2.size = {'width': 96, 'height': 22} cand2.get_attribute.return_value = 'foo' cand3 = MagicMock() cand3.location = {'x': 370, 'y': 362} cand3.size = {'width': 96, 'height': 22} cand3.get_attribute.return_value = 'foo' assert get_closest_element(locator_element, [cand1, cand2, cand3]) == cand2 assert get_closest_element(locator_element, [cand1, cand3, cand2]) == cand2 assert get_closest_element(locator_element, [cand3, cand1, cand2]) == cand2
def get_table_element(self, locator, anchor): if util.xpath_validator(locator): table_element = element.get_unique_element_by_xpath(locator) else: # Search using text table_xpath = "//*[text()= '{0}']/ancestor::table".format(locator) table_elements = element.get_webelements_in_active_area( table_xpath) if table_elements and len(table_elements) == 1: table_element = table_elements[0] elif not table_elements: # Find table element using locator locator_element = text.get_text_using_anchor(locator, anchor) table_elements = self._get_all_table_elements() table_element = element.get_closest_element( locator_element, table_elements) else: # Found many table_element = text.get_element_using_anchor( table_elements, anchor) if table_element: return table_element raise QWebElementNotFoundError( 'Table element not found by locator {}'.format(locator))
def get_input_element_by_locator(locator, anchor, **kwargs): """Find input element. Parameters ---------- locator : str Text that locates the input field. The input field that is closest to the text is selected. Also one can use xpath by adding xpath= prefix and then the xpath. Error is raised if the xpath matches to multiple elements. anchor : str Text near the input field's locator element. If the page contains many places where the locator is then anchor is used to get the one that is closest to it. """ if locator.startswith("xpath=") or locator.startswith( "//") or locator.startswith("(//"): if locator.startswith("xpath="): xpath = locator.split("=", 1)[1] else: xpath = locator input_element = element.get_unique_element_by_xpath(xpath, **kwargs) else: # Search using text input_xpath = CONFIG["MatchingInputElement"].format(locator) input_elements = element.get_webelements_in_active_area( input_xpath, **kwargs) if not input_elements: # Find input element using locator locator_element = text.get_text_using_anchor( locator, anchor, **kwargs) input_elements = _get_all_input_elements() input_element = element.get_closest_element( locator_element, input_elements) elif len(input_elements) == 1: input_element = input_elements[0] # pylint: disable=unsubscriptable-object else: # Found many input_element = text.get_element_using_anchor( input_elements, anchor, **kwargs) return input_element
def test_no_visible_elements(): with pytest.raises(QWebElementNotFoundError): get_closest_element('', [])