Skip to content

Commit da3c1d0

Browse files
authored
Merge pull request #235 from seleniumbase/masterqa-upgrade
MasterQA Upgrade
2 parents 9b98ef6 + 41060bd commit da3c1d0

File tree

15 files changed

+279
-119
lines changed

15 files changed

+279
-119
lines changed

examples/master_qa/basic_masterqa_test.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
class MasterQATests(MasterQA):
55

66
def test_masterqa(self):
7-
self.open("http://xkcd.com/1700/")
7+
self.open("https://xkcd.com/1700/")
88
self.verify("Do you see a webcomic?")
9-
self.click_link_text('Store')
10-
self.click_link_text('all the things')
11-
self.verify("Do you see items for sale?")
12-
self.update_text("input.search-input", "Robots\n")
13-
self.verify("Do you see robots in the search results?")
9+
self.highlight_click('link=Blag')
10+
self.verify('Do you see a blog archive?')
11+
self.highlight_update_text("input#s", "Dragons\n")
12+
self.verify('Do you see "dragons" in the search results?')

examples/master_qa/masterqa_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
class MasterQATests(MasterQA):
55

66
def test_xkcd(self):
7-
self.open("http://xkcd.com/1512/")
7+
self.open("https://xkcd.com/1512/")
88
for i in range(4):
99
self.click('a[rel="next"]')
1010
for i in range(3):
1111
self.click('a[rel="prev"]')
1212
self.verify()
13-
self.open("http://xkcd.com/1520/")
13+
self.open("https://xkcd.com/1520/")
1414
for i in range(2):
1515
self.click('a[rel="next"]')
1616
self.verify("Can you find the moon?")
@@ -19,7 +19,7 @@ def test_xkcd(self):
1919
self.click_link_text('Blag')
2020
self.update_text("input#s", "Robots!\n")
2121
self.verify("Does it say 'Hooray robots' on the page?")
22-
self.open("http://xkcd.com/213/")
22+
self.open("https://xkcd.com/213/")
2323
for i in range(5):
2424
self.click('a[rel="prev"]')
2525
self.verify("Does the page say 'Abnormal Expressions'?")

help_docs/method_summary.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ self.play_tour(name=None)
114114

115115
self.export_tour(name=None, filename="my_tour.js")
116116

117+
self.activate_jquery_confirm()
118+
117119
self.activate_messenger()
118120

119121
self.post_message(message, style="info", duration=None)

seleniumbase/config/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878

7979
# The time delay (in seconds) before the validation pop-up appears
8080
# This value can be overwritten on the command line. Ex: --verify_delay=0.5
81-
MASTERQA_WAIT_TIME_BEFORE_VERIFY = 1.0
81+
MASTERQA_WAIT_TIME_BEFORE_VERIFY = 0.75
8282

8383
# If True, the automation will start in full-screen mode
8484
MASTERQA_START_IN_FULL_SCREEN_MODE = False

seleniumbase/core/tour_helper.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from seleniumbase.fixtures import constants
1111
from seleniumbase.fixtures import js_utils
1212
from seleniumbase.fixtures import page_actions
13-
from seleniumbase.fixtures import page_utils
1413

1514

