From 61384e4954bfc56be7c01a506e2f6e2a778a70e6 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:09:35 -0400 Subject: [PATCH 1/7] Add the ability to create presentations with SeleniumBase --- .gitignore | 3 + examples/presenter/ReadMe.md | 149 ++++++++++++++++++++++++++ examples/presenter/my_presentation.py | 56 ++++++++++ help_docs/method_summary.md | 10 ++ seleniumbase/fixtures/base_case.py | 147 +++++++++++++++++++++++++ seleniumbase/fixtures/constants.py | 23 ++++ 6 files changed, 388 insertions(+) create mode 100755 examples/presenter/ReadMe.md create mode 100755 examples/presenter/my_presentation.py diff --git a/.gitignore b/.gitignore index 79f356cacf0..038ce756d79 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,9 @@ archived_logs geckodriver.log pytestdebug.log +# Presentations +presentations_saved + # Reports latest_report report_archives diff --git a/examples/presenter/ReadMe.md b/examples/presenter/ReadMe.md new file mode 100755 index 00000000000..09386cf5cc6 --- /dev/null +++ b/examples/presenter/ReadMe.md @@ -0,0 +1,149 @@ +

SeleniumBase

+ +# 📰 Presenter 📰 + +SeleniumBase Presenter allows you to create an HTML presentation with only a few lines of Python. +The Reveal-JS library is used for running the presentations. + +**Here's a sample slide:** + +
+ +Slides can include HTML, code, images, and iframes. + +Here's how to run the example presentation: +``` +cd examples/presenter +pytest my_presentation.py +``` + + +### Creating a new presentation: + +```python +self.create_presentation(name=None, show_notes=True) + """ Creates a Reveal-JS presentation that you can add slides to. + @Params + name - If creating multiple presentations at the same time, + use this to specify the name of the current presentation. + show_notes - When set to True, the Notes feature becomes enabled, + which allows presenters to see notes next to slides. + """ +``` + +If creating multiple presentations at the same time, you can pass the ``name`` parameter to distinguish between different presentations. +Notes are enabled by default unless you specify: +``show_notes=False`` when calling. + + +### Adding a slide to a presentation: + +```python +self.add_slide(content=None, image=None, code=None, iframe=None, + notes=None, name=None) + """ Allows the user to add slides to a presentation. + @Params + content - The HTML content to display on the presentation slide. + image - Attach an image (from a URL link) to the slide. + code - Attach code of any programming language to the slide. + Language-detection will be used to add syntax formatting. + iframe - Attach an iFrame (from a URL link) to the slide. + notes - Additional notes to include with the slide. + ONLY SEEN if show_notes is set for the presentation. + name - If creating multiple presentations at the same time, + use this to select the presentation to add slides to. + """ +``` + + +### Running a presentation: + +```python +self.begin_presentation(filename="my_presentation.html", name=None) + """ Begin a Reveal-JS Presentation in the web browser. """ +``` + +Before the presentation is run, the full HTML is saved to the ``presentations_saved/`` folder. + + +All methods have the optional ``name`` argument, which is only needed if you're creating multiple presentations at once. + +### Here's an example of using SeleniumBase Presenter: + +```python +from seleniumbase import BaseCase + + +class MyPresenterClass(BaseCase): + + def test_presenter(self): + self.create_presentation() + self.add_slide( + "

Welcome!

" + "

Enjoy the Presentation!

") + self.add_slide( + '

SeleniumBase "Presenter"

' + '' + '

A tool for creating presentations

') + self.add_slide( + '

You can add HTML to any slide:


' + '' + '' + '
Row 1Row 2
Value 1Value 2

' + '

(HTML table example)

') + self.add_slide( + "

You can display code:

