Skip to content

Commit 58e0dea

Browse files
committed
Renamed ElementFinder to LocatorElementEngine
Under this new model I am separating out the new element finder. Thus the old naming is best changed to something different. As it is now the "engine" that drive both the locator parsing and the element finding I am naming it (for now) the LocatorElementEngine :)
1 parent b5ec621 commit 58e0dea

File tree

4 files changed

+117
-162
lines changed

4 files changed

+117
-162
lines changed

src/SeleniumLibrary/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
WindowKeywords,
5252
)
5353
from SeleniumLibrary.keywords.screenshot import EMBED
54-
from SeleniumLibrary.locators import ElementFinder
54+
from SeleniumLibrary.locators import LocatorElementEngine
5555
from SeleniumLibrary.utils import LibraryListener, is_truthy, _convert_timeout, _convert_delay
5656

5757

@@ -640,7 +640,7 @@ def __init__(
640640
self._running_on_failure_keyword = False
641641
self.screenshot_root_directory = screenshot_root_directory
642642
self._resolve_screenshot_root_directory()
643-
self._element_finder = ElementFinder(self)
643+
self._loc_elem_engine = LocatorElementEngine(self)
644644
self._plugin_keywords = []
645645
libraries = [
646646
AlertKeywords(self),
@@ -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._element_finder.find(locator, parent=parent)
769+
return self._loc_elem_engine.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._element_finder.find(
785+
return self._loc_elem_engine.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
@@ -39,11 +39,11 @@ def drivers(self):
3939

4040
@property
4141
def element_finder(self):
42-
return self.ctx._element_finder
42+
return self.ctx._loc_elem_engine
4343

4444
@element_finder.setter
4545
def element_finder(self, value: Any):
46-
self.ctx._element_finder = value
46+
self.ctx._loc_elem_engine = value
4747

4848
@property
4949
def event_firing_webdriver(self):

src/SeleniumLibrary/locators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
# limitations under the License.
1616

1717
from .customlocator import CustomLocator # noqa
18-
from .elementfinder import ElementFinder # noqa
18+
from .elementfinder import LocatorElementEngine # noqa
1919
from .windowmanager import WindowManager # noqa

src/SeleniumLibrary/locators/elementfinder.py

Lines changed: 110 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
class Finder():
3333
def __int__(self):
34-
"""Placeholder to Finder class instantation method """
34+
"""Placeholder to Finder class instantiation method """
3535
pass
3636

3737
def pre_find_action(self):
@@ -133,97 +133,18 @@ def _get_tag_and_constraints(self, tag):
133133
tag = "textarea"
134134
return tag, constraints
135135

136+
def _is_webelement(self, element):
137+
# Hook for unit tests
138+
return isinstance(element, (WebElement, EventFiringWebElement))
136139

137-
class Strategies(ContextAware):
138-
def __init__(self, ctx):
139-
ContextAware.__init__(self, ctx)
140-
strategies = {
141-
"identifier": ElementFinder._find_by_identifier,
142-
"id": ElementFinder._find_by_id,
143-
"name": ElementFinder._find_by_name,
144-
"xpath": ElementFinder._find_by_xpath,
145-
"dom": ElementFinder._find_by_dom,
146-
"link": ElementFinder._find_by_link_text,
147-
"partial link": ElementFinder._find_by_partial_link_text,
148-
"css": ElementFinder._find_by_css_selector,
149-
"class": ElementFinder._find_by_class_name,
150-
"jquery": ElementFinder._find_by_jquery_selector,
151-
"sizzle": ElementFinder._find_by_jquery_selector,
152-
"tag": ElementFinder._find_by_tag_name,
153-
"scLocator": ElementFinder._find_by_sc_locator,
154-
"data": ElementFinder._find_by_data_locator,
155-
"default": ElementFinder._find_by_default,
156-
}
157-
self._strategies = NormalizedDict(
158-
initial=strategies, caseless=True, spaceless=True
159-
)
160-
self._default_strategies = list(strategies)
161-
def register(self, strategy_name, strategy_keyword, persist=False):
162-
strategy = CustomLocator(self.ctx, strategy_name, strategy_keyword)
163-
if strategy.name in self._strategies:
164-
raise RuntimeError(
165-
f"The custom locator '{strategy.name}' cannot be registered. "
166-
"A locator of that name already exists."
167-
)
168-
self._strategies[strategy.name] = strategy.find
169-
if is_falsy(persist):
170-
# Unregister after current scope ends
171-
events.on("scope_end", "current", self.unregister, strategy.name)
172-
173-
def unregister(self, strategy_name):
174-
if strategy_name in self._default_strategies:
175-
raise RuntimeError(
176-
f"Cannot unregister the default strategy '{strategy_name}'."
177-
)
178-
if strategy_name not in self._strategies:
179-
raise RuntimeError(
180-
f"Cannot unregister the non-registered strategy '{strategy_name}'."
181-
)
182-
del self._strategies[strategy_name]
183-
140+
def _disallow_webelement_parent(self, element):
141+
if _is_webelement(element):
142+
raise ValueError("This method does not allow WebElement as parent")
184143

185144

186-
class ElementFinder(ContextAware):
145+
class LocatorElementEngine(ContextAware):
187146
def __init__(self, ctx):
188147
ContextAware.__init__(self, ctx)
189-
strategies = {
190-
"identifier": self._find_by_identifier,
191-
"id": self._find_by_id,
192-
"name": self._find_by_name,
193-
"xpath": self._find_by_xpath,
194-
"dom": self._find_by_dom,
195-
"link": self._find_by_link_text,
196-
"partial link": self._find_by_partial_link_text,
197-
"css": self._find_by_css_selector,
198-
"class": self._find_by_class_name,
199-
"jquery": self._find_by_jquery_selector,
200-
"sizzle": self._find_by_jquery_selector,
201-
"tag": self._find_by_tag_name,
202-
"scLocator": self._find_by_sc_locator,
203-
"data": self._find_by_data_locator,
204-
"default": self._find_by_default,
205-
}
206-
self._strategies = NormalizedDict(
207-
initial=strategies, caseless=True, spaceless=True
208-
)
209-
self._default_strategies = list(strategies)
210-
self._key_attrs = {
211-
None: ["@id", "@name"],
212-
"a": [
213-
"@id",
214-
"@name",
215-
"@href",
216-
"normalize-space(descendant-or-self::text())",
217-
],
218-
"img": ["@id", "@name", "@src", "@alt"],
219-
"input": ["@id", "@name", "@value", "@src"],
220-
"button": [
221-
"@id",
222-
"@name",
223-
"@value",
224-
"normalize-space(descendant-or-self::text())",
225-
],
226-
}
227148
self._split_re = re.compile(
228149
r" >> (?=identifier ?[:|=]|id ?[:|=]|name ?[:|=]|xpath ?[:|=]|dom ?[:|=]|link ?[:|=]|partial link ?[:|=]"
229150
r"|css ?[:|=]|class ?[:|=]|jquery ?[:|=]|sizzle ?[:|=]|tag ?[:|=]|scLocator ?[:|=])",
@@ -283,6 +204,108 @@ def _find(self, locator, tag=None, first_only=True, required=True, parent=None):
283204
return elements[0]
284205
return elements
285206

207+
def _get_tag_and_constraints(self, tag):
208+
if tag is None:
209+
return None, {}
210+
tag = tag.lower()
211+
constraints = {}
212+
if tag == "link":
213+
tag = "a"
214+
if tag == "partial link":
215+
tag = "a"
216+
elif tag == "image":
217+
tag = "img"
218+
elif tag == "list":
219+
tag = "select"
220+
elif tag == "radio button":
221+
tag = "input"
222+
constraints["type"] = "radio"
223+
elif tag == "checkbox":
224+
tag = "input"
225+
constraints["type"] = "checkbox"
226+
elif tag == "text field":
227+
tag = "input"
228+
constraints["type"] = [
229+
"date",
230+
"datetime-local",
231+
"email",
232+
"month",
233+
"number",
234+
"password",
235+
"search",
236+
"tel",
237+
"text",
238+
"time",
239+
"url",
240+
"week",
241+
"file",
242+
]
243+
elif tag == "file upload":
244+
tag = "input"
245+
constraints["type"] = "file"
246+
elif tag == "text area":
247+
tag = "textarea"
248+
return tag, constraints
249+
250+
def _parse_locator(self, locator):
251+
if re.match(r"\(*//", locator):
252+
return "xpath", locator
253+
index = self._get_locator_separator_index(locator)
254+
if index != -1:
255+
prefix = locator[:index].strip()
256+
if prefix in self._strategies:
257+
return prefix, locator[index + 1 :].lstrip()
258+
return "default", locator
259+
260+
def _get_locator_separator_index(self, locator):
261+
if "=" not in locator:
262+
return locator.find(":")
263+
if ":" not in locator:
264+
return locator.find("=")
265+
return min(locator.find("="), locator.find(":"))
266+
267+
class Strategies(ContextAware):
268+
def __init__(self, ctx):
269+
ContextAware.__init__(self, ctx)
270+
strategies = {
271+
"identifier": self._find_by_identifier,
272+
"id": self._find_by_id,
273+
"name": self._find_by_name,
274+
"xpath": self._find_by_xpath,
275+
"dom": self._find_by_dom,
276+
"link": self._find_by_link_text,
277+
"partial link": self._find_by_partial_link_text,
278+
"css": self._find_by_css_selector,
279+
"class": self._find_by_class_name,
280+
"jquery": self._find_by_jquery_selector,
281+
"sizzle": self._find_by_jquery_selector,
282+
"tag": self._find_by_tag_name,
283+
"scLocator": self._find_by_sc_locator,
284+
"data": self._find_by_data_locator,
285+
"default": self._find_by_default,
286+
}
287+
self._strategies = NormalizedDict(
288+
initial=strategies, caseless=True, spaceless=True
289+
)
290+
self._default_strategies = list(strategies)
291+
self._key_attrs = {
292+
None: ["@id", "@name"],
293+
"a": [
294+
"@id",
295+
"@name",
296+
"@href",
297+
"normalize-space(descendant-or-self::text())",
298+
],
299+
"img": ["@id", "@name", "@src", "@alt"],
300+
"input": ["@id", "@name", "@value", "@src"],
301+
"button": [
302+
"@id",
303+
"@name",
304+
"@value",
305+
"normalize-space(descendant-or-self::text())",
306+
],
307+
}
308+
286309
def register(self, strategy_name, strategy_keyword, persist=False):
287310
strategy = CustomLocator(self.ctx, strategy_name, strategy_keyword)
288311
if strategy.name in self._strategies:
@@ -306,14 +329,6 @@ def unregister(self, strategy_name):
306329
)
307330
del self._strategies[strategy_name]
308331

309-
def _is_webelement(self, element):
310-
# Hook for unit tests
311-
return isinstance(element, (WebElement, EventFiringWebElement))
312-
313-
def _disallow_webelement_parent(self, element):
314-
if self._is_webelement(element):
315-
raise ValueError("This method does not allow WebElement as parent")
316-
317332
def _find_by_identifier(self, criteria, tag, constraints, parent):
318333
elements = self._normalize(
319334
parent.find_elements(By.ID, criteria)
@@ -424,66 +439,6 @@ def _get_xpath_constraint(self, name, value):
424439
else:
425440
return f"@{name}='{value}'"
426441

427-
def _get_tag_and_constraints(self, tag):
428-
if tag is None:
429-
return None, {}
430-
tag = tag.lower()
431-
constraints = {}
432-
if tag == "link":
433-
tag = "a"
434-
if tag == "partial link":
435-
tag = "a"
436-
elif tag == "image":
437-
tag = "img"
438-
elif tag == "list":
439-
tag = "select"
440-
elif tag == "radio button":
441-
tag = "input"
442-
constraints["type"] = "radio"
443-
elif tag == "checkbox":
444-
tag = "input"
445-
constraints["type"] = "checkbox"
446-
elif tag == "text field":
447-
tag = "input"
448-
constraints["type"] = [
449-
"date",
450-
"datetime-local",
451-
"email",
452-
"month",
453-
"number",
454-
"password",
455-
"search",
456-
"tel",
457-
"text",
458-
"time",
459-
"url",
460-
"week",
461-
"file",
462-
]
463-
elif tag == "file upload":
464-
tag = "input"
465-
constraints["type"] = "file"
466-
elif tag == "text area":
467-
tag = "textarea"
468-
return tag, constraints
469-
470-
def _parse_locator(self, locator):
471-
if re.match(r"\(*//", locator):
472-
return "xpath", locator
473-
index = self._get_locator_separator_index(locator)
474-
if index != -1:
475-
prefix = locator[:index].strip()
476-
if prefix in self._strategies:
477-
return prefix, locator[index + 1 :].lstrip()
478-
return "default", locator
479-
480-
def _get_locator_separator_index(self, locator):
481-
if "=" not in locator:
482-
return locator.find(":")
483-
if ":" not in locator:
484-
return locator.find("=")
485-
return min(locator.find("="), locator.find(":"))
486-
487442
def _element_matches(self, element, tag, constraints):
488443
if not element.tag_name.lower() == tag:
489444
return False

0 commit comments

Comments
 (0)