1615
def raise_unable_to_load_jquery_exception(driver):
@@ -691,7 +690,7 @@ def export_tour(tour_steps, name=None, filename="my_tour.js"):
691690
bootstrap_tour_js = constants.BootstrapTour.MIN_JS
692691
backdrop_style = style_sheet.bt_backdrop_style
693692
backdrop_style = backdrop_style.replace('\n', '')
694-
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
693+
backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style)
695694
instructions += 'injectJS("%s");' % jquery_js
696695
instructions += '\n\n//////// Resources - Load 2 ////////\n\n'
697696
instructions += 'injectCSS("%s");\n' % bootstrap_tour_css
@@ -703,7 +702,7 @@ def export_tour(tour_steps, name=None, filename="my_tour.js"):
703702
hopscotch_js = constants.Hopscotch.MIN_JS
704703
backdrop_style = style_sheet.hops_backdrop_style
705704
backdrop_style = backdrop_style.replace('\n', '')
706-
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
705+
backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style)
707706
instructions += 'injectCSS("%s");\n' % hopscotch_css
708707
instructions += 'injectStyle("%s");\n' % backdrop_style
709708
instructions += 'injectJS("%s");' % hopscotch_js
@@ -727,7 +726,7 @@ def export_tour(tour_steps, name=None, filename="my_tour.js"):
727726
spinner_css = constants.Messenger.SPINNER_CSS
728727
backdrop_style = style_sheet.sh_backdrop_style
729728
backdrop_style = backdrop_style.replace('\n', '')
730-
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
729+
backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style)
731730
instructions += 'injectCSS("%s");\n' % spinner_css
732731
instructions += 'injectJS("%s");\n' % jquery_js
733732
instructions += 'injectJS("%s");' % tether_js

seleniumbase/fixtures/base_case.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -748,10 +748,10 @@ def activate_jquery(self):
748748
js_utils.activate_jquery(self.driver)
749749

750750
def __are_quotes_escaped(self, string):
751-
return page_utils.are_quotes_escaped(string)
751+
return js_utils.are_quotes_escaped(string)
752752

753753
def __escape_quotes_if_needed(self, string):
754-
return page_utils.escape_quotes_if_needed(string)
754+
return js_utils.escape_quotes_if_needed(string)
755755

756756
def create_tour(self, name=None, theme=None):
757757
""" Creates a tour for a website. By default, the Shepherd Javascript
@@ -1173,6 +1173,9 @@ def export_tour(self, name=None, filename="my_tour.js"):
11731173
save the tour to. """
11741174
tour_helper.export_tour(self._tour_steps, name=name, filename=filename)
11751175

1176+
def activate_jquery_confirm(self):
1177+
js_utils.activate_jquery_confirm(self.driver)
1178+
11761179
def activate_messenger(self):
11771180
js_utils.activate_messenger(self.driver)
11781181

@@ -1453,7 +1456,7 @@ def ad_block(self):
14531456

14541457
def jq_format(self, code):
14551458
# DEPRECATED - Use re.escape() instead, which does the action you want.
1456-
return page_utils._jq_format(code)
1459+
return js_utils._jq_format(code)
14571460

14581461
def get_domain_url(self, url):
14591462
return page_utils.get_domain_url(url)

seleniumbase/fixtures/constants.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ class IntroJS:
8585
"intro.js/%s/intro.min.js" % VER)
8686

8787

88+
class JqueryConfirm:
89+
VER = "3.3.2"
90+
MIN_CSS = ("//cdnjs.cloudflare.com/ajax/libs/"
91+
"jquery-confirm/%s/jquery-confirm.min.css" % VER)
92+
MIN_JS = ("//cdnjs.cloudflare.com/ajax/libs/"
93+
"jquery-confirm/%s/jquery-confirm.min.js" % VER)
94+
95+
8896
class Shepherd:
8997
VER = "1.8.1"
9098
MIN_JS = ("//cdnjs.cloudflare.com/ajax/libs/"

seleniumbase/fixtures/js_utils.py

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
from selenium.common.exceptions import WebDriverException
99
from seleniumbase.config import settings
1010
from seleniumbase.fixtures import constants
11-
from seleniumbase.fixtures import page_utils
11+
12+
13+
def is_jquery_activated(driver):
14+
try:
15+
driver.execute_script("jQuery('html')") # Fails if jq is not defined
16+
return True
17+
except Exception:
18+
return False
1219

1320

1421
def activate_jquery(driver):
@@ -42,6 +49,37 @@ def activate_jquery(driver):
4249
'''directive. ''' % driver.current_url)
4350

4451

