Skip to content

Commit 00d1b64

Browse files
committed
Add ability to create SeleniumBase Bootstrap Tours
1 parent 4ea23a1 commit 00d1b64

File tree

2 files changed

+205
-23
lines changed

2 files changed

+205
-23
lines changed

help_docs/method_summary.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,14 @@ self.add_css_style(css_style)
9191

9292
self.add_js_code_from_link(js_link)
9393

94-
self.add_meta_tag(http_equiv=None, content=None):
94+
self.add_meta_tag(http_equiv=None, content=None)
9595

9696
self.activate_jquery()
9797

9898
self.create_tour(name=None, theme=None)
9999

100+
self.create_bootstrap_tour(name=None)
101+
100102
self.add_tour_step(message, selector=None, name=None,
101103
title=None, theme=None, alignment=None)
102104

seleniumbase/fixtures/base_case.py

Lines changed: 202 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -813,13 +813,12 @@ def activate_jquery(self):
813813
def __activate_bootstrap(self):
814814
""" Allows you to use Bootstrap Tours with SeleniumBase
815815
http://bootstraptour.com/
816-
(Currently, SeleniumBase methods only use Shepherd Tours.)
817816
"""
818817
bootstrap_tour_css = constants.BootstrapTour.MIN_CSS
819818
bootstrap_tour_js = constants.BootstrapTour.MIN_JS
820819

821820
verify_script = ("""// Instance the tour
822-
var tour = new Tour({
821+
var tour2 = new Tour({
823822
});""")
824823

825824
for x in range(4):
@@ -844,7 +843,7 @@ def __activate_bootstrap(self):
844843

845844
def __is_bootstrap_activated(self):
846845
verify_script = ("""// Instance the tour
847-
var tour = new Tour({
846+
var tour2 = new Tour({
848847
});""")
849848
try:
850849
self.execute_script(verify_script)
@@ -873,6 +872,8 @@ def __activate_shepherd(self):
873872

874873
self.__activate_bootstrap()
875874
self.wait_for_ready_state_complete()
875+
self.add_css_style(backdrop_style)
876+
self.wait_for_ready_state_complete()
876877
for x in range(4):
877878
# self.activate_jquery() # Included with __activate_bootstrap()
878879
self.add_css_link(spinner_css)
@@ -886,8 +887,6 @@ def __activate_shepherd(self):
886887
self.add_js_link(underscore_js)
887888
self.add_js_link(backbone_js)
888889
self.add_js_link(shepherd_js)
889-
time.sleep(0.01)
890-
self.add_css_style(backdrop_style)
891890
time.sleep(0.1)
892891

893892
for x in range(int(settings.MINI_TIMEOUT * 2.0)):
@@ -896,6 +895,7 @@ def __activate_shepherd(self):
896895
self.execute_script(sh_style) # Verify Shepherd has loaded
897896
self.wait_for_ready_state_complete()
898897
self.execute_script(sh_style) # Need it twice for ordering
898+
self.wait_for_ready_state_complete()
899899
time.sleep(0.05)
900900
return
901901
except Exception:
@@ -928,7 +928,10 @@ def create_tour(self, name=None, theme=None):
928928

929929
shepherd_theme = "shepherd-theme-arrows"
930930
if theme:
931-
if theme == "default":
931+
if theme.lower() == "bootstrap":
932+
self.create_bootstrap_tour(name)
933+
return
934+
elif theme == "default":
932935
shepherd_theme = "shepherd-theme-default"
933936
elif theme == "dark":
934937
shepherd_theme = "shepherd-theme-dark"
@@ -939,29 +942,53 @@ def create_tour(self, name=None, theme=None):
939942
elif theme == "square-dark":
940943
shepherd_theme = "shepherd-theme-square-dark"
941944

942-
new_tour = ("""let tour = new Shepherd.Tour({
945+
new_tour = ("""
946+
// Shepherd Tour
947+
let tour = new Shepherd.Tour({
943948
defaults: {
944949
classes: '%s',
945950
scrollTo: true
946951
}
947-
});""" % shepherd_theme)
952+
});
953+
""" % shepherd_theme)
954+
955+
self._tour_steps[name] = []
956+
self._tour_steps[name].append(new_tour)
957+
958+
def create_bootstrap_tour(self, name=None):
959+
""" Creates a Bootstrap tour for a website.
960+
@Params
961+
name - If creating multiple tours, use this to select the
962+
tour you wish to add steps to.
963+
"""
964+
if not name:
965+
name = "default"
966+
967+
new_tour = ("""
968+
// Bootstrap Tour
969+
var tour = new Tour({
970+
steps: [
971+
""")
948972

949973
self._tour_steps[name] = []
950974
self._tour_steps[name].append(new_tour)
951975

