@@ -109,12 +109,13 @@ def click(self, selector, by=By.CSS_SELECTOR,
109
109
if self .timeout_multiplier and timeout == settings .SMALL_TIMEOUT :
110
110
timeout = self .__get_new_timeout (timeout )
111
111
selector , by = self .__recalculate_selector (selector , by )
112
- if page_utils .is_link_text_selector (selector ):
112
+ if page_utils .is_link_text_selector (selector ) or by == By . LINK_TEXT :
113
113
if not self .is_link_text_visible (selector ):
114
114
# Handle a special case of links hidden in dropdowns
115
115
self .click_link_text (selector , timeout = timeout )
116
116
return
117
- if page_utils .is_partial_link_text_selector (selector ):
117
+ if page_utils .is_partial_link_text_selector (selector ) or (
118
+ by == By .PARTIAL_LINK_TEXT ):
118
119
if not self .is_partial_link_text_visible (selector ):
119
120
# Handle a special case of partial links hidden in dropdowns
120
121
self .click_partial_link_text (selector , timeout = timeout )
@@ -232,6 +233,17 @@ def is_link_text_present(self, link_text):
232
233
return True
233
234
return False
234
235
236
+ def is_partial_link_text_present (self , link_text ):
237
+ """ Returns True if the partial link appears in the HTML of the page.
238
+ The element doesn't need to be visible,
239
+ such as elements hidden inside a dropdown selection. """
240
+ soup = self .get_beautiful_soup ()
241
+ html_links = soup .find_all ('a' )
242
+ for html_link in html_links :
243
+ if link_text .strip () in html_link .text .strip ():
244
+ return True
245
+ return False
246
+
235
247
def get_link_attribute (self , link_text , attribute , hard_fail = True ):
236
248
""" Finds a link by link text and then returns the attribute's value.
237
249
If the link text or attribute cannot be found, an exception will
@@ -254,6 +266,31 @@ def get_link_attribute(self, link_text, attribute, hard_fail=True):
254
266
else :
255
267
return None
256
268
269
+ def get_partial_link_attribute (self , link_text , attribute , hard_fail = True ):
270
+ """ Finds a link by partial link text and then returns the attribute's
271
+ value. If the partial link text or attribute cannot be found, an
272
+ exception will get raised if hard_fail is True (otherwise None
273
+ is returned). """
274
+ soup = self .get_beautiful_soup ()
275
+ html_links = soup .find_all ('a' )
276
+ for html_link in html_links :
277
+ if link_text .strip () in html_link .text .strip ():
278
+ if html_link .has_attr (attribute ):
279
+ attribute_value = html_link .get (attribute )
280
+ return attribute_value
281
+ if hard_fail :
282
+ raise Exception (
283
+ 'Unable to find attribute {%s} from '
284
+ 'partial link text {%s}!'
285
+ % (attribute , link_text ))
286
+ else :
287
+ return None
288
+ if hard_fail :
289
+ raise Exception (
290
+ "Partial Link text {%s} was not found!" % link_text )
291
+ else :
292
+ return None
293
+
257
294
def wait_for_link_text_present (self , link_text ,
258
295
timeout = settings .SMALL_TIMEOUT ):
259
296
start_ms = time .time () * 1000.0
@@ -273,6 +310,25 @@ def wait_for_link_text_present(self, link_text,
273
310
"Link text {%s} was not present after %s seconds!" % (
274
311
link_text , timeout ))
275
312
313
+ def wait_for_partial_link_text_present (self , link_text ,
314
+ timeout = settings .SMALL_TIMEOUT ):
315
+ start_ms = time .time () * 1000.0
316
+ stop_ms = start_ms + (timeout * 1000.0 )
317
+ for x in range (int (timeout * 5 )):
318
+ try :
319
+ if not self .is_partial_link_text_present (link_text ):
320
+ raise Exception (
321
+ "Partial Link text {%s} was not found!" % link_text )
322
+ return
323
+ except Exception :
324
+ now_ms = time .time () * 1000.0
325
+ if now_ms >= stop_ms :
326
+ break
327
+ time .sleep (0.2 )
328
+ raise Exception (
329
+ "Partial Link text {%s} was not present after %s seconds!" % (
330
+ link_text , timeout ))
331
+
276
332
def click_link_text (self , link_text , timeout = settings .SMALL_TIMEOUT ):
277
333
""" This method clicks link text on a page """
278
334
# If using phantomjs, might need to extract and open the link directly
@@ -387,20 +443,68 @@ def click_partial_link_text(self, partial_link_text,
387
443
'{%s}' % partial_link_text )
388
444
raise Exception (
389
445
"Partial link text {%s} was not found!" % partial_link_text )
390
- # Not using phantomjs
391
- element = self .wait_for_partial_link_text (
392
- partial_link_text , timeout = timeout )
393
- self .__demo_mode_highlight_if_active (
394
- partial_link_text , by = By .PARTIAL_LINK_TEXT )
395
- pre_action_url = self .driver .current_url
446
+ if not self .is_partial_link_text_present (partial_link_text ):
447
+ self .wait_for_partial_link_text_present (
448
+ partial_link_text , timeout = timeout )
449
+ pre_action_url = self .get_current_url ()
396
450
try :
397
- element .click ()
398
- except (StaleElementReferenceException , ENI_Exception ):
399
- self .wait_for_ready_state_complete ()
400
- time .sleep (0.05 )
401
451
element = self .wait_for_partial_link_text (
402
- partial_link_text , timeout = timeout )
403
- element .click ()
452
+ partial_link_text , timeout = 0.2 )
453
+ self .__demo_mode_highlight_if_active (
454
+ partial_link_text , by = By .LINK_TEXT )
455
+ try :
456
+ element .click ()
457
+ except (StaleElementReferenceException , ENI_Exception ):
458
+ self .wait_for_ready_state_complete ()
459
+ time .sleep (0.05 )
460
+ element = self .wait_for_partial_link_text (
461
+ partial_link_text , timeout = timeout )
462
+ element .click ()
463
+ except Exception :
464
+ found_css = False
465
+ text_id = self .get_partial_link_attribute (
466
+ partial_link_text , "id" , False )
467
+ if text_id :
468
+ link_css = '[id="%s"]' % partial_link_text
469
+ found_css = True
470
+
471
+ if not found_css :
472
+ href = self .__get_href_from_partial_link_text (
473
+ partial_link_text , False )
474
+ if href :
475
+ if href .startswith ('/' ) or page_utils .is_valid_url (href ):
476
+ link_css = '[href="%s"]' % href
477
+ found_css = True
478
+
479
+ if not found_css :
480
+ ngclick = self .get_partial_link_attribute (
481
+ partial_link_text , "ng-click" , False )
482
+ if ngclick :
483
+ link_css = '[ng-click="%s"]' % ngclick
484
+ found_css = True
485
+
486
+ if not found_css :
487
+ onclick = self .get_partial_link_attribute (
488
+ partial_link_text , "onclick" , False )
489
+ if onclick :
490
+ link_css = '[onclick="%s"]' % onclick
491
+ found_css = True
492
+
493
+ success = False
494
+ if found_css :
495
+ if self .is_element_visible (link_css ):
496
+ self .click (link_css )
497
+ success = True
498
+ else :
499
+ # The link text might be hidden under a dropdown menu
500
+ success = self .__click_dropdown_partial_link_text (
501
+ partial_link_text , link_css )
502
+
503
+ if not success :
504
+ element = self .wait_for_link_text_visible (
505
+ partial_link_text , timeout = settings .MINI_TIMEOUT )
506
+ element .click ()
507
+
404
508
if settings .WAIT_FOR_RSC_ON_CLICKS :
405
509
self .wait_for_ready_state_complete ()
406
510
if self .demo_mode :
@@ -3064,29 +3168,75 @@ def __get_href_from_link_text(self, link_text, hard_fail=True):
3064
3168
def __click_dropdown_link_text (self , link_text , link_css ):
3065
3169
""" When a link may be hidden under a dropdown menu, use this. """
3066
3170
soup = self .get_beautiful_soup ()
3067
- drop_down_list = soup .select ('[class*=dropdown]' )
3068
- for item in soup .select ('[class*=HeaderMenu]' ):
3069
- drop_down_list .append (item )
3070
- for item in soup .select ('[class*=menu-item]' ):
3171
+ drop_down_list = []
3172
+ for item in soup .select ('li[class]' ):
3071
3173
drop_down_list .append (item )
3072
- for item in soup .select ('[class*=chevron]' ):
3174
+ csstype = link_css .split ('[' )[1 ].split ('=' )[0 ]
3175
+ for item in drop_down_list :
3176
+ item_text_list = item .text .split ('\n ' )
3177
+ if link_text in item_text_list and csstype in item .decode ():
3178
+ dropdown_css = ""
3179
+ try :
3180
+ for css_class in item ['class' ]:
3181
+ dropdown_css += '.'
3182
+ dropdown_css += css_class
3183
+ except Exception :
3184
+ continue
3185
+ dropdown_css = item .name + dropdown_css
3186
+ matching_dropdowns = self .find_visible_elements (dropdown_css )
3187
+ for dropdown in matching_dropdowns :
3188
+ # The same class names might be used for multiple dropdowns
3189
+ try :
3190
+ if dropdown .is_displayed ():
3191
+ page_actions .hover_element_and_click (
3192
+ self .driver , dropdown , link_text ,
3193
+ click_by = By .LINK_TEXT , timeout = 0.12 )
3194
+ return True
3195
+ except Exception :
3196
+ pass
3197
+ return False
3198
+
3199
+ def __get_href_from_partial_link_text (self , link_text , hard_fail = True ):
3200
+ href = self .get_partial_link_attribute (link_text , "href" , hard_fail )
3201
+ if not href :
3202
+ return None
3203
+ if href .startswith ('//' ):
3204
+ link = "http:" + href
3205
+ elif href .startswith ('/' ):
3206
+ url = self .driver .current_url
3207
+ domain_url = self .get_domain_url (url )
3208
+ link = domain_url + href
3209
+ else :
3210
+ link = href
3211
+ return link
3212
+
3213
+ def __click_dropdown_partial_link_text (self , link_text , link_css ):
3214
+ """ When a partial link may be hidden under a dropdown, use this. """
3215
+ soup = self .get_beautiful_soup ()
3216
+ drop_down_list = []
3217
+ for item in soup .select ('li[class]' ):
3073
3218
drop_down_list .append (item )
3074
3219
csstype = link_css .split ('[' )[1 ].split ('=' )[0 ]
3075
3220
for item in drop_down_list :
3076
- if link_text in item .text .split ('\n ' ) and csstype in item .decode ():
3221
+ item_text_list = item .text .split ('\n ' )
3222
+ if link_text in item_text_list and csstype in item .decode ():
3077
3223
dropdown_css = ""
3078
- for css_class in item ['class' ]:
3079
- dropdown_css += '.'
3080
- dropdown_css += css_class
3224
+ try :
3225
+ for css_class in item ['class' ]:
3226
+ dropdown_css += '.'
3227
+ dropdown_css += css_class
3228
+ except Exception :
3229
+ continue
3081
3230
dropdown_css = item .name + dropdown_css
3082
3231
matching_dropdowns = self .find_visible_elements (dropdown_css )
3083
3232
for dropdown in matching_dropdowns :
3084
3233
# The same class names might be used for multiple dropdowns
3085
3234
try :
3086
- page_actions .hover_element_and_click (
3087
- self .driver , dropdown , link_text ,
3088
- click_by = By .LINK_TEXT , timeout = 0.2 )
3089
- return True
3235
+ if dropdown .is_displayed ():
3236
+ page_actions .hover_element_and_click (
3237
+ self .driver , dropdown , link_text ,
3238
+ click_by = By .PARTIAL_LINK_TEXT , timeout = 0.12 )
3239
+ return True
3090
3240
except Exception :
3091
3241
pass
3092
3242
return False
0 commit comments