Skip to content

Commit f00ca7f

Browse files
committed
Add the ability to export a website tour as a .js file
1 parent 7e4d59f commit f00ca7f

File tree

4 files changed

+201
-0
lines changed

4 files changed

+201
-0
lines changed

examples/tour_examples/ReadMe.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,15 @@ class MyTourClass(BaseCase):
9797
```bash
9898
pytest google_tour.py
9999
```
100+
101+
### Exporting a Tour:
102+
103+
If you want to save the tour you created as a Javascript file, use:
104+
105+
``self.export_tour()``
106+
107+
OR
108+
109+
``self.export_tour(name=None, filename="my_tour.js")``
110+
111+
(``name`` is optional, needed only if you were creating multiple tours at once. ``filename`` is the name of the file to save the Javascript to.) Once you've exported your tour, you can use it outside of SeleniumBase. You can even copy the tour's Javascript code to the Console of your web browser to play the tour from there (you need to be on the correct web page for it to work).

examples/tour_examples/xkcd_tour.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from seleniumbase import BaseCase
2+
3+
4+
class MyTestClass(BaseCase):
5+
6+
def test_basic(self):
7+
self.open('https://xkcd.com/1117/')
8+
self.assert_element('img[alt="My Sky"]')
9+
self.create_shepherd_tour()
10+
self.add_tour_step("Welcome to XKCD!")
11+
self.add_tour_step("This is the XKCD logo.", "#masthead img")
12+
self.add_tour_step("Here's the daily webcomic.", "#comic img")
13+
self.add_tour_step("This is the title.", "#ctitle", alignment="top")
14+
self.add_tour_step("Click here for the next comic.", 'a[rel="next"]')
15+
self.add_tour_step("Or here for the previous comic.", 'a[rel="prev"]')
16+
self.add_tour_step("Learn about the author here.", 'a[rel="author"]')
17+
self.add_tour_step("Click for the license here.", 'a[rel="license"]')
18+
self.add_tour_step("This selects a random comic.", 'a[href*="random"]')
19+
self.add_tour_step("Thanks for taking this tour!")
20+
# self.export_tour() # Use this to export the tour as a .js file
21+
self.play_tour()
22+

