Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/SeleniumLibrary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have similar sphinx style parameter definitions as find_element in above?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't hurt. More important change would be adding return type information, but it should mention that return type is a list of WebElement objects, not just that the type is a list.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You had some other syntax changes to type info. Can you take care of all of them at the same time?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can. I will merge this PR and write separate PR for fixing it.

"""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, '
Expand Down
18 changes: 7 additions & 11 deletions src/SeleniumLibrary/base/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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)
Expand Down
27 changes: 27 additions & 0 deletions test/acceptance/public_api.robot
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/resources/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<title>(root)/index.html</title>
</head>
<body>
<p>This is the haystack and somewhere on this page is a <em>needle</em>.</p>
<p id="first">This is the haystack and somewhere on this page is a <em>needle</em>.</p>
<!-- this is a comment -->
<p>This is more text</p>
<span id="some_id">This text is inside an identified element</span>
Expand Down
8 changes: 8 additions & 0 deletions test/resources/testlibs/CustomSeleniumKeywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
18 changes: 0 additions & 18 deletions test/unit/api/test_public_api_objects.py

This file was deleted.

6 changes: 3 additions & 3 deletions test/unit/locators/test_tableelementfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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')