", + code=( + 'from seleniumbase import BaseCase\n\n' + 'class MyTestClass(BaseCase):\n\n' + ' def test_basic(self):\n' + ' self.open("https://store.xkcd.com/search")\n' + ' self.type(\'input[name="q"]\', "xkcd book\\n")\n' + ' self.assert_text("xkcd: volume 0", "h3")\n' + ' self.open("https://xkcd.com/353/")\n' + ' self.assert_title("xkcd: Python")\n' + ' self.assert_element(\'img[alt="Python"]\')\n' + ' self.click(\'a[rel="license"]\')\n' + ' self.assert_text("free to copy and reuse")\n' + ' self.go_back()\n' + ' self.click_link_text("About")\n' + ' self.assert_exact_text("xkcd.com", "h2")\n')) + self.add_slide( + "

You can highlight code:

", + code=( + 'from seleniumbase import BaseCase\n\n' + 'class MyTestClass(BaseCase):\n\n' + ' def test_basic(self):\n' + ' self.open("https://store.xkcd.com/search")\n' + ' self.type(\'input[name="q"]\', "xkcd book\\n")\n')) + self.add_slide( + "

You can add notes to slides:

", + notes="

") + self.add_slide( + "

You can add images to slides:

", + image="https://seleniumbase.io/img/sb_logo_10.png") + self.add_slide( + "

You can add iframes to slides:

", + iframe="https://seleniumbase.io/demo_page") + self.add_slide("

The End

") + self.begin_presentation() +``` + +#### This example is from [my_presentation.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/my_presentation.py), which you can run from the ``examples/presenter`` folder with the following command: + +```bash +pytest my_presentation.py +``` + +### Saving a presentation: + +If you want to save the presentation you created as an HTML file, use: + +```python +self.save_presentation(filename="my_presentation.html", name=None) +``` + +Presentations automatically get saved when calling: +```python +self.begin_presentation() +``` diff --git a/examples/presenter/my_presentation.py b/examples/presenter/my_presentation.py new file mode 100755 index 00000000000..2a646f1b908 --- /dev/null +++ b/examples/presenter/my_presentation.py @@ -0,0 +1,56 @@ +from seleniumbase import BaseCase + + +class MyPresenterClass(BaseCase): + + def test_presenter(self): + self.create_presentation() + self.add_slide( + "

Welcome!

" + "

Enjoy the Presentation!

") + self.add_slide( + '

SeleniumBase "Presenter"

' + '' + '

A tool for creating presentations

') + self.add_slide( + '

You can add HTML to any slide:


' + '' + '' + '
Row 1Row 2
Value 1Value 2

' + '

(HTML table example)

') + self.add_slide( + "

You can display code:

", + code=( + 'from seleniumbase import BaseCase\n\n' + 'class MyTestClass(BaseCase):\n\n' + ' def test_basic(self):\n' + ' self.open("https://store.xkcd.com/search")\n' + ' self.type(\'input[name="q"]\', "xkcd book\\n")\n' + ' self.assert_text("xkcd: volume 0", "h3")\n' + ' self.open("https://xkcd.com/353/")\n' + ' self.assert_title("xkcd: Python")\n' + ' self.assert_element(\'img[alt="Python"]\')\n' + ' self.click(\'a[rel="license"]\')\n' + ' self.assert_text("free to copy and reuse")\n' + ' self.go_back()\n' + ' self.click_link_text("About")\n' + ' self.assert_exact_text("xkcd.com", "h2")\n')) + self.add_slide( + "

You can highlight code:

", + code=( + 'from seleniumbase import BaseCase\n\n' + 'class MyTestClass(BaseCase):\n\n' + ' def test_basic(self):\n' + ' self.open("https://store.xkcd.com/search")\n' + ' self.type(\'input[name="q"]\', "xkcd book\\n")\n')) + self.add_slide( + "

You can add notes to slides:

", + notes="

") + self.add_slide( + "

You can add images to slides:

", + image="https://seleniumbase.io/img/sb_logo_10.png") + self.add_slide( + "

You can add iframes to slides:

", + iframe="https://seleniumbase.io/demo_page") + self.add_slide("

The End

