Merge pull request #219 from seleniumbase/major-refactoring

Major refactoring
This commit is contained in:
Michael Mintz 2018-10-10 03:40:31 -04:00 committed by GitHub
commit ddcf39bcb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1414 additions and 1295 deletions

View File

@ -5,13 +5,13 @@ selenium==3.14.1
pytest>=3.8.2
pytest-cov>=2.6.0
pytest-html>=1.19.0
pytest-rerunfailures>=4.1
pytest-rerunfailures>=4.2
pytest-xdist>=1.23.2
parameterized==0.6.1
beautifulsoup4>=4.6.0
six>=1.11.0
pyotp>=2.2.6
requests>=2.19.1
beautifulsoup4>=4.6.3
unittest2>=1.1.0
chardet>=3.0.4
urllib3>=1.23

796
seleniumbase/core/tour_helper.py Executable file
View File

@ -0,0 +1,796 @@
"""
This module contains methods for running website tours.
These helper methods SHOULD NOT be called directly from tests.
"""
import re
import time
from selenium.webdriver.common.by import By
from seleniumbase.config import settings
from seleniumbase.core import style_sheet
from seleniumbase.fixtures import constants
from seleniumbase.fixtures import js_utils
from seleniumbase.fixtures import page_actions
from seleniumbase.fixtures import page_utils
def raise_unable_to_load_jquery_exception(driver):
""" The most-likely reason for jQuery not loading on web pages. """
raise Exception(
'''Unable to load jQuery on "%s" due to a possible violation '''
'''of the website's Content Security Policy '''
'''directive. ''' % driver.current_url)
def activate_bootstrap(driver):
""" Allows you to use Bootstrap Tours with SeleniumBase
http://bootstraptour.com/
"""
bootstrap_tour_css = constants.BootstrapTour.MIN_CSS
bootstrap_tour_js = constants.BootstrapTour.MIN_JS
verify_script = ("""// Verify Bootstrap Tour activated
var tour2 = new Tour({
});""")
backdrop_style = style_sheet.bt_backdrop_style
js_utils.add_css_style(driver, backdrop_style)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
for x in range(4):
js_utils.activate_jquery(driver)
js_utils.add_css_link(driver, bootstrap_tour_css)
js_utils.add_js_link(driver, bootstrap_tour_js)
time.sleep(0.1)
for x in range(int(settings.MINI_TIMEOUT * 2.0)):
# Bootstrap needs a small amount of time to load & activate.
try:
driver.execute_script(verify_script)
time.sleep(0.05)
return
except Exception:
time.sleep(0.15)
raise_unable_to_load_jquery_exception(driver)
def is_bootstrap_activated(driver):
verify_script = ("""// Verify Bootstrap Tour activated
var tour2 = new Tour({
});""")
try:
driver.execute_script(verify_script)
return True
except Exception:
return False
def activate_hopscotch(driver):
""" Allows you to use Hopscotch Tours with SeleniumBase
http://linkedin.github.io/hopscotch/
"""
hopscotch_css = constants.Hopscotch.MIN_CSS
hopscotch_js = constants.Hopscotch.MIN_JS
backdrop_style = style_sheet.hops_backdrop_style
verify_script = ("""// Verify Hopscotch activated
var hops = hopscotch.isActive;
""")
activate_bootstrap(driver)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
js_utils.add_css_style(driver, backdrop_style)
for x in range(4):
js_utils.activate_jquery(driver)
js_utils.add_css_link(driver, hopscotch_css)
js_utils.add_js_link(driver, hopscotch_js)
time.sleep(0.1)
for x in range(int(settings.MINI_TIMEOUT * 2.0)):
# Hopscotch needs a small amount of time to load & activate.
try:
driver.execute_script(verify_script)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
time.sleep(0.05)
return
except Exception:
time.sleep(0.15)
raise_unable_to_load_jquery_exception(driver)
def is_hopscotch_activated(driver):
verify_script = ("""// Verify Hopscotch activated
var hops = hopscotch.isActive;
""")
try:
driver.execute_script(verify_script)
return True
except Exception:
return False
def activate_introjs(driver):
""" Allows you to use IntroJS Tours with SeleniumBase
https://introjs.com/
"""
intro_css = constants.IntroJS.MIN_CSS
intro_js = constants.IntroJS.MIN_JS
verify_script = ("""// Verify IntroJS activated
var intro2 = introJs();
""")
activate_bootstrap(driver)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
for x in range(4):
js_utils.activate_jquery(driver)
js_utils.add_css_link(driver, intro_css)
js_utils.add_js_link(driver, intro_js)
time.sleep(0.1)
for x in range(int(settings.MINI_TIMEOUT * 2.0)):
# IntroJS needs a small amount of time to load & activate.
try:
driver.execute_script(verify_script)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
time.sleep(0.05)
return
except Exception:
time.sleep(0.15)
raise_unable_to_load_jquery_exception(driver)
def is_introjs_activated(driver):
verify_script = ("""// Verify IntroJS activated
var intro2 = introJs();
""")
try:
driver.execute_script(verify_script)
return True
except Exception:
return False
def activate_shepherd(driver):
""" Allows you to use Shepherd Tours with SeleniumBase
http://github.hubspot.com/shepherd/docs/welcome/
"""
shepherd_js = constants.Shepherd.MIN_JS
sh_theme_arrows_css = constants.Shepherd.THEME_ARROWS_CSS
sh_theme_arrows_fix_css = constants.Shepherd.THEME_ARR_FIX_CSS
sh_theme_default_css = constants.Shepherd.THEME_DEFAULT_CSS
sh_theme_dark_css = constants.Shepherd.THEME_DARK_CSS
sh_theme_sq_css = constants.Shepherd.THEME_SQ_CSS
sh_theme_sq_dark_css = constants.Shepherd.THEME_SQ_DK_CSS
tether_js = constants.Tether.MIN_JS
spinner_css = constants.Messenger.SPINNER_CSS
sh_style = style_sheet.sh_style_test
backdrop_style = style_sheet.sh_backdrop_style
activate_bootstrap(driver)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
js_utils.add_css_style(driver, backdrop_style)
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
for x in range(4):
js_utils.add_css_link(driver, spinner_css)
js_utils.add_css_link(driver, sh_theme_arrows_css)
js_utils.add_css_link(driver, sh_theme_arrows_fix_css)
js_utils.add_css_link(driver, sh_theme_default_css)
js_utils.add_css_link(driver, sh_theme_dark_css)
js_utils.add_css_link(driver, sh_theme_sq_css)
js_utils.add_css_link(driver, sh_theme_sq_dark_css)
js_utils.add_js_link(driver, tether_js)
js_utils.add_js_link(driver, shepherd_js)
time.sleep(0.1)
for x in range(int(settings.MINI_TIMEOUT * 2.0)):
# Shepherd needs a small amount of time to load & activate.
try:
driver.execute_script(sh_style) # Verify Shepherd has loaded
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
driver.execute_script(sh_style) # Need it twice for ordering
js_utils.wait_for_ready_state_complete(driver)
js_utils.wait_for_angularjs(driver)
time.sleep(0.05)
return
except Exception:
time.sleep(0.15)
raise_unable_to_load_jquery_exception(driver)
def is_shepherd_activated(driver):
sh_style = style_sheet.sh_style_test
try:
driver.execute_script(sh_style) # Verify Shepherd has loaded
return True
except Exception:
return False
def play_shepherd_tour(driver, tour_steps, msg_dur, name=None, interval=0):
""" Plays a Shepherd tour on the current website. """
instructions = ""
for tour_step in tour_steps[name]:
instructions += tour_step
instructions += ("""
// Start the tour
tour.start();
$tour = tour;""")
autoplay = False
if interval and interval > 0:
autoplay = True
interval = float(interval)
if interval < 0.5:
interval = 0.5
if not is_shepherd_activated(driver):
activate_shepherd(driver)
if len(tour_steps[name]) > 1:
try:
selector = re.search(
r"[\S\s]+{element: '([\S\s]+)', on: [\S\s]+",
tour_steps[name][1]).group(1)
selector = selector.replace('\\', '')
page_actions.wait_for_element_present(
driver, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT)
except Exception:
js_utils.post_messenger_error_message(
driver, "Tour Error: {'%s'} was not found!" % selector,
msg_dur, duration=settings.SMALL_TIMEOUT)
raise Exception(
"Tour Error: {'%s'} was not found! "
"Exiting due to failure on first tour step!"
"" % selector)
driver.execute_script(instructions)
tour_on = True
if autoplay:
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
latest_element = None
latest_text = None
while tour_on:
try:
time.sleep(0.01)
result = driver.execute_script(
"return Shepherd.activeTour.currentStep.isOpen()")
except Exception:
tour_on = False
result = None
if result:
tour_on = True
if autoplay:
try:
element = driver.execute_script(
"return Shepherd.activeTour.currentStep"
".options.attachTo.element")
shep_text = driver.execute_script(
"return Shepherd.activeTour.currentStep"
".options.text")
except Exception:
continue
if element != latest_element or shep_text != latest_text:
latest_element = element
latest_text = shep_text
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
if ((element == latest_element) and
(shep_text == latest_text)):
driver.execute_script("Shepherd.activeTour.next()")
try:
latest_element = driver.execute_script(
"return Shepherd.activeTour.currentStep"
".options.attachTo.element")
latest_text = driver.execute_script(
"return Shepherd.activeTour.currentStep"
".options.text")
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
except Exception:
pass
continue
else:
try:
time.sleep(0.01)
selector = driver.execute_script(
"return Shepherd.activeTour"
".currentStep.options.attachTo.element")
try:
js_utils.wait_for_css_query_selector(
driver, selector, timeout=settings.SMALL_TIMEOUT)
except Exception:
remove_script = (
"jQuery('%s').remove()" % "div.shepherd-content")
driver.execute_script(remove_script)
js_utils.post_messenger_error_message(
driver, "Tour Error: {'%s'} was not found!" % selector,
msg_dur, duration=settings.SMALL_TIMEOUT)
time.sleep(0.1)
driver.execute_script("Shepherd.activeTour.next()")
if autoplay:
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
tour_on = True
except Exception:
tour_on = False
time.sleep(0.1)
def play_bootstrap_tour(
driver, tour_steps, browser, msg_dur, name=None, interval=0):
""" Plays a Bootstrap tour on the current website. """
instructions = ""
for tour_step in tour_steps[name]:
instructions += tour_step
instructions += (
"""]);
// Initialize the tour
tour.init();
// Start the tour
tour.start();
// Fix timing issue by restarting tour immediately
tour.restart();
// Save for later
$tour = tour;""")
if interval and interval > 0:
if interval < 1:
interval = 1
interval = str(float(interval) * 1000.0)
instructions = instructions.replace(
'duration: 0,', 'duration: %s,' % interval)
if not is_bootstrap_activated(driver):
activate_bootstrap(driver)
if len(tour_steps[name]) > 1:
try:
if "element: " in tour_steps[name][1]:
selector = re.search(
r"[\S\s]+element: '([\S\s]+)',[\S\s]+title: '",
tour_steps[name][1]).group(1)
selector = selector.replace('\\', '').replace(':first', '')
page_actions.wait_for_element_present(
driver, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT)
else:
selector = "html"
except Exception:
js_utils.post_messenger_error_message(
driver, "Tour Error: {'%s'} was not found!" % selector,
msg_dur, duration=settings.SMALL_TIMEOUT)
raise Exception(
"Tour Error: {'%s'} was not found! "
"Exiting due to failure on first tour step!"
"" % selector)
driver.execute_script(instructions)
tour_on = True
while tour_on:
try:
time.sleep(0.01)
if browser != "firefox":
result = driver.execute_script(
"return $tour.ended()")
else:
page_actions.wait_for_element_present(
driver, ".tour-tour", by=By.CSS_SELECTOR, timeout=0.4)
result = False
except Exception:
tour_on = False
result = None
if result is False:
tour_on = True
else:
try:
time.sleep(0.01)
if browser != "firefox":
result = driver.execute_script(
"return $tour.ended()")
else:
page_actions.wait_for_element_present(
driver, ".tour-tour", by=By.CSS_SELECTOR, timeout=0.4)
result = False
if result is False:
time.sleep(0.1)
continue
else:
return
except Exception:
tour_on = False
time.sleep(0.1)
def play_hopscotch_tour(
driver, tour_steps, browser, msg_dur, name=None, interval=0):
""" Plays a Hopscotch tour on the current website. """
instructions = ""
for tour_step in tour_steps[name]:
instructions += tour_step
instructions += (
"""]
};
// Start the tour!
hopscotch.startTour(tour);
$tour = hopscotch;""")
autoplay = False
if interval and interval > 0:
autoplay = True
interval = float(interval)
if interval < 0.5:
interval = 0.5
if not is_hopscotch_activated(driver):
activate_hopscotch(driver)
if len(tour_steps[name]) > 1:
try:
if "target: " in tour_steps[name][1]:
selector = re.search(
r"[\S\s]+target: '([\S\s]+)',[\S\s]+title: '",
tour_steps[name][1]).group(1)
selector = selector.replace('\\', '').replace(':first', '')
page_actions.wait_for_element_present(
driver, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT)
else:
selector = "html"
except Exception:
js_utils.post_messenger_error_message(
driver, "Tour Error: {'%s'} was not found!" % selector,
msg_dur, duration=settings.SMALL_TIMEOUT)
raise Exception(
"Tour Error: {'%s'} was not found! "
"Exiting due to failure on first tour step!"
"" % selector)
driver.execute_script(instructions)
tour_on = True
if autoplay:
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
latest_step = 0
while tour_on:
try:
time.sleep(0.01)
if browser != "firefox":
result = not driver.execute_script(
"return $tour.isActive")
else:
page_actions.wait_for_element_present(
driver, ".hopscotch-bubble",
by=By.CSS_SELECTOR, timeout=0.4)
result = False
except Exception:
tour_on = False
result = None
if result is False:
tour_on = True
if autoplay:
try:
current_step = driver.execute_script(
"return $tour.getCurrStepNum()")
except Exception:
continue
if current_step != latest_step:
latest_step = current_step
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
if current_step == latest_step:
driver.execute_script("return $tour.nextStep()")
try:
latest_step = driver.execute_script(
"return $tour.getCurrStepNum()")
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
except Exception:
pass
continue
else:
try:
time.sleep(0.01)
if browser != "firefox":
result = not driver.execute_script(
"return $tour.isActive")
else:
page_actions.wait_for_element_present(
driver, ".hopscotch-bubble",
by=By.CSS_SELECTOR, timeout=0.4)
result = False
if result is False:
time.sleep(0.1)
continue
else:
return
except Exception:
tour_on = False
time.sleep(0.1)
def play_introjs_tour(
driver, tour_steps, browser, msg_dur, name=None, interval=0):
""" Plays an IntroJS tour on the current website. """
instructions = ""
for tour_step in tour_steps[name]:
instructions += tour_step
instructions += (
"""]
});
intro.setOption("disableInteraction", true);
intro.setOption("overlayOpacity", .29);
intro.setOption("scrollToElement", true);
intro.setOption("keyboardNavigation", true);
intro.setOption("exitOnEsc", false);
intro.setOption("exitOnOverlayClick", false);
intro.setOption("showStepNumbers", false);
intro.setOption("showProgress", false);
intro.start();
$tour = intro;
};
// Start the tour
startIntro();
""")
autoplay = False
if interval and interval > 0:
autoplay = True
interval = float(interval)
if interval < 0.5:
interval = 0.5
if not is_introjs_activated(driver):
activate_introjs(driver)
if len(tour_steps[name]) > 1:
try:
if "element: " in tour_steps[name][1]:
selector = re.search(
r"[\S\s]+element: '([\S\s]+)',[\S\s]+intro: '",
tour_steps[name][1]).group(1)
selector = selector.replace('\\', '')
page_actions.wait_for_element_present(
driver, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT)
else:
selector = "html"
except Exception:
js_utils.post_messenger_error_message(
driver, "Tour Error: {'%s'} was not found!" % selector,
msg_dur, duration=settings.SMALL_TIMEOUT)
raise Exception(
"Tour Error: {'%s'} was not found! "
"Exiting due to failure on first tour step!"
"" % selector)
driver.execute_script(instructions)
tour_on = True
if autoplay:
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
latest_step = 0
while tour_on:
try:
time.sleep(0.01)
if browser != "firefox":
result = driver.execute_script(
"return $tour._currentStep")
else:
page_actions.wait_for_element_present(
driver, ".introjs-tooltip",
by=By.CSS_SELECTOR, timeout=0.4)
result = True
except Exception:
tour_on = False
result = None
if result is not None:
tour_on = True
if autoplay:
try:
current_step = driver.execute_script(
"return $tour._currentStep")
except Exception:
continue
if current_step != latest_step:
latest_step = current_step
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
if current_step == latest_step:
driver.execute_script("return $tour.nextStep()")
try:
latest_step = driver.execute_script(
"return $tour._currentStep")
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
except Exception:
pass
continue
else:
try:
time.sleep(0.01)
if browser != "firefox":
result = driver.execute_script(
"return $tour._currentStep")
else:
page_actions.wait_for_element_present(
driver, ".introjs-tooltip",
by=By.CSS_SELECTOR, timeout=0.4)
result = True
if result is not None:
time.sleep(0.1)
continue
else:
return
except Exception:
tour_on = False
time.sleep(0.1)
def export_tour(tour_steps, name=None, filename="my_tour.js"):
""" Exports a tour as a JS file.
It will include necessary resources as well, such as jQuery.
You'll be able to copy the tour directly into the Console of
any web browser to play the tour outside of SeleniumBase runs. """
if not name:
name = "default"
if name not in tour_steps:
raise Exception("Tour {%s} does not exist!" % name)
if not filename.endswith('.js'):
raise Exception('Tour file must end in ".js"!')
tour_type = None
if "Bootstrap" in tour_steps[name][0]:
tour_type = "bootstrap"
elif "Hopscotch" in tour_steps[name][0]:
tour_type = "hopscotch"
elif "IntroJS" in tour_steps[name][0]:
tour_type = "introjs"
elif "Shepherd" in tour_steps[name][0]:
tour_type = "shepherd"
else:
raise Exception('Unknown tour type!')
instructions = (
'''//////// Resources ////////\n\n'''
'''function injectCSS(css_link) {'''
'''var head = document.getElementsByTagName("head")[0];'''
'''var link = document.createElement("link");'''
'''link.rel = "stylesheet";'''
'''link.type = "text/css";'''
'''link.href = css_link;'''
'''link.crossorigin = "anonymous";'''
'''head.appendChild(link);'''
'''};\n'''
'''function injectJS(js_link) {'''
'''var head = document.getElementsByTagName("head")[0];'''
'''var script = document.createElement("script");'''
'''script.src = js_link;'''
'''script.defer;'''
'''script.type="text/javascript";'''
'''script.crossorigin = "anonymous";'''
'''script.onload = function() { null };'''
'''head.appendChild(script);'''
'''};\n'''
'''function injectStyle(css) {'''
'''var head = document.getElementsByTagName("head")[0];'''
'''var style = document.createElement("style");'''
'''style.type = "text/css";'''
'''style.appendChild(document.createTextNode(css));'''
'''head.appendChild(style);'''
'''};\n''')
if tour_type == "bootstrap":
jquery_js = constants.JQuery.MIN_JS
bootstrap_tour_css = constants.BootstrapTour.MIN_CSS
bootstrap_tour_js = constants.BootstrapTour.MIN_JS
backdrop_style = style_sheet.bt_backdrop_style
backdrop_style = backdrop_style.replace('\n', '')
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
instructions += 'injectJS("%s");' % jquery_js
instructions += '\n\n//////// Resources - Load 2 ////////\n\n'
instructions += 'injectCSS("%s");\n' % bootstrap_tour_css
instructions += 'injectStyle("%s");\n' % backdrop_style
instructions += 'injectJS("%s");' % bootstrap_tour_js
elif tour_type == "hopscotch":
hopscotch_css = constants.Hopscotch.MIN_CSS
hopscotch_js = constants.Hopscotch.MIN_JS
backdrop_style = style_sheet.hops_backdrop_style
backdrop_style = backdrop_style.replace('\n', '')
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
instructions += 'injectCSS("%s");\n' % hopscotch_css
instructions += 'injectStyle("%s");\n' % backdrop_style
instructions += 'injectJS("%s");' % hopscotch_js
elif tour_type == "introjs":
intro_css = constants.IntroJS.MIN_CSS
intro_js = constants.IntroJS.MIN_JS
instructions += 'injectCSS("%s");\n' % intro_css
instructions += 'injectJS("%s");' % intro_js
elif tour_type == "shepherd":
jquery_js = constants.JQuery.MIN_JS
shepherd_js = constants.Shepherd.MIN_JS
sh_theme_arrows_css = constants.Shepherd.THEME_ARROWS_CSS
sh_theme_arrows_fix_css = constants.Shepherd.THEME_ARR_FIX_CSS
sh_theme_default_css = constants.Shepherd.THEME_DEFAULT_CSS
sh_theme_dark_css = constants.Shepherd.THEME_DARK_CSS
sh_theme_sq_css = constants.Shepherd.THEME_SQ_CSS
sh_theme_sq_dark_css = constants.Shepherd.THEME_SQ_DK_CSS
tether_js = constants.Tether.MIN_JS
spinner_css = constants.Messenger.SPINNER_CSS
backdrop_style = style_sheet.sh_backdrop_style
backdrop_style = backdrop_style.replace('\n', '')
backdrop_style = page_utils.escape_quotes_if_needed(backdrop_style)
instructions += 'injectCSS("%s");\n' % spinner_css
instructions += 'injectJS("%s");\n' % jquery_js
instructions += 'injectJS("%s");' % tether_js
instructions += '\n\n//////// Resources - Load 2 ////////\n\n'
instructions += 'injectCSS("%s");' % sh_theme_arrows_css
instructions += 'injectCSS("%s");' % sh_theme_arrows_fix_css
instructions += 'injectCSS("%s");' % sh_theme_default_css
instructions += 'injectCSS("%s");' % sh_theme_dark_css
instructions += 'injectCSS("%s");' % sh_theme_sq_css
instructions += 'injectCSS("%s");\n' % sh_theme_sq_dark_css
instructions += 'injectStyle("%s");\n' % backdrop_style
instructions += 'injectJS("%s");' % shepherd_js
instructions += '\n\n//////// Tour Code ////////\n\n'
for tour_step in tour_steps[name]:
instructions += tour_step
if tour_type == "bootstrap":
instructions += (
"""]);
// Initialize the tour
tour.init();
// Start the tour
tour.start();
$tour = tour;
$tour.restart();\n
""")
elif tour_type == "hopscotch":
instructions += (
"""]
};
// Start the tour!
hopscotch.startTour(tour);
$tour = hopscotch;\n
""")
elif tour_type == "introjs":
instructions += (
"""]
});
intro.setOption("disableInteraction", true);
intro.setOption("overlayOpacity", .29);
intro.setOption("scrollToElement", true);
intro.setOption("keyboardNavigation", true);
intro.setOption("exitOnEsc", false);
intro.setOption("exitOnOverlayClick", false);
intro.setOption("showStepNumbers", false);
intro.setOption("showProgress", false);
intro.start();
$tour = intro;
};
startIntro();\n
""")
elif tour_type == "shepherd":
instructions += (
"""
tour.start();
$tour = tour;\n
""")
else:
pass
import codecs
out_file = codecs.open(filename, "w+")
out_file.writelines(instructions)
out_file.close()
print('\n>>> [%s] was saved!\n' % filename)

