Skip to content

Commit 13f07f7

Browse files
pekkaklarckaaltat
authored andcommitted
Add 'find_element(s)' to public API (robotframework#965)
* Add 'find_element(s)' to public API * Cleanup internal find_element(s) API Make (table_)element_finder private. We cannot promise that we keep these objects stable in the future. Better not to have them in the public API either. Related to robotframework#882.
1 parent eeb5024 commit 13f07f7

File tree

7 files changed

+73
-35
lines changed

7 files changed

+73
-35
lines changed

src/SeleniumLibrary/__init__.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,8 @@ def __init__(self, timeout=5.0, implicit_wait=0.0,
344344
self._browsers = BrowserCache()
345345
DynamicCore.__init__(self, libraries)
346346
self.ROBOT_LIBRARY_LISTENER = LibraryListener()
347-
self.element_finder = ElementFinder(self)
348-
self.table_element_finder = TableElementFinder(self)
347+
self._element_finder = ElementFinder(self)
348+
self._table_element_finder = TableElementFinder(self)
349349

350350
_speed_in_secs = Deprecated('_speed_in_secs', 'speed')
351351
_timeout_in_secs = Deprecated('_timeout_in_secs', 'timeout')
@@ -389,6 +389,31 @@ def browser(self):
389389
raise RuntimeError('No browser is open')
390390
return self._browsers.current
391391

392+
def find_element(self, locator, parent=None):
393+
"""Find element matching ``locator``.
394+
395+
This method and :meth:`find_elements` form the recommended
396+
public API for external tools to get elements via SeleniumLibrary.
397+
398+
:param locator: Locator to use when searching the element.
399+
See library documentation for the supported locator syntax.
400+
:param parent: Optional parent ``WebElememt`` to search child elements
401+
from. By default search starts from the root using ``WebDriver``.
402+
:rtype: selenium.webdriver.remote.webelement.WebElement
403+
:raises SeleniumLibrary.errors.ElementNotFound: If element not found.
404+
"""
405+
return self._element_finder.find(locator, parent=parent)
406+
407+
def find_elements(self, locator, parent=None):
408+
"""Find all elements matching ``locator``.
409+
410+
Returns a list of ``WebElement`` objects. If no matching elements
411+
are found, the list is empty. Otherwise semantics are exactly same
412+
as with the :meth:`find_element` method.
413+
"""
414+
return self._element_finder.find(locator, first_only=False,
415+
required=False, parent=parent)
416+
392417
@property
393418
def _cache(self):
394419
warnings.warn('"SeleniumLibrary._cache" is deprecated, '

src/SeleniumLibrary/base/context.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,29 @@ def browsers(self):
3737

3838
@property
3939
def element_finder(self):
40-
return self.ctx.element_finder
40+
return self.ctx._element_finder
4141

4242
@property
4343
def table_element_finder(self):
44-
return self.ctx.table_element_finder
44+
return self.ctx._table_element_finder
4545

46-
def find_element(self, locator, tag=None, first_only=True, required=True,
47-
parent=None):
46+
def find_element(self, locator, tag=None, required=True, parent=None):
4847
"""Find element matching `locator`.
4948
5049
:param locator: Locator to use when searching the element.
5150
See library documentation for the supported locator syntax.
5251
:param tag: Limit searching only to these elements.
53-
:param first_only: Return only first matching element if true,
54-
a list of elements otherwise.
5552
:param required: Raise `ElementNotFound` if element not found when
5653
true, return `None` otherwise.
57-
:param parent: Possible parent `WebElememt` element to search
58-
elements from. By default search starts from `WebDriver`.
54+
:param parent: Optional parent `WebElememt` to search child elements
55+
from. By default search starts from the root using `WebDriver`.
5956
:return: Found `WebElement` or `None` if element not found and
6057
`required` is false.
6158
:rtype: selenium.webdriver.remote.webelement.WebElement
6259
:raises SeleniumLibrary.errors.ElementNotFound: If element not found
6360
and `required` is true.
6461
"""
65-
return self.element_finder.find(locator, tag, first_only,
66-
required, parent)
62+
return self.element_finder.find(locator, tag, True, required, parent)
6763

6864
def find_elements(self, locator, tag=None, parent=None):
6965
"""Find all elements matching `locator`.
@@ -72,7 +68,7 @@ def find_elements(self, locator, tag=None, parent=None):
7268
is found, the list is empty. Otherwise semantics are exactly same
7369
as with :meth:`find_element`.
7470
"""
75-
return self.find_element(locator, tag, False, False, parent)
71+
return self.element_finder.find(locator, tag, False, False, parent)
7672

7773
def is_text_present(self, text):
7874
locator = "xpath://*[contains(., %s)]" % escape_xpath_value(text)

test/acceptance/public_api.robot

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
*** Settings ***
2+
Resource resource.robot
3+
Library CustomSeleniumKeywords
4+
Suite Setup Run Keywords
5+
... Set Library Search Order CustomSeleniumKeywords AND
6+
... Open Browser To Start Page
7+
Suite Teardown Close Browser
8+
9+
*** Test Cases ***
10+
Find Element
11+
${first} = Use Find Element first
12+
Should Be Equal ${first.text} This is the haystack and somewhere on this page is a needle.
13+
${em} = Use Find Element css:em ${first}
14+
Should Be Equal ${em.text} needle
15+
Run Keyword And Expect Error
16+
... Element with locator 'nonex' not found.
17+
... Use Find Element nonex
18+
19+
Find Elements
20+
${paras} = Use Find Elements //p
21+
Should Be Equal ${paras[0].text} This is the haystack and somewhere on this page is a needle.
22+
Length Should Be ${paras} 2
23+
${ems} = Use Find Elements tag:em ${paras[0]}
24+
Should Be Equal ${ems[0].text} needle
25+
Length Should Be ${ems} 1
26+
${nonex} = Use Find Elements nonex
27+
Length Should Be ${nonex} 0

test/resources/html/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<title>(root)/index.html</title>
44
</head>
55
<body>
6-
<p>This is the haystack and somewhere on this page is a <em>needle</em>.</p>
6+
<p id="first">This is the haystack and somewhere on this page is a <em>needle</em>.</p>
77
<!-- this is a comment -->
88
<p>This is more text</p>
99
<span id="some_id">This text is inside an identified element</span>

test/resources/testlibs/CustomSeleniumKeywords.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@ def custom_selenium_keyword(self):
2121
@keyword
2222
def custom_selenium_keyword_inner(self):
2323
raise AssertionError
24+
25+
@keyword
26+
def use_find_element(self, locator, parent=None):
27+
return self.find_element(locator, parent)
28+
29+
@keyword
30+
def use_find_elements(self, locator, parent=None):
31+
return self.find_elements(locator, parent)

test/unit/api/test_public_api_objects.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

test/unit/locators/test_tableelementfinder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class ElementFinderTest(unittest.TestCase):
99

1010
def setUp(self):
1111
self.ctx = mock()
12-
self.ctx.element_finder = mock()
12+
self.ctx._element_finder = mock()
1313
self.finder = TableElementFinder(self.ctx)
1414

1515
def tearDown(self):
@@ -48,8 +48,8 @@ def test_by_search_in_locators(self):
4848
element1.text = 'not here'
4949
element2.text = 'content'
5050
table_elements = [element1, element2]
51-
when(self.ctx.element_finder).find(
51+
when(self.ctx._element_finder).find(
5252
'css=table', None, True, True, None).thenReturn(table)
53-
when(self.ctx.element_finder).find(
53+
when(self.ctx._element_finder).find(
5454
xpath[0], None, False, False, table).thenReturn(table_elements)
5555
self.finder._search_in_locators('css=table', xpath, 'content')

0 commit comments

Comments
 (0)