") + self.begin_presentation() diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index b965d3a5b52..33730b71b2f 100755 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -356,6 +356,16 @@ self.add_meta_tag(http_equiv=None, content=None) ############ +self.create_presentation(name=None, show_notes=True) + +self.add_slide(content=None, image=None, code=None, iframe=None, notes=None, name=None) + +self.save_presentation(filename="my_presentation.html", name=None) + +self.begin_presentation(filename="my_presentation.html", name=None) + +############ + self.create_tour(name=None, theme=None) self.create_shepherd_tour(name=None, theme=None) diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 17d2fa7bf1e..ccbdab00da6 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -83,6 +83,7 @@ def __init__(self, *args, **kwargs): self.__device_pixel_ratio = None # Requires self._* instead of self.__* for external class use self._language = "English" + self._presentation_slides = {} self._html_report_extra = [] # (Used by pytest_plugin.py) self._default_driver = None self._drivers_list = [] @@ -3150,6 +3151,152 @@ def add_meta_tag(self, http_equiv=None, content=None): ############ + def create_presentation(self, name=None, show_notes=True): + """ Creates a Reveal-JS presentation that you can add slides to. + @Params + name - If creating multiple presentations at the same time, + use this to specify the name of the current presentation. + show_notes - When set to True, the Notes feature becomes enabled, + which allows presenters to see notes next to slides. + """ + if not name: + name = "default" + + new_presentation = ( + """ + + + + + + + +
+
+ """ % (constants.Reveal.MIN_CSS, constants.Reveal.WHITE_MIN_CSS)) + + self._presentation_slides[name] = [] + self._presentation_slides[name].append(new_presentation) + + def add_slide(self, content=None, image=None, code=None, iframe=None, + notes=None, name=None): + """ Allows the user to add slides to a presentation. + @Params + content - The HTML content to display on the presentation slide. + image - Attach an image (from a URL link) to the slide. + code - Attach code of any programming language to the slide. + Language-detection will be used to add syntax formatting. + iframe - Attach an iFrame (from a URL link) to the slide. + notes - Additional notes to include with the slide. + ONLY SEEN if show_notes is set for the presentation. + name - If creating multiple presentations at the same time, + use this to select the presentation to add slides to. + """ + + if not name: + name = "default" + if name not in self._presentation_slides: + # Create a presentation if it doesn't already exist + self.create_presentation(name=name, show_notes=True) + if not content: + content = "" + if not notes: + notes = "" + + html = ('
%s' % content) + if image: + html += '
' % image + if code: + html += '
' + html += '
%s
' % code + if iframe: + html += ('
' + '' % iframe) + html += '' % notes + html += '
' + + self._presentation_slides[name].append(html) + + def save_presentation(self, filename="my_presentation.html", name=None): + """ Saves a Reveal-JS Presentation to a folder for later use. """ + + if not name: + name = "default" + if name not in self._presentation_slides: + raise Exception("Presentation {%s} does not exist!" % name) + if not filename.endswith('.html'): + raise Exception('Presentation file must end in ".html"!') + + the_html = "" + for slide in self._presentation_slides[name]: + the_html += slide + + the_html += ( + """ +
+
+ + + + + + + """ % (constants.Reveal.MIN_JS, + constants.Reveal.MARKED_JS, + constants.PrettifyJS.RUN_PRETTIFY_JS)) + + saved_presentations_folder = constants.Presentations.SAVED_FOLDER + if saved_presentations_folder.endswith("/"): + saved_presentations_folder = saved_presentations_folder[:-1] + if not os.path.exists(saved_presentations_folder): + try: + os.makedirs(saved_presentations_folder) + except Exception: + pass + file_path = saved_presentations_folder + "/" + filename + out_file = codecs.open(file_path, "w+") + out_file.writelines(the_html) + out_file.close() + print('\n>>> [%s] was saved!\n' % file_path) + return file_path + + def begin_presentation(self, filename="my_presentation.html", name=None): + """ Begin a Reveal-JS Presentation in the web browser. """ + + if self.headless: + return # Presentations should not run in headless mode. + if not name: + name = "default" + if name not in self._presentation_slides: + raise Exception("Presentation {%s} does not exist!" % name) + if not filename.endswith('.html'): + raise Exception('Presentation file must end in ".html"!') + + end_slide = ( + '
' + '

') + self._presentation_slides[name].append(end_slide) + file_path = self.save_presentation(name=name, filename=filename) + self._presentation_slides[name].pop() + + self.open_html_file(file_path) + presentation_folder = constants.Presentations.SAVED_FOLDER + while (len(self.driver.window_handles) > 0 and ( + presentation_folder in self.get_current_url())): + time.sleep(0.1) + if self.is_element_visible("p.End_Presentation_Now"): + break + + ############ + def create_tour(self, name=None, theme=None): """ Creates a tour for a website. By default, the Shepherd JavaScript Library is used with the Shepherd "Light" / "Arrows" theme. diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py index 59bc6ad11ca..509bc5b4afd 100755 --- a/seleniumbase/fixtures/constants.py +++ b/seleniumbase/fixtures/constants.py @@ -19,6 +19,10 @@ class Files: ARCHIVED_DOWNLOADS_FOLDER = "archived_files" +class Presentations: + SAVED_FOLDER = "presentations_saved" + + class SavedCookies: STORAGE_FOLDER = "saved_cookies" @@ -84,6 +88,25 @@ class HtmlInspector: "html-inspector/%s/html-inspector.min.js" % VER) +class PrettifyJS: + RUN_PRETTIFY_JS = ("https://cdn.jsdelivr.net/gh/google/" + "code-prettify@master/loader/run_prettify.js") + + +class Reveal: + VER = "3.8.0" + MIN_CSS = ("https://cdnjs.cloudflare.com/ajax/libs/" + "reveal.js/%s/css/reveal.min.css" % VER) + WHITE_MIN_CSS = ("https://cdnjs.cloudflare.com/ajax/libs/" + "reveal.js/%s/css/theme/white.min.css" % VER) + MIN_JS = ("https://cdnjs.cloudflare.com/ajax/libs/" + "reveal.js/%s/js/reveal.min.js" % VER) + MARKED_JS = ("https://cdnjs.cloudflare.com/ajax/libs/" + "reveal.js/%s/plugin/markdown/marked.js" % VER) + MARKDOWN_MIN_JS = ("https://cdnjs.cloudflare.com/ajax/libs/" + "reveal.js/%s/plugin/markdown/markdown.min.js" % VER) + + class BootstrapTour: VER = "0.11.0" MIN_CSS = ("https://cdnjs.cloudflare.com/ajax/libs/" From a02e450cd32f667f59215a0108e35ccde71e17bd Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:09:56 -0400 Subject: [PATCH 2/7] Update console scripts --- seleniumbase/console_scripts/sb_print.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seleniumbase/console_scripts/sb_print.py b/seleniumbase/console_scripts/sb_print.py index 80476ffbbde..6fd1eede863 100755 --- a/seleniumbase/console_scripts/sb_print.py +++ b/seleniumbase/console_scripts/sb_print.py @@ -342,6 +342,13 @@ def main(): if console_width and (code_width + extra_r_spaces < console_width): used_width = code_width + extra_r_spaces + try: + if "🗺️" in the_code: + # Fix width of an emoji + the_code = the_code.replace("🗺️", "🗺️ ") + except Exception: + pass + magic_syntax = Syntax( the_code, code_lang, theme="monokai", line_numbers=line_numbers, code_width=used_width, From e7b6aef4db85c777463d2335107145b60b678ae2 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:10:23 -0400 Subject: [PATCH 3/7] Create a new tour example --- examples/tour_examples/maps_introjs_tour.py | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100755 examples/tour_examples/maps_introjs_tour.py diff --git a/examples/tour_examples/maps_introjs_tour.py b/examples/tour_examples/maps_introjs_tour.py new file mode 100755 index 00000000000..fe1c9293326 --- /dev/null +++ b/examples/tour_examples/maps_introjs_tour.py @@ -0,0 +1,34 @@ +from seleniumbase import BaseCase + + +class MyTourClass(BaseCase): + + def test_google_maps_tour(self): + self.open("https://www.google.com/maps/@42.3598616,-71.0912631,15z") + self.wait_for_element("#searchboxinput") + self.wait_for_element("#minimap") + self.wait_for_element("#zoom") + + self.create_tour(theme="introjs") + self.add_tour_step("Welcome to Google Maps!", + title="✅ SeleniumBase Tours 🌎") + self.add_tour_step("Type in a location here.", "#searchboxinput", + title="Search Box") + self.add_tour_step("Then click here to show it on the map.", + "#searchbox-searchbutton", alignment="bottom") + self.add_tour_step("Or click here to get driving directions.", + "#searchbox-directions", alignment="bottom") + self.add_tour_step("Use this button to switch to Satellite view.", + "#minimap div.widget-minimap", alignment="right") + self.add_tour_step("Click here to zoom in.", "#widget-zoom-in", + alignment="left") + self.add_tour_step("Or click here to zoom out.", "#widget-zoom-out", + alignment="left") + self.add_tour_step("Use the Menu button to see more options.", + ".searchbox-hamburger-container", alignment="right") + self.add_tour_step("Or click here to see more Google apps.", + '[title="Google apps"]', alignment="left") + self.add_tour_step("Thanks for using SeleniumBase Tours!", + title="🚃 End of Guided Tour 🚃") + self.export_tour(filename="google_maps_introjs_tour.js") + self.play_tour() From b63349568324c17093aa1655284d891168098937 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:12:09 -0400 Subject: [PATCH 4/7] Update Python dependencies --- docs/requirements.txt | 2 +- requirements.txt | 13 ++++++------- setup.py | 11 +++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 83f280b6b45..9b2f93ab1d3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ livereload==2.6.2;python_version>="3.6" mkdocs==1.1.2 -mkdocs-material==5.3.0 +mkdocs-material==5.3.3 mkdocs-simple-hooks==0.1.1 mkdocs-material-extensions==1.0 mkdocs-minify-plugin==0.3.0 diff --git a/requirements.txt b/requirements.txt index 0981f942acf..4a663531853 100755 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ setuptools-scm>=4.1.2 wheel>=0.34.2 six==1.15.0 nose==1.3.7 -ipdb==0.13.2 +ipdb==0.13.3 idna==2.9 chardet==3.0.4 urllib3==1.25.9 @@ -14,16 +14,15 @@ requests==2.24.0 selenium==3.141.0 pluggy==0.13.1 attrs>=19.3.0 -py==1.8.1;sys_platform=="win32" -py==1.8.2;sys_platform!="win32" +py==1.8.1 pytest==4.6.11;python_version<"3.5" pytest==5.4.3;python_version>="3.5" pytest-cov==2.10.0 -pytest-forked==1.1.3 +pytest-forked==1.2.0 pytest-html==1.22.1;python_version<"3.6" pytest-html==2.0.1;python_version>="3.6" pytest-metadata==1.8.0;python_version<"3.6" -pytest-metadata==1.9.0;python_version>="3.6" +pytest-metadata==1.10.0;python_version>="3.6" pytest-ordering==0.6 pytest-rerunfailures==8.0;python_version<"3.6" pytest-rerunfailures==9.0;python_version>="3.6" @@ -43,11 +42,11 @@ coverage==5.1 pyotp==2.3.0 boto==2.49.0 cffi==1.14.0 -rich==2.2.3;python_version>="3.6" and python_version<"4.0" +rich==2.2.6;python_version>="3.6" and python_version<"4.0" flake8==3.7.9;python_version<"3.5" flake8==3.8.3;python_version>="3.5" pyflakes==2.1.1;python_version<"3.5" pyflakes==2.2.0;python_version>="3.5" -certifi>=2020.4.5.2 +certifi>=2020.6.20 pdfminer.six==20191110;python_version<"3.5" pdfminer.six==20200517;python_version>="3.5" diff --git a/setup.py b/setup.py index aacd5c89dd1..b5d08a8fd51 100755 --- a/setup.py +++ b/setup.py @@ -106,16 +106,15 @@ 'selenium==3.141.0', 'pluggy==0.13.1', 'attrs>=19.3.0', - 'py==1.8.1;sys_platform=="win32"', - 'py==1.8.2;sys_platform!="win32"', + 'py==1.8.1', 'pytest==4.6.11;python_version<"3.5"', 'pytest==5.4.3;python_version>="3.5"', 'pytest-cov==2.10.0', - 'pytest-forked==1.1.3', + 'pytest-forked==1.2.0', 'pytest-html==1.22.1;python_version<"3.6"', 'pytest-html==2.0.1;python_version>="3.6"', 'pytest-metadata==1.8.0;python_version<"3.6"', - 'pytest-metadata==1.9.0;python_version>="3.6"', + 'pytest-metadata==1.10.0;python_version>="3.6"', 'pytest-ordering==0.6', 'pytest-rerunfailures==8.0;python_version<"3.6"', 'pytest-rerunfailures==9.0;python_version>="3.6"', @@ -135,12 +134,12 @@ 'pyotp==2.3.0', 'boto==2.49.0', 'cffi==1.14.0', - 'rich==2.2.3;python_version>="3.6" and python_version<"4.0"', + 'rich==2.2.6;python_version>="3.6" and python_version<"4.0"', 'flake8==3.7.9;python_version<"3.5"', 'flake8==3.8.3;python_version>="3.5"', 'pyflakes==2.1.1;python_version<"3.5"', 'pyflakes==2.2.0;python_version>="3.5"', - 'certifi>=2020.4.5.2', + 'certifi>=2020.6.20', 'pdfminer.six==20191110;python_version<"3.5"', 'pdfminer.six==20200517;python_version>="3.5"', ], From 799c56b47214a0c3cf977d90ee51e75db7196984 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:12:26 -0400 Subject: [PATCH 5/7] Update the ReadMe --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 348ca80b6d4..88fb87710f9 100755 --- a/README.md +++ b/README.md @@ -27,20 +27,21 @@ Tests are run with "pytest". Browsers are controlled by WebDriver.

-🚀 Get Started | -🧙‍♂️ CMD Options | -🏰 Features | -👨‍🏫 Examples | -📱 Mobile Testing | -📊 Reports +🚀Get Started | +🧙‍♂️CMD Options | +🏰Features | +👨‍🏫Examples | +📊Reports | +📱Mobile | +🤖CI
-📖 API | +📖API | 🗺️ Tours | -🌎 Languages | -⏺️ Recorder | -🛂 MasterQA | -👩‍🎨 Visual Tests | -🤖 GitHub-CI +🌎Languages | +📰Presenter | +⏺️Recorder | +👩‍🎨Visual Tests | +🛂MasterQA

SeleniumBase

From db7fd0fcb96b9aacc910948efabbc668c40f2298 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:13:08 -0400 Subject: [PATCH 6/7] Version 1.41.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b5d08a8fd51..6fce97ad0e4 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ setup( name='seleniumbase', - version='1.40.2', + version='1.41.0', description='Fast, Easy, and Reliable Browser Automation & Testing.', long_description=long_description, long_description_content_type='text/markdown', From ccde2ef83808586b4b054b4a0f008e65d5ea2264 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Thu, 25 Jun 2020 18:15:16 -0400 Subject: [PATCH 7/7] Update mkdocs --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 92e25ef87d3..91d5030898a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -73,6 +73,7 @@ nav: - Console Scripts: seleniumbase/console_scripts/ReadMe.md - JS Package Manager: help_docs/js_package_manager.md - Site Tours: examples/tour_examples/ReadMe.md + - Presenter: examples/presenter/ReadMe.md - Visual Testing: examples/visual_testing/ReadMe.md - Integrations: - Logging and Reports: examples/example_logs/ReadMe.md