diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py index ddd0acc47..8e3767c5c 100644 --- a/src/SeleniumLibrary/__init__.py +++ b/src/SeleniumLibrary/__init__.py @@ -344,8 +344,8 @@ def __init__(self, timeout=5.0, implicit_wait=0.0, self._browsers = BrowserCache() DynamicCore.__init__(self, libraries) self.ROBOT_LIBRARY_LISTENER = LibraryListener() - self.element_finder = ElementFinder(self) - self.table_element_finder = TableElementFinder(self) + self._element_finder = ElementFinder(self) + self._table_element_finder = TableElementFinder(self) _speed_in_secs = Deprecated('_speed_in_secs', 'speed') _timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout') @@ -389,6 +389,31 @@ def browser(self): raise RuntimeError('No browser is open') return self._browsers.current + def find_element(self, locator, parent=None): + """Find element matching ``locator``. + + This method and :meth:`find_elements` form the recommended + public API for external tools to get elements via SeleniumLibrary. + + :param locator: Locator to use when searching the element. + See library documentation for the supported locator syntax. + :param parent: Optional parent ``WebElememt`` to search child elements + from. By default search starts from the root using ``WebDriver``. + :rtype: selenium.webdriver.remote.webelement.WebElement + :raises SeleniumLibrary.errors.ElementNotFound: If element not found. + """ + return self._element_finder.find(locator, parent=parent) + + def find_elements(self, locator, parent=None): + """Find all elements matching ``locator``. + + Returns a list of ``WebElement`` objects. If no matching elements + are found, the list is empty. Otherwise semantics are exactly same + as with the :meth:`find_element` method. + """ + return self._element_finder.find(locator, first_only=False, + required=False, parent=parent) + @property def _cache(self): warnings.warn('"SeleniumLibrary._cache" is deprecated, ' diff --git a/src/SeleniumLibrary/base/context.py b/src/SeleniumLibrary/base/context.py index 061d1ed75..5eda0bc7c 100644 --- a/src/SeleniumLibrary/base/context.py +++ b/src/SeleniumLibrary/base/context.py @@ -37,33 +37,29 @@ def browsers(self): @property def element_finder(self): - return self.ctx.element_finder + return self.ctx._element_finder @property def table_element_finder(self): - return self.ctx.table_element_finder + return self.ctx._table_element_finder - def find_element(self, locator, tag=None, first_only=True, required=True, - parent=None): + def find_element(self, locator, tag=None, required=True, parent=None): """Find element matching `locator`. :param locator: Locator to use when searching the element. See library documentation for the supported locator syntax. :param tag: Limit searching only to these elements. - :param first_only: Return only first matching element if true, - a list of elements otherwise. :param required: Raise `ElementNotFound` if element not found when true, return `None` otherwise. - :param parent: Possible parent `WebElememt` element to search - elements from. By default search starts from `WebDriver`. + :param parent: Optional parent `WebElememt` to search child elements + from. By default search starts from the root using `WebDriver`. :return: Found `WebElement` or `None` if element not found and `required` is false. :rtype: selenium.webdriver.remote.webelement.WebElement :raises SeleniumLibrary.errors.ElementNotFound: If element not found and `required` is true. """ - return self.element_finder.find(locator, tag, first_only, - required, parent) + return self.element_finder.find(locator, tag, True, required, parent) def find_elements(self, locator, tag=None, parent=None): """Find all elements matching `locator`. @@ -72,7 +68,7 @@ def find_elements(self, locator, tag=None, parent=None): is found, the list is empty. Otherwise semantics are exactly same as with :meth:`find_element`. """ - return self.find_element(locator, tag, False, False, parent) + return self.element_finder.find(locator, tag, False, False, parent) def is_text_present(self, text): locator = "xpath://*[contains(., %s)]" % escape_xpath_value(text) diff --git a/test/acceptance/public_api.robot b/test/acceptance/public_api.robot new file mode 100644 index 000000000..317e8f882 --- /dev/null +++ b/test/acceptance/public_api.robot @@ -0,0 +1,27 @@ +*** Settings *** +Resource resource.robot +Library CustomSeleniumKeywords +Suite Setup Run Keywords +... Set Library Search Order CustomSeleniumKeywords AND +... Open Browser To Start Page +Suite Teardown Close Browser + +*** Test Cases *** +Find Element + ${first} = Use Find Element first + Should Be Equal ${first.text} This is the haystack and somewhere on this page is a needle. + ${em} = Use Find Element css:em ${first} + Should Be Equal ${em.text} needle + Run Keyword And Expect Error + ... Element with locator 'nonex' not found. + ... Use Find Element nonex + +Find Elements + ${paras} = Use Find Elements //p + Should Be Equal ${paras[0].text} This is the haystack and somewhere on this page is a needle. + Length Should Be ${paras} 2 + ${ems} = Use Find Elements tag:em ${paras[0]} + Should Be Equal ${ems[0].text} needle + Length Should Be ${ems} 1 + ${nonex} = Use Find Elements nonex + Length Should Be ${nonex} 0 diff --git a/test/resources/html/index.html b/test/resources/html/index.html index 0651e3b74..a3919e35e 100644 --- a/test/resources/html/index.html +++ b/test/resources/html/index.html @@ -3,7 +3,7 @@ (root)/index.html -

This is the haystack and somewhere on this page is a needle.

+

This is the haystack and somewhere on this page is a needle.

This is more text

This text is inside an identified element diff --git a/test/resources/testlibs/CustomSeleniumKeywords.py b/test/resources/testlibs/CustomSeleniumKeywords.py index b3e92c07f..c8469ee1f 100755 --- a/test/resources/testlibs/CustomSeleniumKeywords.py +++ b/test/resources/testlibs/CustomSeleniumKeywords.py @@ -21,3 +21,11 @@ def custom_selenium_keyword(self): @keyword def custom_selenium_keyword_inner(self): raise AssertionError + + @keyword + def use_find_element(self, locator, parent=None): + return self.find_element(locator, parent) + + @keyword + def use_find_elements(self, locator, parent=None): + return self.find_elements(locator, parent) diff --git a/test/unit/api/test_public_api_objects.py b/test/unit/api/test_public_api_objects.py deleted file mode 100644 index 8a02c034d..000000000 --- a/test/unit/api/test_public_api_objects.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest - -from SeleniumLibrary import SeleniumLibrary -from SeleniumLibrary.locators import ElementFinder, TableElementFinder - - -class TableElementFinderInAPITest(unittest.TestCase): - - def test_table_finder(self): - sl = SeleniumLibrary() - self.assertIsInstance(sl.table_element_finder, TableElementFinder) - - -class ElementFinderInAPITest(unittest.TestCase): - - def test_table_finder(self): - sl = SeleniumLibrary() - self.assertIsInstance(sl.element_finder, ElementFinder) diff --git a/test/unit/locators/test_tableelementfinder.py b/test/unit/locators/test_tableelementfinder.py index 410661f27..ceaf638be 100644 --- a/test/unit/locators/test_tableelementfinder.py +++ b/test/unit/locators/test_tableelementfinder.py @@ -9,7 +9,7 @@ class ElementFinderTest(unittest.TestCase): def setUp(self): self.ctx = mock() - self.ctx.element_finder = mock() + self.ctx._element_finder = mock() self.finder = TableElementFinder(self.ctx) def tearDown(self): @@ -48,8 +48,8 @@ def test_by_search_in_locators(self): element1.text = 'not here' element2.text = 'content' table_elements = [element1, element2] - when(self.ctx.element_finder).find( + when(self.ctx._element_finder).find( 'css=table', None, True, True, None).thenReturn(table) - when(self.ctx.element_finder).find( + when(self.ctx._element_finder).find( xpath[0], None, False, False, table).thenReturn(table_elements) self.finder._search_in_locators('css=table', xpath, 'content')