952976
def add_tour_step(self, message, selector=None, name=None,
953-
title=None, theme=None, alignment=None):
977+
title=None, theme=None, alignment=None, duration=None):
954978
""" Allows the user to add tour steps for a website.
955979
@Params
956980
message - The message to display.
957981
selector - The CSS Selector of the Element to attach to.
958982
name - If creating multiple tours, use this to select the
959983
tour you wish to add steps to.
960984
title - Additional header text that appears above the message.
961-
theme - Choose from "arrows", "dark", "default", "square", and
985+
theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.
986+
Choose from "arrows", "dark", "default", "square", and
962987
"square-dark". ("arrows" is used if None is selected.)
963988
alignment - Choose from "top", "bottom", "left", and "right".
964989
("top" is the default alignment).
990+
duration - (Bootstrap Tours ONLY) The amount of time, in seconds,
991+
before automatically advancing to the next tour step.
965992
"""
966993
if not selector:
967994
selector = "html"
@@ -970,7 +997,8 @@ def add_tour_step(self, message, selector=None, name=None,
970997
if not name:
971998
name = "default"
972999
if name not in self._tour_steps:
973-
self.create_tour(name=name)
1000+
# By default, will create a Bootstrap tour if no tours exist
1001+
self.create_tour(name=name, theme="bootstrap")
9741002

9751003
if not title:
9761004
title = ""
@@ -981,6 +1009,34 @@ def add_tour_step(self, message, selector=None, name=None,
9811009
else:
9821010
message = ""
9831011

1012+
if not alignment or (
1013+
alignment not in ["top", "bottom", "left", "right"]):
1014+
alignment = "top"
1015+
1016+
if "Bootstrap" in self._tour_steps[name][0]:
1017+
self.__add_bootstrap_tour_step(
1018+
message, selector=selector, name=name, title=title,
1019+
alignment=alignment, duration=duration)
1020+
else:
1021+
self.__add_shepherd_tour_step(
1022+
message, selector=selector, name=name, title=title,
1023+
theme=theme, alignment=alignment)
1024+
1025+
def __add_shepherd_tour_step(self, message, selector=None, name=None,
1026+
title=None, theme=None, alignment=None):
1027+
""" Allows the user to add tour steps for a website.
1028+
@Params
1029+
message - The message to display.
1030+
selector - The CSS Selector of the Element to attach to.
1031+
name - If creating multiple tours, use this to select the
1032+
tour you wish to add steps to.
1033+
title - Additional header text that appears above the message.
1034+
theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.
1035+
Choose from "arrows", "dark", "default", "square", and
1036+
"square-dark". ("arrows" is used if None is selected.)
1037+
alignment - Choose from "top", "bottom", "left", and "right".
1038+
("top" is the default alignment).
1039+
"""
9841040
if theme == "default":
9851041
shepherd_theme = "shepherd-theme-default"
9861042
elif theme == "dark":
@@ -997,10 +1053,6 @@ def add_tour_step(self, message, selector=None, name=None,
9971053
self._tour_steps[name][0]).group(1)
9981054
shepherd_theme = shepherd_base_theme
9991055

1000-
if not alignment or (
1001-
alignment not in ["top", "bottom", "left", "right"]):
1002-
alignment = "top"
1003-
10041056
shepherd_classes = shepherd_theme
10051057
if selector == "html":
10061058
shepherd_classes += " shepherd-orphan"
@@ -1016,6 +1068,41 @@ def add_tour_step(self, message, selector=None, name=None,
10161068

10171069
self._tour_steps[name].append(step)
10181070

1071+
def __add_bootstrap_tour_step(self, message, selector=None, name=None,
1072+
title=None, alignment=None, duration=None):
1073+
""" Allows the user to add tour steps for a website.
1074+
@Params
1075+
message - The message to display.
1076+
selector - The CSS Selector of the Element to attach to.
1077+
name - If creating multiple tours, use this to select the
1078+
tour you wish to add steps to.
1079+
title - Additional header text that appears above the message.
1080+
alignment - Choose from "top", "bottom", "left", and "right".
1081+
("top" is the default alignment).
1082+
duration - (Bootstrap Tours ONLY) The amount of time, in seconds,
1083+
before automatically advancing to the next tour step.
1084+
"""
1085+
if selector != "html":
1086+
element_row = "element: '%s'," % selector
1087+
else:
1088+
element_row = ""
1089+
if not duration:
1090+
duration = "0"
1091+
else:
1092+
duration = str(float(duration) * 1000.0)
1093+
1094+
step = ("""{
1095+
%s
1096+
title: '%s',
1097+
content: '%s',
1098+
orphan: true,
1099+
placement: 'auto %s',
1100+
smartPlacement: true,
1101+
duration: %s,
1102+
},""" % (element_row, title, message, alignment, duration))
1103+
1104+
self._tour_steps[name].append(step)
1105+
10191106
def play_tour(self, name=None, interval=0):
10201107
""" Plays a tour on the current website.
10211108
@Params
@@ -1027,23 +1114,36 @@ def play_tour(self, name=None, interval=0):
10271114
if self.headless:
10281115
return # Tours should not run in headless mode.
10291116

1030-
autoplay = False
1031-
if interval and interval > 0:
1032-
autoplay = True
1033-
interval = float(interval)
1034-
if interval < 0.5:
1035-
interval = 0.5
1036-
10371117
if not name:
10381118
name = "default"
10391119
if name not in self._tour_steps:
10401120
raise Exception("Tour {%s} does not exist!" % name)
10411121

1122+
if "Bootstrap" in self._tour_steps[name][0]:
1123+
self.__play_bootstrap_tour(name=name, interval=interval)
1124+
else:
1125+
self.__play_shepherd_tour(name=name, interval=interval)
1126+
1127+
def __play_shepherd_tour(self, name=None, interval=0):
1128+
""" Plays a tour on the current website.
1129+
@Params
1130+
name - If creating multiple tours, use this to select the
1131+
tour you wish to play.
1132+
interval - The delay time between autoplaying tour steps.
1133+
If set to 0 (default), the tour is fully manual control.
1134+
"""
10421135
instructions = ""
10431136
for tour_step in self._tour_steps[name]:
10441137
instructions += tour_step
10451138
instructions += "tour.start();"
10461139

1140+
autoplay = False
1141+
if interval and interval > 0:
1142+
autoplay = True
1143+
interval = float(interval)
1144+
if interval < 0.5:
1145+
interval = 0.5
1146+
10471147
if not self.__is_shepherd_activated():
10481148
self.__activate_shepherd()
10491149

@@ -1134,6 +1234,86 @@ def play_tour(self, name=None, interval=0):
11341234
tour_on = False
11351235
time.sleep(0.1)
11361236

1237+
def __play_bootstrap_tour(self, name=None, interval=0):
1238+
""" Plays a tour on the current website.
1239+
@Params
1240+
name - If creating multiple tours, use this to select the
1241+
tour you wish to play.
1242+
interval - The delay time between autoplaying tour steps.
1243+
If set to 0 (default), the tour is fully manual control.
1244+
"""
1245+
instructions = ""
1246+
for tour_step in self._tour_steps[name]:
1247+
instructions += tour_step
1248+
instructions += (
1249+
"""]});
1250+
1251+
// Initialize the tour
1252+
tour.init();
1253+
1254+
// Start the tour
1255+
tour.start();
1256+
1257+
$tour = tour;
1258+
$tour.restart();""")
1259+
1260+
if interval and interval > 0:
1261+
if interval < 1:
1262+
interval = 1
1263+
interval = str(float(interval) * 1000.0)
1264+
instructions = instructions.replace(
1265+
'duration: 0,', 'duration: %s,' % interval)
1266+
1267+
if not self.__is_bootstrap_activated():
1268+
self.__activate_bootstrap()
1269+
1270+
if len(self._tour_steps[name]) > 1:
1271+
try:
1272+
if "element: " in self._tour_steps[name][1]:
1273+
selector = re.search(
1274+
"[\S\s]+element: '([\S\s]+)',[\S\s]+title: '",
1275+
self._tour_steps[name][1]).group(1)
1276+
selector = selector.replace('\\', '')
1277+
self.wait_for_element_present(
1278+
selector, timeout=(settings.SMALL_TIMEOUT))
1279+
else:
1280+
selector = "html"
1281+
except Exception:
1282+
self.__post_messenger_error_message(
1283+
"Tour Error: {'%s'} was not found!"
1284+
"" % selector,
1285+
duration=settings.SMALL_TIMEOUT)
1286+
raise Exception(
1287+
"Tour Error: {'%s'} was not found! "
1288+
"Exiting due to failure on first tour step!"
1289+
"" % selector)
1290+
1291+
self.execute_script(instructions)
1292+
tour_on = True
1293+
while tour_on:
1294+
try:
1295+
time.sleep(0.01)
1296+
result = self.execute_script(
1297+
"return $tour.ended()")
1298+
except Exception:
1299+
tour_on = False
1300+
result = None
1301+
if result is False:
1302+
tour_on = True
1303+
else:
1304+
try:
1305+
time.sleep(0.01)
1306+
result = self.execute_script(
1307+
"return $tour.ended()")
1308+
if result is False:
1309+
time.sleep(0.1)
1310+
continue
1311+
else:
1312+
return
1313+
except Exception:
1314+
tour_on = False
1315+
time.sleep(0.1)
1316+
11371317
def __wait_for_css_query_selector(
11381318
self, selector, timeout=settings.SMALL_TIMEOUT):
11391319
element = None

0 commit comments

Comments
 (0)