52+
def are_quotes_escaped(string):
53+
if (string.count("\\'") != string.count("'") or
54+
string.count('\\"') != string.count('"')):
55+
return True
56+
return False
57+
58+
59+
def escape_quotes_if_needed(string):
60+
"""
61+
re.escape() works differently in Python 3.7.0 than earlier versions:
62+
63+
Python 3.6.5:
64+
>>> import re
65+
>>> re.escape('"')
66+
'\\"'
67+
68+
Python 3.7.0:
69+
>>> import re
70+
>>> re.escape('"')
71+
'"'
72+
73+
SeleniumBase needs quotes to be properly escaped for Javascript calls.
74+
"""
75+
if are_quotes_escaped(string):
76+
if string.count("'") != string.count("\\'"):
77+
string = string.replace("'", "\\'")
78+
if string.count('"') != string.count('\\"'):
79+
string = string.replace('"', '\\"')
80+
return string
81+
82+
4583
def safe_execute_script(driver, script):
4684
""" When executing a script that contains a jQuery command,
4785
it's important that the jQuery library has been loaded first.
@@ -125,7 +163,7 @@ def wait_for_css_query_selector(
125163
for x in range(int(timeout * 10)):
126164
try:
127165
selector = re.escape(selector)
128-
selector = page_utils.escape_quotes_if_needed(selector)
166+
selector = escape_quotes_if_needed(selector)
129167
element = driver.execute_script(
130168
"""return document.querySelector('%s')""" % selector)
131169
if element:
@@ -230,7 +268,7 @@ def add_css_link(driver, css_link):
230268
head.appendChild(link);
231269
}
232270
injectCSS("%s");""")
233-
css_link = page_utils.escape_quotes_if_needed(css_link)
271+
css_link = escape_quotes_if_needed(css_link)
234272
driver.execute_script(script_to_add_css % css_link)
235273

236274

@@ -247,7 +285,7 @@ def add_js_link(driver, js_link):
247285
head.appendChild(script);
248286
}
249287
injectJS("%s");""")
250-
js_link = page_utils.escape_quotes_if_needed(js_link)
288+
js_link = escape_quotes_if_needed(js_link)
251289
driver.execute_script(script_to_add_js % js_link)
252290

253291

@@ -262,7 +300,7 @@ def add_css_style(driver, css_style):
262300
}
263301
injectStyle("%s");""")
264302
css_style = css_style.replace('\n', '')
265-
css_style = page_utils.escape_quotes_if_needed(css_style)
303+
css_style = escape_quotes_if_needed(css_style)
266304
driver.execute_script(add_css_style_script % css_style)
267305

268306

@@ -278,7 +316,7 @@ def add_js_code_from_link(driver, js_link):
278316
'''s.appendChild(document.createTextNode("%s"));'''
279317
'''h.appendChild(s);''')
280318
js_code = js_code.replace('\n', '')
281-
js_code = page_utils.escape_quotes_if_needed(js_code)
319+
js_code = escape_quotes_if_needed(js_code)
282320
driver.execute_script(add_js_code_script % js_code)
283321

284322

@@ -299,6 +337,35 @@ def add_meta_tag(driver, http_equiv=None, content=None):
299337
driver.execute_script(script_to_add_meta)
300338

301339

