@@ -22,7 +22,6 @@ def test_anything(self):
22
22
"""
23
23
24
24
import getpass
25
- import json
26
25
import logging
27
26
import math
28
27
import os
@@ -122,6 +121,17 @@ def click(self, selector, by=By.CSS_SELECTOR,
122
121
element = page_actions .wait_for_element_visible (
123
122
self .driver , selector , by , timeout = timeout )
124
123
element .click ()
124
+ except WebDriverException :
125
+ self .wait_for_ready_state_complete ()
126
+ if not by == By .LINK_TEXT :
127
+ # Only use a JavaScript click if not clicking by Link Text
128
+ self .__js_click (selector , by = by )
129
+ else :
130
+ # One more attempt to click on the element
131
+ element = page_actions .wait_for_element_visible (
132
+ self .driver , selector , by , timeout = timeout )
133
+ element .click ()
134
+
125
135
if settings .WAIT_FOR_RSC_ON_CLICKS :
126
136
self .wait_for_ready_state_complete ()
127
137
if self .demo_mode :
@@ -489,6 +499,20 @@ def add_text(self, selector, new_value, by=By.CSS_SELECTOR,
489
499
element .send_keys (Keys .RETURN )
490
500
if settings .WAIT_FOR_RSC_ON_PAGE_LOADS :
491
501
self .wait_for_ready_state_complete ()
502
+ except Exception :
503
+ exc_message = self ._get_exception_message ()
504
+ update = ("Your version of ChromeDriver may be out-of-date! "
505
+ "Please go to "
506
+ "https://sites.google.com/a/chromium.org/chromedriver/ "
507
+ "and download the latest version to your system PATH! "
508
+ "Original Exception Message: %s" % exc_message )
509
+ using_old_chromedriver = False
510
+ if "unknown error: call function result missing" in exc_message :
511
+ using_old_chromedriver = True
512
+ if self .browser == 'chrome' and using_old_chromedriver :
513
+ raise Exception (update )
514
+ else :
515
+ raise Exception (exc_message )
492
516
if self .demo_mode :
493
517
if self .driver .current_url != pre_action_url :
494
518
self ._demo_mode_pause_if_active ()
@@ -512,7 +536,7 @@ def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
512
536
new_value - the new value for setting the text field
513
537
by - the type of selector to search by (Default: CSS)
514
538
timeout - how long to wait for the selector to be visible
515
- retry - if True, use jquery if the selenium text update fails
539
+ retry - if True, use JS if the selenium text update fails
516
540
"""
517
541
if self .timeout_multiplier and timeout == settings .LARGE_TIMEOUT :
518
542
timeout = self ._get_new_timeout (timeout )
@@ -531,6 +555,8 @@ def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
531
555
element = self .wait_for_element_visible (
532
556
selector , by = by , timeout = timeout )
533
557
element .clear ()
558
+ except Exception :
559
+ pass # Clearing the text field first isn't critical
534
560
self ._demo_mode_pause_if_active (tiny = True )
535
561
pre_action_url = self .driver .current_url
536
562
try :
@@ -556,10 +582,23 @@ def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
556
582
element .send_keys (Keys .RETURN )
557
583
if settings .WAIT_FOR_RSC_ON_PAGE_LOADS :
558
584
self .wait_for_ready_state_complete ()
585
+ except Exception :
586
+ exc_message = self ._get_exception_message ()
587
+ update = ("Your version of ChromeDriver may be out-of-date! "
588
+ "Please go to "
589
+ "https://sites.google.com/a/chromium.org/chromedriver/ "
590
+ "and download the latest version to your system PATH! "
591
+ "Original Exception Message: %s" % exc_message )
592
+ using_old_chromedriver = False
593
+ if "unknown error: call function result missing" in exc_message :
594
+ using_old_chromedriver = True
595
+ if self .browser == 'chrome' and using_old_chromedriver :
596
+ raise Exception (update )
597
+ else :
598
+ raise Exception (exc_message )
559
599
if (retry and element .get_attribute ('value' ) != new_value and (
560
600
not new_value .endswith ('\n ' ))):
561
- logging .debug ('update_text_value is falling back to jQuery!' )
562
- selector = re .escape (selector )
601
+ logging .debug ('update_text() is falling back to JavaScript!' )
563
602
self .set_value (selector , new_value , by = by )
564
603
if self .demo_mode :
565
604
if self .driver .current_url != pre_action_url :
@@ -772,7 +811,7 @@ def bring_to_front(self, selector, by=By.CSS_SELECTOR):
772
811
773
812
def highlight (self , selector , by = By .CSS_SELECTOR ,
774
813
loops = settings .HIGHLIGHTS , scroll = True ):
775
- """ This method uses fancy javascript to highlight an element.
814
+ """ This method uses fancy JavaScript to highlight an element.
776
815
Used during demo_mode.
777
816
@Params
778
817
selector - the selector of the element to find
@@ -930,13 +969,37 @@ def scroll_click(self, selector, by=By.CSS_SELECTOR):
930
969
def click_xpath (self , xpath ):
931
970
self .click (xpath , by = By .XPATH )
932
971
972
+ def js_click (self , selector , by = By .CSS_SELECTOR ):
973
+ """ Clicks an element using pure JS. Does not use jQuery. """
974
+ selector , by = self ._recalculate_selector (selector , by )
975
+ if by == By .LINK_TEXT :
976
+ message = (
977
+ "Pure JavaScript doesn't support clicking by Link Text. "
978
+ "You may want to use self.jquery_click() instead, which "
979
+ "allows this with :contains(), assuming jQuery isn't blocked. "
980
+ "For now, self.js_click() will use a regular WebDriver click." )
981
+ logging .debug (message )
982
+ self .click (selector , by = by )
983
+ return
984
+ element = self .wait_for_element_present (
985
+ selector , by = by , timeout = settings .SMALL_TIMEOUT )
986
+ if self .is_element_visible (selector , by = by ):
987
+ self ._demo_mode_highlight_if_active (selector , by )
988
+ if not self .demo_mode :
989
+ self ._scroll_to_element (element )
990
+ css_selector = self .convert_to_css_selector (selector , by = by )
991
+ css_selector = re .escape (css_selector )
992
+ self .__js_click (selector , by = by ) # The real "magic" happens here
993
+ self ._demo_mode_pause_if_active ()
994
+
933
995
def jquery_click (self , selector , by = By .CSS_SELECTOR ):
996
+ """ Clicks an element using jQuery. Different from using pure JS. """
934
997
selector , by = self ._recalculate_selector (selector , by )
935
- selector = self .convert_to_css_selector (selector , by = by )
936
998
self .wait_for_element_present (
937
999
selector , by = by , timeout = settings .SMALL_TIMEOUT )
938
1000
if self .is_element_visible (selector , by = by ):
939
1001
self ._demo_mode_highlight_if_active (selector , by )
1002
+ selector = self .convert_to_css_selector (selector , by = by )
940
1003
selector = self ._make_css_match_first_element_only (selector )
941
1004
click_script = """jQuery('%s')[0].click()""" % selector
942
1005
self .safe_execute_script (click_script )
@@ -1056,7 +1119,8 @@ def convert_xpath_to_css(self, xpath):
1056
1119
def convert_to_css_selector (self , selector , by ):
1057
1120
""" This method converts a selector to a CSS_SELECTOR.
1058
1121
jQuery commands require a CSS_SELECTOR for finding elements.
1059
- This method should only be used for jQuery actions. """
1122
+ This method should only be used for jQuery/JavaScript actions.
1123
+ Pure JavaScript doesn't support using a:contains("LINK_TEXT"). """
1060
1124
if by == By .CSS_SELECTOR :
1061
1125
return selector
1062
1126
elif by == By .ID :
@@ -1080,21 +1144,21 @@ def convert_to_css_selector(self, selector, by):
1080
1144
1081
1145
def set_value (self , selector , new_value , by = By .CSS_SELECTOR ,
1082
1146
timeout = settings .LARGE_TIMEOUT ):
1083
- """ This method uses jQuery to update a text field.
1084
- Similar to jquery_update_text_value(), but the element
1085
- doesn't need to be officially visible to work. """
1147
+ """ This method uses JavaScript to update a text field. """
1086
1148
if self .timeout_multiplier and timeout == settings .LARGE_TIMEOUT :
1087
1149
timeout = self ._get_new_timeout (timeout )
1088
1150
if page_utils .is_xpath_selector (selector ):
1089
1151
by = By .XPATH
1090
1152
orginal_selector = selector
1091
- selector = self .convert_to_css_selector (selector , by = by )
1092
- self ._demo_mode_highlight_if_active (selector , by )
1093
- self .scroll_to (selector , by = by , timeout = timeout )
1094
- value = json .dumps (new_value )
1095
- selector = self ._make_css_match_first_element_only (selector )
1096
- set_value_script = """jQuery('%s').val(%s)""" % (selector , value )
1097
- self .safe_execute_script (set_value_script )
1153
+ css_selector = self .convert_to_css_selector (selector , by = by )
1154
+ self ._demo_mode_highlight_if_active (orginal_selector , by )
1155
+ if not self .demo_mode :
1156
+ self .scroll_to (orginal_selector , by = by , timeout = timeout )
1157
+ value = re .escape (new_value )
1158
+ css_selector = re .escape (css_selector )
1159
+ script = ("""document.querySelector('%s').value='%s';"""
1160
+ % (css_selector , value ))
1161
+ self .execute_script (script )
1098
1162
if new_value .endswith ('\n ' ):
1099
1163
element = self .wait_for_element_present (
1100
1164
orginal_selector , by = by , timeout = timeout )
@@ -1103,12 +1167,20 @@ def set_value(self, selector, new_value, by=By.CSS_SELECTOR,
1103
1167
self .wait_for_ready_state_complete ()
1104
1168
self ._demo_mode_pause_if_active ()
1105
1169
1170
+ def js_update_text (self , selector , new_value , by = By .CSS_SELECTOR ,
1171
+ timeout = settings .LARGE_TIMEOUT ):
1172
+ """ Same as self.set_value() """
1173
+ if self .timeout_multiplier and timeout == settings .LARGE_TIMEOUT :
1174
+ timeout = self ._get_new_timeout (timeout )
1175
+ self .set_value (
1176
+ selector , new_value , by = by , timeout = timeout )
1177
+
1106
1178
def jquery_update_text_value (self , selector , new_value , by = By .CSS_SELECTOR ,
1107
1179
timeout = settings .LARGE_TIMEOUT ):
1108
1180
""" This method uses jQuery to update a text field.
1109
1181
If the new_value string ends with the newline character,
1110
1182
WebDriver will finish the call, which simulates pressing
1111
- {Enter/Return} after the text is entered. """
1183
+ {Enter/Return} after the text is entered. """
1112
1184
if self .timeout_multiplier and timeout == settings .LARGE_TIMEOUT :
1113
1185
timeout = self ._get_new_timeout (timeout )
1114
1186
if page_utils .is_xpath_selector (selector ):
@@ -1128,7 +1200,7 @@ def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
1128
1200
1129
1201
def jquery_update_text (self , selector , new_value , by = By .CSS_SELECTOR ,
1130
1202
timeout = settings .LARGE_TIMEOUT ):
1131
- """ The shorter version of jquery_update_text_value()
1203
+ """ The shorter version of self. jquery_update_text_value()
1132
1204
(The longer version remains for backwards compatibility.) """
1133
1205
if self .timeout_multiplier and timeout == settings .LARGE_TIMEOUT :
1134
1206
timeout = self ._get_new_timeout (timeout )
@@ -1138,6 +1210,9 @@ def jquery_update_text(self, selector, new_value, by=By.CSS_SELECTOR,
1138
1210
def hover_on_element (self , selector , by = By .CSS_SELECTOR ):
1139
1211
if page_utils .is_xpath_selector (selector ):
1140
1212
by = By .XPATH
1213
+ if page_utils .is_link_text_selector (selector ):
1214
+ selector = page_utils .get_link_text_from_selector (selector )
1215
+ by = By .LINK_TEXT
1141
1216
self .wait_for_element_visible (
1142
1217
selector , by = by , timeout = settings .SMALL_TIMEOUT )
1143
1218
self ._demo_mode_highlight_if_active (selector , by )
@@ -1154,6 +1229,14 @@ def hover_and_click(self, hover_selector, click_selector,
1154
1229
hover_by = By .XPATH
1155
1230
if page_utils .is_xpath_selector (click_selector ):
1156
1231
click_by = By .XPATH
1232
+ if page_utils .is_link_text_selector (hover_selector ):
1233
+ hover_selector = page_utils .get_link_text_from_selector (
1234
+ hover_selector )
1235
+ hover_by = By .LINK_TEXT
1236
+ if page_utils .is_link_text_selector (click_selector ):
1237
+ click_selector = page_utils .get_link_text_from_selector (
1238
+ click_selector )
1239
+ click_by = By .LINK_TEXT
1157
1240
self .wait_for_element_visible (
1158
1241
hover_selector , by = hover_by , timeout = timeout )
1159
1242
self ._demo_mode_highlight_if_active (hover_selector , hover_by )
@@ -1740,6 +1823,24 @@ def process_checks(self, print_only=False):
1740
1823
1741
1824
############
1742
1825
1826
+ def __js_click (self , selector , by = By .CSS_SELECTOR ):
1827
+ """ Clicks an element using pure JS. Does not use jQuery. """
1828
+ selector , by = self ._recalculate_selector (selector , by )
1829
+ css_selector = self .convert_to_css_selector (selector , by = by )
1830
+ css_selector = re .escape (css_selector )
1831
+ script = ("""var simulateClick = function (elem) {
1832
+ var evt = new MouseEvent('click', {
1833
+ bubbles: true,
1834
+ cancelable: true,
1835
+ view: window
1836
+ });
1837
+ var canceled = !elem.dispatchEvent(evt);
1838
+ };
1839
+ var someLink = document.querySelector('%s');
1840
+ simulateClick(someLink);"""
1841
+ % css_selector )
1842
+ self .execute_script (script )
1843
+
1743
1844
def _get_href_from_link_text (self , link_text , hard_fail = True ):
1744
1845
href = self .get_link_attribute (link_text , "href" , hard_fail )
1745
1846
if not href :
@@ -1815,8 +1916,6 @@ def _pick_select_option(self, dropdown_selector, option,
1815
1916
else :
1816
1917
self ._demo_mode_pause_if_active (tiny = True )
1817
1918
1818
- ############
1819
-
1820
1919
def _recalculate_selector (self , selector , by ):
1821
1920
# Try to determine the type of selector automatically
1822
1921
if page_utils .is_xpath_selector (selector ):
0 commit comments