help_docs/method_summary.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ self.add_tour_step(message, selector=None, name=None,
110110

111111
self.play_tour(name=None)
112112

113+
self.export_tour(name=None, filename="my_tour.js")
114+
113115
self.activate_messenger()
114116

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

seleniumbase/fixtures/base_case.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,171 @@ def __play_introjs_tour(self, name=None, interval=0):
18381838
tour_on = False
18391839
time.sleep(0.1)
18401840

1841+
def export_tour(self, name=None, filename="my_tour.js"):
1842+
""" Exports a tour as a JS file.
1843+
You can call self.export_tour() anywhere where you could
1844+
normally use self.play_tour()
1845+
It will include necessary resources as well, such as jQuery.
1846+
You'll be able to copy the tour directly into the Console of
1847+
any web browser to play the tour outside of SeleniumBase runs. """
1848+
if not name:
1849+
name = "default"
1850+
if name not in self._tour_steps:
1851+
raise Exception("Tour {%s} does not exist!" % name)
1852+
if not filename.endswith('.js'):
1853+
raise Exception('Tour file must end in ".js"!')
1854+
1855+
tour_type = None
1856+
if "Bootstrap" in self._tour_steps[name][0]:
1857+
tour_type = "bootstrap"
1858+
elif "Hopscotch" in self._tour_steps[name][0]:
1859+
tour_type = "hopscotch"
1860+
elif "IntroJS" in self._tour_steps[name][0]:
1861+
tour_type = "introjs"
1862+
elif "Shepherd" in self._tour_steps[name][0]:
1863+
tour_type = "shepherd"
1864+
else:
1865+
raise Exception('Unknown tour type!')
1866+
1867+
instructions = (
1868+
'''//////// Resources ////////\n\n'''
1869+
'''function injectCSS(css_link) {'''
1870+
'''var head = document.getElementsByTagName("head")[0];'''
1871+
'''var link = document.createElement("link");'''
1872+
'''link.rel = "stylesheet";'''
1873+
'''link.type = "text/css";'''
1874+
'''link.href = css_link;'''
1875+
'''link.crossorigin = "anonymous";'''
1876+
'''head.appendChild(link);'''
1877+
'''};\n'''
1878+
'''function injectJS(js_link) {'''
1879+
'''var head = document.getElementsByTagName("head")[0];'''
1880+
'''var script = document.createElement("script");'''
1881+
'''script.src = js_link;'''
1882+
'''script.defer;'''
1883+
'''script.type="text/javascript";'''
1884+
'''script.crossorigin = "anonymous";'''
1885+
'''script.onload = function() { null };'''
1886+
'''head.appendChild(script);'''
1887+
'''};\n'''
1888+
'''function injectStyle(css) {'''
1889+
'''var head = document.getElementsByTagName("head")[0];'''
1890+
'''var style = document.createElement("style");'''
1891+
'''style.type = "text/css";'''
1892+
'''style.appendChild(document.createTextNode(css));'''
1893+
'''head.appendChild(style);'''
1894+
'''};\n''')
1895+
1896+
if tour_type == "bootstrap":
1897+
jquery_js = constants.JQuery.MIN_JS
1898+
bootstrap_tour_css = constants.BootstrapTour.MIN_CSS
1899+
bootstrap_tour_js = constants.BootstrapTour.MIN_JS
1900+
backdrop_style = style_sheet.bt_backdrop_style
1901+
backdrop_style = backdrop_style.replace('\n', '')
1902+
backdrop_style = self.__escape_quotes_if_needed(backdrop_style)
1903+
instructions += 'injectJS("%s");' % jquery_js
1904+
instructions += '\n\n//////// Resources - Load 2 ////////\n\n'
1905+
instructions += 'injectCSS("%s");\n' % bootstrap_tour_css
1906+
instructions += 'injectStyle("%s");\n' % backdrop_style
1907+
instructions += 'injectJS("%s");' % bootstrap_tour_js
1908+
1909+
elif tour_type == "hopscotch":
1910+
hopscotch_css = constants.Hopscotch.MIN_CSS
1911+
hopscotch_js = constants.Hopscotch.MIN_JS
1912+
backdrop_style = style_sheet.hops_backdrop_style
1913+
backdrop_style = backdrop_style.replace('\n', '')
1914+
backdrop_style = self.__escape_quotes_if_needed(backdrop_style)
1915+
instructions += 'injectCSS("%s");\n' % hopscotch_css
1916+
instructions += 'injectStyle("%s");\n' % backdrop_style
1917+
instructions += 'injectJS("%s");' % hopscotch_js
1918+
1919+
elif tour_type == "introjs":
1920+
intro_css = constants.IntroJS.MIN_CSS
1921+
intro_js = constants.IntroJS.MIN_JS
1922+
instructions += 'injectCSS("%s");\n' % intro_css
1923+
instructions += 'injectJS("%s");' % intro_js
1924+
1925+
elif tour_type == "shepherd":
1926+
jquery_js = constants.JQuery.MIN_JS
1927+
shepherd_js = constants.Shepherd.MIN_JS
1928+
sh_theme_arrows_css = constants.Shepherd.THEME_ARROWS_CSS
1929+
sh_theme_arrows_fix_css = constants.Shepherd.THEME_ARR_FIX_CSS
1930+
sh_theme_default_css = constants.Shepherd.THEME_DEFAULT_CSS
1931+
sh_theme_dark_css = constants.Shepherd.THEME_DARK_CSS
1932+
sh_theme_sq_css = constants.Shepherd.THEME_SQ_CSS
1933+
sh_theme_sq_dark_css = constants.Shepherd.THEME_SQ_DK_CSS
1934+
tether_js = constants.Tether.MIN_JS
1935+
spinner_css = constants.Messenger.SPINNER_CSS
1936+
backdrop_style = style_sheet.sh_backdrop_style
1937+
backdrop_style = backdrop_style.replace('\n', '')
1938+
backdrop_style = self.__escape_quotes_if_needed(backdrop_style)
1939+
instructions += 'injectCSS("%s");\n' % spinner_css
1940+
instructions += 'injectJS("%s");\n' % jquery_js
1941+
instructions += 'injectJS("%s");' % tether_js
1942+
instructions += '\n\n//////// Resources - Load 2 ////////\n\n'
1943+
instructions += 'injectCSS("%s");' % sh_theme_arrows_css
1944+
instructions += 'injectCSS("%s");' % sh_theme_arrows_fix_css
1945+
instructions += 'injectCSS("%s");' % sh_theme_default_css
1946+
instructions += 'injectCSS("%s");' % sh_theme_dark_css
1947+
instructions += 'injectCSS("%s");' % sh_theme_sq_css
1948+
instructions += 'injectCSS("%s");\n' % sh_theme_sq_dark_css
1949+
instructions += 'injectStyle("%s");\n' % backdrop_style
1950+
instructions += 'injectJS("%s");' % shepherd_js
1951+
1952+
instructions += '\n\n//////// Tour Code ////////\n\n'
1953+
for tour_step in self._tour_steps[name]:
1954+
instructions += tour_step
1955+
1956+
if tour_type == "bootstrap":
1957+
instructions += (
1958+
"""]);
1959+
// Initialize the tour
1960+
tour.init();
1961+
// Start the tour
1962+
tour.start();
1963+
$tour = tour;
1964+
$tour.restart();\n
1965+
""")
1966+
elif tour_type == "hopscotch":
1967+
instructions += (
1968+
"""]
1969+
};
1970+
// Start the tour!
1971+
hopscotch.startTour(tour);
1972+
$tour = hopscotch;\n
1973+
""")
1974+
elif tour_type == "introjs":
1975+
instructions += (
1976+
"""]
1977+
});
1978+
intro.setOption("disableInteraction", true);
1979+
intro.setOption("overlayOpacity", .29);
1980+
intro.setOption("scrollToElement", true);
1981+
intro.setOption("keyboardNavigation", true);
1982+
intro.setOption("exitOnEsc", false);
1983+
intro.setOption("exitOnOverlayClick", false);
1984+
intro.setOption("showStepNumbers", false);
1985+
intro.setOption("showProgress", false);
1986+
intro.start();
1987+
$tour = intro;
1988+
};
1989+
startIntro();\n
1990+
""")
1991+
elif tour_type == "shepherd":
1992+
instructions += (
1993+
"""
1994+
tour.start();
1995+
$tour = tour;\n
1996+
""")
1997+
else:
1998+
pass
1999+
2000+
import codecs
2001+
out_file = codecs.open(filename, "w+")
2002+
out_file.writelines(instructions)
2003+
out_file.close()
2004+
print('\n>>> [%s] was saved!\n' % filename)
2005+
18412006
def __wait_for_css_query_selector(
18422007
self, selector, timeout=settings.SMALL_TIMEOUT):
18432008
element = None

0 commit comments

Comments
 (0)