340+
def is_jquery_confirm_activated(driver):
341+
try:
342+
driver.execute_script("jconfirm") # Fails if jq_confirm is not defined
343+
return True
344+
except Exception:
345+
return False
346+
347+
348+
def activate_jquery_confirm(driver):
349+
jquery_js = constants.JQuery.MIN_JS
350+
jq_confirm_css = constants.JqueryConfirm.MIN_CSS
351+
jq_confirm_js = constants.JqueryConfirm.MIN_JS
352+
353+
if not is_jquery_activated(driver):
354+
add_js_link(driver, jquery_js)
355+
add_css_link(driver, jq_confirm_css)
356+
add_js_link(driver, jq_confirm_js)
357+
358+
for x in range(int(settings.MINI_TIMEOUT * 10.0)):
359+
# jQuery-Confirm needs a small amount of time to load & activate.
360+
try:
361+
driver.execute_script("jconfirm")
362+
wait_for_ready_state_complete(driver)
363+
wait_for_angularjs(driver)
364+
return
365+
except Exception:
366+
time.sleep(0.1)
367+
368+
302369
def activate_messenger(driver):
303370
jquery_js = constants.JQuery.MIN_JS
304371
messenger_css = constants.Messenger.MIN_CSS
@@ -395,7 +462,7 @@ def post_message(driver, message, msg_dur, style="info", duration=None):
395462
else:
396463
duration = msg_dur
397464
message = re.escape(message)
398-
message = page_utils.escape_quotes_if_needed(message)
465+
message = escape_quotes_if_needed(message)
399466
messenger_script = ('''Messenger().post({message: "%s", type: "%s", '''
400467
'''hideAfter: %s, hideOnNavigate: true});'''
401468
% (message, style, duration))
@@ -548,3 +615,18 @@ def slow_scroll_to_element(driver, element, browser):
548615
if distance > 430 or distance < -300:
549616
# Add small recovery time for long-distance slow-scrolling
550617
time.sleep(0.162)
618+
619+
620+
def _jq_format(code):
621+
"""
622+
DEPRECATED - Use re.escape() instead, which performs the intended action.
623+
Use before throwing raw code such as 'div[tab="advanced"]' into jQuery.
624+
Selectors with quotes inside of quotes would otherwise break jQuery.
625+
If you just want to escape quotes, there's escape_quotes_if_needed().
626+
This is similar to "json.dumps(value)", but with one less layer of quotes.
627+
"""
628+
code = code.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n')
629+
code = code.replace('\"', '\\\"').replace('\'', '\\\'')
630+
code = code.replace('\v', '\\v').replace('\a', '\\a').replace('\f', '\\f')
631+
code = code.replace('\b', '\\b').replace(r'\u', '\\u').replace('\r', '\\r')
632+
return code

seleniumbase/fixtures/page_utils.py

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -83,55 +83,9 @@ def _save_data_as(data, destination_folder, file_name):
8383
out_file.close()
8484

8585

86-
def are_quotes_escaped(string):
87-
if (string.count("\\'") != string.count("'") or
88-
string.count('\\"') != string.count('"')):
89-
return True
90-
return False
91-
92-
93-
def escape_quotes_if_needed(string):
94-
"""
95-
re.escape() works differently in Python 3.7.0 than earlier versions:
96-
97-
Python 3.6.5:
98-
>>> import re
99-
>>> re.escape('"')
100-
'\\"'
101-
102-
Python 3.7.0:
103-
>>> import re
104-
>>> re.escape('"')
105-
'"'
106-
107-
SeleniumBase needs quotes to be properly escaped for Javascript calls.
108-
"""
109-
if are_quotes_escaped(string):
110-
if string.count("'") != string.count("\\'"):
111-
string = string.replace("'", "\\'")
112-
if string.count('"') != string.count('\\"'):
113-
string = string.replace('"', '\\"')
114-
return string
115-
116-
11786
def make_css_match_first_element_only(selector):
11887
# Only get the first match
11988
last_syllable = selector.split(' ')[-1]
12089
if ':' not in last_syllable and ':contains' not in selector:
12190
selector += ':first'
12291
return selector
123-
124-
125-
def _jq_format(code):
126-
"""
127-
DEPRECATED - Use re.escape() instead, which performs the intended action.
128-
Use before throwing raw code such as 'div[tab="advanced"]' into jQuery.
129-
Selectors with quotes inside of quotes would otherwise break jQuery.
130-
If you just want to escape quotes, there's escape_quotes_if_needed().
131-
This is similar to "json.dumps(value)", but with one less layer of quotes.
132-
"""
133-
code = code.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n')
134-
code = code.replace('\"', '\\\"').replace('\'', '\\\'')
135-
code = code.replace('\v', '\\v').replace('\a', '\\a').replace('\f', '\\f')
136-
code = code.replace('\b', '\\b').replace(r'\u', '\\u').replace('\r', '\\r')
137-
return code

0 commit comments

Comments
 (0)