3131
3232class 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