File diff suppressed because it is too large Load Diff

550
seleniumbase/fixtures/js_utils.py Executable file
View File

@ -0,0 +1,550 @@
"""
This module contains useful Javascript utility methods for base_case.py
These helper methods SHOULD NOT be called directly from tests.
"""
import re
import requests
import time
from selenium.common.exceptions import WebDriverException
from seleniumbase.config import settings
from seleniumbase.fixtures import constants
from seleniumbase.fixtures import page_utils
def activate_jquery(driver):
""" If "jQuery is not defined", use this method to activate it for use.
This happens because jQuery is not always defined on web sites. """
try:
# Let's first find out if jQuery is already defined.
driver.execute_script("jQuery('html')")
# Since that command worked, jQuery is defined. Let's return.
return
except Exception:
# jQuery is not currently defined. Let's proceed by defining it.
pass
jquery_js = constants.JQuery.MIN_JS
activate_jquery_script = (
'''var script = document.createElement('script');'''
'''script.src = "%s";document.getElementsByTagName('head')[0]'''
'''.appendChild(script);''' % jquery_js)
driver.execute_script(activate_jquery_script)
for x in range(int(settings.MINI_TIMEOUT * 10.0)):
# jQuery needs a small amount of time to activate.
try:
driver.execute_script("jQuery('html')")
return
except Exception:
time.sleep(0.1)
# Since jQuery still isn't activating, give up and raise an exception
raise Exception(
'''Unable to load jQuery on "%s" due to a possible violation '''
'''of the website's Content Security Policy '''
'''directive. ''' % driver.current_url)
def safe_execute_script(driver, script):
""" When executing a script that contains a jQuery command,
it's important that the jQuery library has been loaded first.
This method will load jQuery if it wasn't already loaded. """
try:
driver.execute_script(script)
except Exception:
# The likely reason this fails is because: "jQuery is not defined"
activate_jquery(driver) # It's a good thing we can define it here
driver.execute_script(script)
def wait_for_ready_state_complete(driver, timeout=settings.EXTREME_TIMEOUT):
"""
The DOM (Document Object Model) has a property called "readyState".
When the value of this becomes "complete", page resources are considered
fully loaded (although AJAX and other loads might still be happening).
This method will wait until document.readyState == "complete".
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
try:
ready_state = driver.execute_script("return document.readyState")
except WebDriverException:
# Bug fix for: [Permission denied to access property "document"]
time.sleep(0.03)
return True
if ready_state == u'complete':
time.sleep(0.01) # Better be sure everything is done loading
return True
else:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception(
"Page elements never fully loaded after %s seconds!" % timeout)
def execute_async_script(driver, script, timeout=settings.EXTREME_TIMEOUT):
driver.set_script_timeout(timeout)
return driver.execute_async_script(script)
def wait_for_angularjs(driver, timeout=settings.LARGE_TIMEOUT, **kwargs):
if not settings.WAIT_FOR_ANGULARJS:
return
NG_WRAPPER = '%(prefix)s' \
'var $elm=document.querySelector(' \
'\'[data-ng-app],[ng-app],.ng-scope\')||document;' \
'if(window.angular && angular.getTestability){' \
'angular.getTestability($elm).whenStable(%(handler)s)' \
'}else{' \
'var $inj;try{$inj=angular.element($elm).injector()||' \
'angular.injector([\'ng\'])}catch(ex){' \
'$inj=angular.injector([\'ng\'])};$inj.get=$inj.get||' \
'$inj;$inj.get(\'$browser\').' \
'notifyWhenNoOutstandingRequests(%(handler)s)}' \
'%(suffix)s'
def_pre = 'var cb=arguments[arguments.length-1];if(window.angular){'
prefix = kwargs.pop('prefix', def_pre)
handler = kwargs.pop('handler', 'function(){cb(true)}')
suffix = kwargs.pop('suffix', '}else{cb(false)}')
script = NG_WRAPPER % {'prefix': prefix,
'handler': handler,
'suffix': suffix}
try:
execute_async_script(driver, script, timeout=timeout)
except Exception:
time.sleep(0.05)
def wait_for_css_query_selector(
driver, selector, timeout=settings.SMALL_TIMEOUT):
element = None
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
try:
selector = re.escape(selector)
selector = page_utils.escape_quotes_if_needed(selector)
element = driver.execute_script(
"""return document.querySelector('%s')""" % selector)
if element:
return element
except Exception:
element = None
if not element:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception(
"Element {%s} was not present after %s seconds!" % (
selector, timeout))
def highlight_with_js(driver, selector, loops, o_bs):
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 128, 128, 0.5)';"""
% selector)
driver.execute_script(script)
for n in range(loops):
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(255, 0, 0, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 0, 128, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(0, 0, 255, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(0, 255, 0, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 128, 0, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 0, 128, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: %s';"""
% (selector, o_bs))
driver.execute_script(script)
def highlight_with_jquery(driver, selector, loops, o_bs):
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 128, 128, 0.5)');""" % selector
safe_execute_script(driver, script)
for n in range(loops):
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(255, 0, 0, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(0, 0, 255, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(0, 255, 0, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 128, 0, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs)
driver.execute_script(script)
def add_css_link(driver, css_link):
script_to_add_css = (
"""function injectCSS(css) {
var head = document.getElementsByTagName("head")[0];
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = css;
link.crossorigin = "anonymous";
head.appendChild(link);
}
injectCSS("%s");""")
css_link = page_utils.escape_quotes_if_needed(css_link)
driver.execute_script(script_to_add_css % css_link)
def add_js_link(driver, js_link):
script_to_add_js = (
"""function injectJS(link) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = link;
script.defer;
script.type="text/javascript";
script.crossorigin = "anonymous";
script.onload = function() { null };
head.appendChild(script);
}
injectJS("%s");""")
js_link = page_utils.escape_quotes_if_needed(js_link)
driver.execute_script(script_to_add_js % js_link)
def add_css_style(driver, css_style):
add_css_style_script = (
"""function injectStyle(css) {
var head = document.getElementsByTagName("head")[0];
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(css));
head.appendChild(style);
}
injectStyle("%s");""")
css_style = css_style.replace('\n', '')
css_style = page_utils.escape_quotes_if_needed(css_style)
driver.execute_script(add_css_style_script % css_style)
def add_js_code_from_link(driver, js_link):
if js_link.startswith("//"):
js_link = "http:" + js_link
js_code = requests.get(js_link).text
add_js_code_script = (
'''var h = document.getElementsByTagName('head').item(0);'''
'''var s = document.createElement("script");'''
'''s.type = "text/javascript";'''
'''s.onload = function() { null };'''
'''s.appendChild(document.createTextNode("%s"));'''
'''h.appendChild(s);''')
js_code = js_code.replace('\n', '')
js_code = page_utils.escape_quotes_if_needed(js_code)
driver.execute_script(add_js_code_script % js_code)
def add_meta_tag(driver, http_equiv=None, content=None):
if http_equiv is None:
http_equiv = "Content-Security-Policy"
if content is None:
content = ("default-src *; style-src 'self' 'unsafe-inline'; "
"script-src: 'self' 'unsafe-inline' 'unsafe-eval'")
script_to_add_meta = (
"""function injectMeta() {
var meta = document.createElement('meta');
meta.httpEquiv = "%s";
meta.content = "%s";
document.getElementsByTagName('head')[0].appendChild(meta);
}
injectMeta();""" % (http_equiv, content))
driver.execute_script(script_to_add_meta)
def activate_messenger(driver):
jquery_js = constants.JQuery.MIN_JS
messenger_css = constants.Messenger.MIN_CSS
messenger_js = constants.Messenger.MIN_JS
msgr_theme_flat_js = constants.Messenger.THEME_FLAT_JS
msgr_theme_future_js = constants.Messenger.THEME_FUTURE_JS
msgr_theme_flat_css = constants.Messenger.THEME_FLAT_CSS
msgr_theme_future_css = constants.Messenger.THEME_FUTURE_CSS
msgr_theme_block_css = constants.Messenger.THEME_BLOCK_CSS
msgr_theme_air_css = constants.Messenger.THEME_AIR_CSS
msgr_theme_ice_css = constants.Messenger.THEME_ICE_CSS
spinner_css = constants.Messenger.SPINNER_CSS
underscore_js = constants.Underscore.MIN_JS
backbone_js = constants.Backbone.MIN_JS
msg_style = ("Messenger.options = {'maxMessages': 8, "
"extraClasses: 'messenger-fixed "
"messenger-on-bottom messenger-on-right', "
"theme: 'future'}")
add_js_link(driver, jquery_js)
add_css_link(driver, messenger_css)
add_css_link(driver, msgr_theme_flat_css)
add_css_link(driver, msgr_theme_future_css)
add_css_link(driver, msgr_theme_block_css)
add_css_link(driver, msgr_theme_air_css)
add_css_link(driver, msgr_theme_ice_css)
add_js_link(driver, underscore_js)
add_js_link(driver, backbone_js)
add_css_link(driver, spinner_css)
add_js_link(driver, messenger_js)
add_js_link(driver, msgr_theme_flat_js)
add_js_link(driver, msgr_theme_future_js)
for x in range(int(settings.MINI_TIMEOUT * 10.0)):
# Messenger needs a small amount of time to load & activate.
try:
driver.execute_script(msg_style)
wait_for_ready_state_complete(driver)
wait_for_angularjs(driver)
return
except Exception:
time.sleep(0.1)
def set_messenger_theme(driver, theme="default", location="default",
max_messages="default"):
if theme == "default":
theme = "future"
if location == "default":
location = "bottom_right"
if max_messages == "default":
max_messages = "8"
valid_themes = ['flat', 'future', 'block', 'air', 'ice']
if theme not in valid_themes:
raise Exception("Theme: %s is not in %s!" % (theme, valid_themes))
valid_locations = (['top_left', 'top_center', 'top_right'
'bottom_left', 'bottom_center', 'bottom_right'])
if location not in valid_locations:
raise Exception(
"Location: %s is not in %s!" % (location, valid_locations))
if location == 'top_left':
messenger_location = "messenger-on-top messenger-on-left"
elif location == 'top_center':
messenger_location = "messenger-on-top"
elif location == 'top_right':
messenger_location = "messenger-on-top messenger-on-right"
elif location == 'bottom_left':
messenger_location = "messenger-on-bottom messenger-on-left"
elif location == 'bottom_center':
messenger_location = "messenger-on-bottom"
elif location == 'bottom_right':
messenger_location = "messenger-on-bottom messenger-on-right"
msg_style = ("Messenger.options = {'maxMessages': %s, "
"extraClasses: 'messenger-fixed %s', theme: '%s'}"
% (max_messages, messenger_location, theme))
try:
driver.execute_script(msg_style)
except Exception:
activate_messenger(driver)
driver.execute_script(msg_style)
time.sleep(0.1)
def post_message(driver, message, msg_dur, style="info", duration=None):
""" A helper method to post a message on the screen with Messenger.
(Should only be called from post_message() in base_case.py) """
if not duration:
if not msg_dur:
duration = settings.DEFAULT_MESSAGE_DURATION
else:
duration = msg_dur
message = re.escape(message)
message = page_utils.escape_quotes_if_needed(message)
messenger_script = ('''Messenger().post({message: "%s", type: "%s", '''
'''hideAfter: %s, hideOnNavigate: true});'''
% (message, style, duration))
try:
driver.execute_script(messenger_script)
except Exception:
activate_messenger(driver)
set_messenger_theme(driver)
try:
driver.execute_script(messenger_script)
except Exception:
time.sleep(0.2)
activate_messenger(driver)
time.sleep(0.2)
set_messenger_theme(driver)
time.sleep(0.5)
driver.execute_script(messenger_script)
def post_messenger_success_message(driver, message, msg_dur, duration=None):
if not duration:
if not msg_dur:
duration = settings.DEFAULT_MESSAGE_DURATION
else:
duration = msg_dur
try:
set_messenger_theme(driver, theme="future", location="bottom_right")
post_message(
driver, message, msg_dur, style="success", duration=duration)
time.sleep(duration)
except Exception:
pass
def post_messenger_error_message(driver, message, msg_dur, duration=None):
if not duration:
if not msg_dur:
duration = settings.DEFAULT_MESSAGE_DURATION
else:
duration = msg_dur
try:
set_messenger_theme(driver, theme="block", location="top_center")
post_message(
driver, message, msg_dur, style="error", duration=duration)
time.sleep(duration)
except Exception:
pass
def highlight_with_js_2(driver, message, selector, o_bs, msg_dur):
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 128, 128, 0.5)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(205, 30, 0, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(128, 0, 128, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(50, 50, 128, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
script = ("""document.querySelector('%s').style =
'box-shadow: 0px 0px 6px 6px rgba(50, 205, 50, 1)';"""
% selector)
driver.execute_script(script)
time.sleep(0.0181)
post_messenger_success_message(driver, message, msg_dur)
script = ("""document.querySelector('%s').style =
'box-shadow: %s';""" % (selector, o_bs))
driver.execute_script(script)
def highlight_with_jquery_2(driver, message, selector, o_bs, msg_dur):
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 128, 128, 0.5)');""" % selector
safe_execute_script(driver, script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(205, 30, 0, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(50, 50, 200, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
script = """jQuery('%s').css('box-shadow',
'0px 0px 6px 6px rgba(50, 205, 50, 1)');""" % selector
driver.execute_script(script)
time.sleep(0.0181)
post_messenger_success_message(driver, message, msg_dur)
script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs)
driver.execute_script(script)
def scroll_to_element(driver, element):
element_location = element.location['y']
element_location = element_location - 130
if element_location < 0:
element_location = 0
scroll_script = "window.scrollTo(0, %s);" % element_location
# The old jQuery scroll_script required by=By.CSS_SELECTOR
# scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector
try:
driver.execute_script(scroll_script)
except WebDriverException:
pass # Older versions of Firefox experienced issues here
def slow_scroll_to_element(driver, element, browser):
if browser == 'ie':
# IE breaks on slow-scrolling. Do a fast scroll instead.
scroll_to_element(driver, element)
return
scroll_position = driver.execute_script("return window.scrollY;")
element_location = element.location['y']
element_location = element_location - 130
if element_location < 0:
element_location = 0
distance = element_location - scroll_position
if distance != 0:
total_steps = int(abs(distance) / 50.0) + 2.0
step_value = float(distance) / total_steps
new_position = scroll_position
for y in range(int(total_steps)):
time.sleep(0.0114)
new_position += step_value
scroll_script = "window.scrollTo(0, %s);" % new_position
driver.execute_script(scroll_script)
time.sleep(0.01)
scroll_script = "window.scrollTo(0, %s);" % element_location
driver.execute_script(scroll_script)
time.sleep(0.01)
if distance > 430 or distance < -300:
# Add small recovery time for long-distance slow-scrolling
time.sleep(0.162)

View File

@ -24,7 +24,6 @@ import os
import sys
import time
import traceback
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.errorhandler import ElementNotVisibleException
@ -426,35 +425,6 @@ def save_test_failure_data(driver, name, browser_type, folder=None):
failure_data_file.close()
def wait_for_ready_state_complete(driver, timeout=settings.EXTREME_TIMEOUT):
"""
The DOM (Document Object Model) has a property called "readyState".
When the value of this becomes "complete", page resources are considered
fully loaded (although AJAX and other loads might still be happening).
This method will wait until document.readyState == "complete".
"""
start_ms = time.time() * 1000.0
stop_ms = start_ms + (timeout * 1000.0)
for x in range(int(timeout * 10)):
try:
ready_state = driver.execute_script("return document.readyState")
except WebDriverException:
# Bug fix for: [Permission denied to access property "document"]
time.sleep(0.03)
return True
if ready_state == u'complete':
time.sleep(0.01) # Better be sure everything is done loading
return True
else:
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
time.sleep(0.1)
raise Exception(
"Page elements never fully loaded after %s seconds!" % timeout)
def wait_for_and_accept_alert(driver, timeout=settings.LARGE_TIMEOUT):
"""
Wait for and accept an alert. Returns the text from the alert.

View File

@ -114,11 +114,20 @@ def escape_quotes_if_needed(string):
return string
def make_css_match_first_element_only(selector):
# Only get the first match
last_syllable = selector.split(' ')[-1]
if ':' not in last_syllable and ':contains' not in selector:
selector += ':first'
return selector
def _jq_format(code):
"""
DEPRECATED - Use re.escape() instead, which performs the intended action.
Use before throwing raw code such as 'div[tab="advanced"]' into jQuery.
Selectors with quotes inside of quotes would otherwise break jQuery.
If you just want to escape quotes, there's escape_quotes_if_needed().
This is similar to "json.dumps(value)", but with one less layer of quotes.
"""
code = code.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n')

View File

@ -1,9 +0,0 @@
"""
This module imports all the commonly used fixtures in one place so that
every test doesn't need to import a number of different fixtures.
"""
from seleniumbase.fixtures.page_actions import * # noqa
from seleniumbase.fixtures.page_utils import * # noqa
from seleniumbase.fixtures.errors import * # noqa
from seleniumbase.fixtures.xpath_to_css import * # noqa

View File

@ -17,7 +17,7 @@ except IOError:
setup(
name='seleniumbase',
version='1.16.4',
version='1.16.5',
description='All-In-One Test Automation Framework',
long_description=long_description,
long_description_content_type='text/markdown',
@ -57,13 +57,13 @@ setup(
'pytest>=3.8.2',
'pytest-cov>=2.6.0',
'pytest-html>=1.19.0',
'pytest-rerunfailures>=4.1',
'pytest-rerunfailures>=4.2',
'pytest-xdist>=1.23.2',
'parameterized==0.6.1',
'beautifulsoup4>=4.6.0',
'six>=1.11.0',
'pyotp>=2.2.6',
'requests>=2.19.1',
'beautifulsoup4>=4.6.3',
'unittest2>=1.1.0',
'chardet>=3.0.4',
'urllib3>=1.23',