Skip to content

Commit 63b35dc

Browse files
committed
Moving towards a working intermediate model for new locator element engine
On the way to complete a working model of the engine. This intermediate model will have the default locator strategy hardcoded. Eventually this will be one in the list of registered elementfinders. Committing now as I need to make a change that may need to be reverted.
1 parent 7204ac2 commit 63b35dc

File tree

4 files changed

+56
-32
lines changed

4 files changed

+56
-32
lines changed

src/SeleniumLibrary/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ def find_element(
766766
:rtype: selenium.webdriver.remote.webelement.WebElement
767767
:raises SeleniumLibrary.errors.ElementNotFound: If element not found.
768768
"""
769-
return self._loc_elem_engine.find(locator, parent=parent)
769+
return self._loc_elem_engine.parse_and_find(locator, parent=parent)
770770

771771
def find_elements(
772772
self, locator: str, parent: WebElement = None
@@ -782,7 +782,7 @@ def find_elements(
782782
:return: list of found `WebElement` or e,mpty if elements are not found.
783783
:rtype: list[selenium.webdriver.remote.webelement.WebElement]
784784
"""
785-
return self._loc_elem_engine.find(
785+
return self._loc_elem_engine.parse_and_find(
786786
locator, first_only=False, required=False, parent=parent
787787
)
788788

src/SeleniumLibrary/base/context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def find_element(
7979
:raises SeleniumLibrary.errors.ElementNotFound: If element not found
8080
and `required` is true.
8181
"""
82-
return self.element_finder.find(locator, tag, True, required, parent)
82+
return self.element_finder.parse_and_find(locator, tag, True, required, parent)
8383

8484
def find_elements(
8585
self, locator: str, tag: Optional[str] = None, parent: WebElement = None
@@ -97,7 +97,7 @@ def find_elements(
9797
:return: list of found `WebElement` or empty if elements are not found.
9898
:rtype: list[selenium.webdriver.remote.webelement.WebElement]
9999
"""
100-
return self.element_finder.find(locator, tag, False, False, parent)
100+
return self.element_finder.parse_and_find(locator, tag, False, False, parent)
101101

102102
def is_text_present(self, text: str):
103103
locator = f"xpath://*[contains(., {escape_xpath_value(text)})]"

src/SeleniumLibrary/locators/elementfinder.py

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
# limitations under the License.
1616
import re
1717
from typing import Union
18+
from typing import Protocol
1819

1920
from robot.api import logger
2021
from robot.utils import NormalizedDict
21-
from selenium.webdriver.remote.webelement import WebElement
22-
from selenium.webdriver.support.event_firing_webdriver import EventFiringWebElement
2322
from selenium.webdriver.common.by import By
2423

2524
from SeleniumLibrary.base import ContextAware
@@ -130,8 +129,25 @@ def __init__(self, ctx):
130129
r"|css ?[:|=]|class ?[:|=]|jquery ?[:|=]|sizzle ?[:|=]|tag ?[:|=]|scLocator ?[:|=])",
131130
re.IGNORECASE,
132131
)
132+
# Should create a (empty?) list of ElementFinder here
133+
# and register the default element finder (and others?) ...
134+
# .. but for now hard code an instance of the defaultelementfinder
135+
self._element_finder = DefaultElementFinder()
133136

134-
def find(
137+
138+
# I am wanting to rename what was called the find method
139+
# def find(
140+
# which would do the parsing of the locator string and then call
141+
# the elementfinder (_find)
142+
#
143+
# Thought I might call it execute_find_cycle but really that is
144+
# the name to give the looping through the element finder list
145+
# def execute_find_cycle(
146+
#
147+
# Have a generic namer of request but seems too generic ..
148+
# def request(...):
149+
# Going with
150+
def parse_and_find(
135151
self,
136152
locator: Union[str, list],
137153
tag=None,
@@ -140,12 +156,18 @@ def find(
140156
parent=None,
141157
):
142158
element = parent
159+
# The _split_locator method could be architected as another class, parse_locator
143160
locators = self._split_locator(locator)
144161
for split_locator in locators[:-1]:
145-
element = self._find(
146-
split_locator, first_only=True, required=True, parent=element
147-
)
148-
return self._find(locators[-1], tag, first_only, required, element)
162+
# This where we would loop through the list of element finders..
163+
# .. but for now just call the find method on the default element finder.
164+
element = self._element_finder.find(split_locator, first_only=True, required=True, parent=element)
165+
#
166+
# .. was ..
167+
# element = self._find
168+
# split_locator, first_only=True, required=True, parent=element
169+
# )
170+
return self._element_finder.find(locators[-1], tag, first_only, required, element)
149171

150172
def _split_locator(self, locator: Union[str, list]) -> list:
151173
if isinstance(locator, list):
@@ -164,25 +186,25 @@ def _split_locator(self, locator: Union[str, list]) -> list:
164186
parts.append(locator)
165187
return parts
166188

167-
def _find(self, locator, tag=None, first_only=True, required=True, parent=None):
168-
element_type = "Element" if not tag else tag.capitalize()
169-
if parent and not is_webelement(parent):
170-
raise ValueError(
171-
f"Parent must be Selenium WebElement but it was {type(parent)}."
172-
)
173-
if is_webelement(locator):
174-
return locator
175-
prefix, criteria = self._parse_locator(locator)
176-
strategy = self._strategies[prefix]
177-
tag, constraints = self._get_tag_and_constraints(tag)
178-
elements = strategy(criteria, tag, constraints, parent=parent or self.driver)
179-
if required and not elements:
180-
raise ElementNotFound(f"{element_type} with locator '{locator}' not found.")
181-
if first_only:
182-
if not elements:
183-
return None
184-
return elements[0]
185-
return elements
189+
# def _find(self, locator, tag=None, first_only=True, required=True, parent=None):
190+
# element_type = "Element" if not tag else tag.capitalize()
191+
# if parent and not is_webelement(parent):
192+
# raise ValueError(
193+
# f"Parent must be Selenium WebElement but it was {type(parent)}."
194+
# )
195+
# if is_webelement(locator):
196+
# return locator
197+
# prefix, criteria = self._parse_locator(locator)
198+
# strategy = self._strategies[prefix]
199+
# tag, constraints = self._get_tag_and_constraints(tag)
200+
# elements = strategy(criteria, tag, constraints, parent=parent or self.driver)
201+
# if required and not elements:
202+
# raise ElementNotFound(f"{element_type} with locator '{locator}' not found.")
203+
# if first_only:
204+
# if not elements:
205+
# return None
206+
# return elements[0]
207+
# return elements
186208

187209
def _get_tag_and_constraints(self, tag):
188210
if tag is None:

src/SeleniumLibrary/locators/utilities.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16+
from selenium.webdriver.remote.webelement import WebElement
17+
from selenium.webdriver.support.event_firing_webdriver import EventFiringWebElement
1618

17-
def is_webelement(self, element):
19+
def is_webelement(element):
1820
# Hook for unit tests
1921
return isinstance(element, (WebElement, EventFiringWebElement))
2022

21-
def disallow_webelement_parent(self, element):
23+
def disallow_webelement_parent(element):
2224
if is_webelement(element):
2325
raise ValueError("This method does not allow WebElement as parent")
2426

0 commit comments

Comments
 (0)