@@ -861,6 +861,50 @@ def __is_bootstrap_activated(self):
861861 except Exception :
862862 return False
863863
864+ def __activate_introjs (self ):
865+ """ Allows you to use IntroJS Tours with SeleniumBase
866+ https://introjs.com/
867+ """
868+ intro_css = constants .IntroJS .MIN_CSS
869+ intro_js = constants .IntroJS .MIN_JS
870+
871+ verify_script = ("""// Verify IntroJS activated
872+ var intro2 = introJs();
873+ """ )
874+
875+ self .__activate_bootstrap ()
876+ self .wait_for_ready_state_complete ()
877+ for x in range (4 ):
878+ self .activate_jquery ()
879+ self .add_css_link (intro_css )
880+ self .add_js_link (intro_js )
881+ time .sleep (0.1 )
882+
883+ for x in range (int (settings .MINI_TIMEOUT * 2.0 )):
884+ # IntroJS needs a small amount of time to load & activate.
885+ try :
886+ self .execute_script (verify_script )
887+ self .wait_for_ready_state_complete ()
888+ time .sleep (0.05 )
889+ return
890+ except Exception :
891+ time .sleep (0.15 )
892+
893+ raise Exception (
894+ '''Unable to load jQuery on "%s" due to a possible violation '''
895+ '''of the website's Content Security Policy '''
896+ '''directive. ''' % self .driver .current_url )
897+
898+ def __is_introjs_activated (self ):
899+ verify_script = ("""// Verify IntroJS activated
900+ var intro2 = introJs();
901+ """ )
902+ try :
903+ self .execute_script (verify_script )
904+ return True
905+ except Exception :
906+ return False
907+
864908 def __activate_shepherd (self ):
865909 """ Allows you to use Shepherd Tours with SeleniumBase
866910 http://github.hubspot.com/shepherd/docs/welcome/
@@ -976,11 +1020,34 @@ def create_bootstrap_tour(self, name=None):
9761020 if not name :
9771021 name = "default"
9781022
979- new_tour = ("""
980- // Bootstrap Tour
981- var tour = new Tour({
982- steps: [
983- """ )
1023+ new_tour = (
1024+ """
1025+ // Bootstrap Tour
1026+ var tour = new Tour({
1027+ });
1028+ tour.addSteps([
1029+ """ )
1030+
1031+ self ._tour_steps [name ] = []
1032+ self ._tour_steps [name ].append (new_tour )
1033+
1034+ def create_introjs_tour (self , name = None ):
1035+ """ Creates an IntroJS tour for a website.
1036+ @Params
1037+ name - If creating multiple tours, use this to select the
1038+ tour you wish to add steps to.
1039+ """
1040+ if not name :
1041+ name = "default"
1042+
1043+ new_tour = (
1044+ """
1045+ // IntroJS Tour
1046+ function startIntro(){
1047+ var intro = introJs();
1048+ intro.setOptions({
1049+ steps: [
1050+ """ )
9841051
9851052 self ._tour_steps [name ] = []
9861053 self ._tour_steps [name ].append (new_tour )
@@ -1032,6 +1099,10 @@ def add_tour_step(self, message, selector=None, name=None,
10321099 self .__add_bootstrap_tour_step (
10331100 message , selector = selector , name = name , title = title ,
10341101 alignment = alignment , duration = duration )
1102+ elif "IntroJS" in self ._tour_steps [name ][0 ]:
1103+ self .__add_introjs_tour_step (
1104+ message , selector = selector , name = name , title = title ,
1105+ alignment = alignment )
10351106 else :
10361107 self .__add_shepherd_tour_step (
10371108 message , selector = selector , name = name , title = title ,
@@ -1120,6 +1191,34 @@ def __add_bootstrap_tour_step(self, message, selector=None, name=None,
11201191
11211192 self ._tour_steps [name ].append (step )
11221193
1194+ def __add_introjs_tour_step (self , message , selector = None , name = None ,
1195+ title = None , alignment = None ):
1196+ """ Allows the user to add tour steps for a website.
1197+ @Params
1198+ message - The message to display.
1199+ selector - The CSS Selector of the Element to attach to.
1200+ name - If creating multiple tours, use this to select the
1201+ tour you wish to add steps to.
1202+ alignment - Choose from "top", "bottom", "left", and "right".
1203+ ("top" is the default alignment).
1204+ """
1205+ if selector != "html" :
1206+ element_row = "element: '%s'," % selector
1207+ else :
1208+ element_row = ""
1209+
1210+ if title :
1211+ message = "<center><b>" + title + "</b></center><hr>" + message
1212+
1213+ message = '<font size=\" 3\" color=\" #33475B\" >' + message + '</font>'
1214+
1215+ step = ("""{%s
1216+ intro: '%s',
1217+ position: '%s'},
1218+ """ % (element_row , message , alignment ))
1219+
1220+ self ._tour_steps [name ].append (step )
1221+
11231222 def play_tour (self , name = None , interval = 0 ):
11241223 """ Plays a tour on the current website.
11251224 @Params
@@ -1138,6 +1237,8 @@ def play_tour(self, name=None, interval=0):
11381237
11391238 if "Bootstrap" in self ._tour_steps [name ][0 ]:
11401239 self .__play_bootstrap_tour (name = name , interval = interval )
1240+ elif "IntroJS" in self ._tour_steps [name ][0 ]:
1241+ self .__play_introjs_tour (name = name , interval = interval )
11411242 else :
11421243 self .__play_shepherd_tour (name = name , interval = interval )
11431244
@@ -1331,6 +1432,125 @@ def __play_bootstrap_tour(self, name=None, interval=0):
13311432 tour_on = False
13321433 time .sleep (0.1 )
13331434
1435+ def __play_introjs_tour (self , name = None , interval = 0 ):
1436+ """ Plays a tour on the current website.
1437+ @Params
1438+ name - If creating multiple tours, use this to select the
1439+ tour you wish to play.
1440+ """
1441+ instructions = ""
1442+ for tour_step in self ._tour_steps [name ]:
1443+ instructions += tour_step
1444+ instructions += (
1445+ """]
1446+ });
1447+ intro.setOption("disableInteraction", true);
1448+ intro.setOption("overlayOpacity", .29);
1449+ intro.setOption("scrollToElement", true);
1450+ intro.setOption("keyboardNavigation", true);
1451+ intro.setOption("exitOnEsc", false);
1452+ intro.setOption("exitOnOverlayClick", false);
1453+ intro.setOption("showStepNumbers", false);
1454+ intro.setOption("showProgress", false);
1455+ intro.start();
1456+ $intro = intro;
1457+ }
1458+ startIntro();
1459+ """ )
1460+
1461+ autoplay = False
1462+ if interval and interval > 0 :
1463+ autoplay = True
1464+ interval = float (interval )
1465+ if interval < 0.5 :
1466+ interval = 0.5
1467+
1468+ if not self .__is_introjs_activated ():
1469+ self .__activate_introjs ()
1470+
1471+ if len (self ._tour_steps [name ]) > 1 :
1472+ try :
1473+ if "element: " in self ._tour_steps [name ][1 ]:
1474+ selector = re .search (
1475+ r"[\S\s]+element: '([\S\s]+)',[\S\s]+intro: '" ,
1476+ self ._tour_steps [name ][1 ]).group (1 )
1477+ selector = selector .replace ('\\ ' , '' )
1478+ self .wait_for_element_present (
1479+ selector , timeout = settings .SMALL_TIMEOUT )
1480+ else :
1481+ selector = "html"
1482+ except Exception :
1483+ self .__post_messenger_error_message (
1484+ "Tour Error: {'%s'} was not found!"
1485+ "" % selector ,
1486+ duration = settings .SMALL_TIMEOUT )
1487+ raise Exception (
1488+ "Tour Error: {'%s'} was not found! "
1489+ "Exiting due to failure on first tour step!"
1490+ "" % selector )
1491+
1492+ self .execute_script (instructions )
1493+ tour_on = True
1494+ if autoplay :
1495+ start_ms = time .time () * 1000.0
1496+ stop_ms = start_ms + (interval * 1000.0 )
1497+ latest_step = 0
1498+ while tour_on :
1499+ try :
1500+ time .sleep (0.01 )
1501+ if self .browser != "firefox" :
1502+ result = self .execute_script (
1503+ "return $intro._currentStep" )
1504+ else :
1505+ self .wait_for_element_present (
1506+ ".introjs-tooltip" , timeout = 0.4 )
1507+ result = True
1508+ except Exception :
1509+ tour_on = False
1510+ result = None
1511+ if result is not None :
1512+ tour_on = True
1513+ if autoplay :
1514+ try :
1515+ current_step = self .execute_script (
1516+ "return $intro._currentStep" )
1517+ except Exception :
1518+ continue
1519+ if current_step != latest_step :
1520+ latest_step = current_step
1521+ start_ms = time .time () * 1000.0
1522+ stop_ms = start_ms + (interval * 1000.0 )
1523+ now_ms = time .time () * 1000.0
1524+ if now_ms >= stop_ms :
1525+ if current_step == latest_step :
1526+ self .execute_script ("return $intro.nextStep()" )
1527+ try :
1528+ latest_step = self .execute_script (
1529+ "return $intro._currentStep" )
1530+ start_ms = time .time () * 1000.0
1531+ stop_ms = start_ms + (interval * 1000.0 )
1532+ except Exception :
1533+ pass
1534+ continue
1535+ else :
1536+ try :
1537+ time .sleep (0.01 )
1538+ if self .browser != "firefox" :
1539+ result = self .execute_script (
1540+ "return $intro._currentStep" )
1541+ else :
1542+ self .wait_for_element_present (
1543+ ".introjs-tooltip" , timeout = 0.4 )
1544+ result = True
1545+ if result is not None :
1546+ time .sleep (0.1 )
1547+ continue
1548+ else :
1549+ return
1550+ except Exception :
1551+ tour_on = False
1552+ time .sleep (0.1 )
1553+
13341554 def __wait_for_css_query_selector (
13351555 self , selector , timeout = settings .SMALL_TIMEOUT ):
13361556 element = None
0 commit comments