+
+Permanent link to this comic: http://xkcd.com/731/
+Image URL (for hotlinking/embedding): http://imgs.xkcd.com/comics/desert_island.png
+
[[A man sits writing in a diary on a desert island, only the sandy tip of which with a palm tree on it stands above the water. Beneath the surface is a kelp forest, some sharks, a stingray, a shipwreck, a submarine, several large jellyfish, a giant squid fighting a sperm whale, a crashed plane, some coral formations, a thermal vent emitting a plume of smoke surrounded by several annelids, and a snail.]]
+Man: Day 44: Still stranded, with nothing but flat empty water as far as the eye can see.
+
+{{Title text: Telescopes and bathyscapes and sonar probes of Scottish lakes, Tacoma Narrows bridge collapse explained with abstract phase-space maps, some x-ray slides, a music score, Minard's Napoleonic war: the most exciting new frontier is charting what's already here.}}
Warning: this comic occasionally contains strong language (which may be unsuitable for children), unusual humor (which may be unsuitable for adults), and advanced mathematics (which may be unsuitable for liberal-arts majors).
+
BTC 1FhCLQK2ZXtCUQDtG98p6fVH7S6mxAsEey We did not invent the algorithm. The algorithm consistently finds Jesus. The algorithm killed Jeeves. The algorithm is banned in China. The algorithm is from Jersey. The algorithm constantly finds Jesus. This is not the algorithm. This is close.
+This means you're free to copy and share these comics (but not to sell them). More details.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SeleniumBase/examples/example_logs/screenshot.jpg b/SeleniumBase/examples/example_logs/screenshot.jpg
new file mode 100644
index 00000000..184469f7
Binary files /dev/null and b/SeleniumBase/examples/example_logs/screenshot.jpg differ
diff --git a/SeleniumBase/examples/gui_test_runner.py b/SeleniumBase/examples/gui_test_runner.py
new file mode 100755
index 00000000..4e2e13bf
--- /dev/null
+++ b/SeleniumBase/examples/gui_test_runner.py
@@ -0,0 +1,109 @@
+'''
+GUI TEST RUNNER
+Run by Typing: "python gui_test_runner.py"
+'''
+
+from Tkinter import Tk, Frame, Button, Label
+import os
+
+
+class App:
+
+ def __init__(self, master):
+ frame = Frame()
+ frame.pack()
+ root.title("Select Test Job To Run")
+ self.label = Label(root, width=40).pack()
+ self.title = Label(frame, text="", fg="black").pack()
+ self.title1 = Label(
+ frame, text="Basic Test Run in Chrome:", fg="blue").pack()
+ self.run1 = Button(
+ frame, command=self.run_1,
+ text=("nosetests my_first_test.py --with-selenium"
+ " --browser=chrome")).pack()
+ self.title2 = Label(
+ frame, text="Basic Test Run in Firefox:", fg="blue").pack()
+ self.run2 = Button(
+ frame, command=self.run_2,
+ text=("nosetests my_first_test.py"
+ " --with-selenium --browser=firefox")).pack()
+ self.title3 = Label(
+ frame, text="Basic Test Run in Demo Mode:", fg="blue").pack()
+ self.run3 = Button(
+ frame, command=self.run_3,
+ text=("nosetests my_first_test.py"
+ " --with-selenium --browser=chrome --demo_mode")).pack()
+ self.title4 = Label(
+ frame,
+ text="Basic Failing Test Run with Screenshots:",
+ fg="blue").pack()
+ self.run4 = Button(
+ frame, command=self.run_4,
+ text=("nosetests test_fail.py --with-selenium --browser=chrome"
+ " --with-testing_base --demo_mode")).pack()
+ self.title5 = Label(
+ frame,
+ text="Basic Failing Test Suite Run with Test Report:",
+ fg="blue").pack()
+ self.run5 = Button(
+ frame, command=self.run_5,
+ text=("nosetests my_test_suite.py --with-selenium"
+ " --browser=chrome --with-testing_base --report")).pack()
+ self.title6 = Label(
+ frame,
+ text="Basic Failing Test Run showing the Multiple-Checks feature:",
+ fg="blue").pack()
+ self.run6 = Button(
+ frame, command=self.run_6,
+ text=("nosetests non_terminating_checks_test.py"
+ " --browser=chrome --with-selenium")).pack()
+ self.title7 = Label(
+ frame,
+ text="MySQL DB Reporting Tests: (See ReadMe.md for Setup Steps!)",
+ fg="blue").pack()
+ self.run7 = Button(
+ frame, command=self.run_7,
+ text=("nosetests my_test_suite.py --with-selenium"
+ " --browser=chrome --with-db_reporting")).pack()
+ self.end_title = Label(frame, text="", fg="black").pack()
+ self.quit = Button(frame, text="QUIT", command=frame.quit).pack()
+
+ def run_1(self):
+ os.system(
+ 'nosetests my_first_test.py --with-selenium --browser=chrome')
+
+ def run_2(self):
+ os.system(
+ 'nosetests my_first_test.py --with-selenium --browser=firefox')
+
+ def run_3(self):
+ os.system(
+ 'nosetests my_first_test.py --with-selenium --demo_mode'
+ ' --browser=chrome')
+
+ def run_4(self):
+ os.system(
+ 'nosetests test_fail.py --with-selenium'
+ ' --browser=chrome --with-testing_base --demo_mode')
+
+ def run_5(self):
+ os.system(
+ 'nosetests my_test_suite.py --with-selenium'
+ ' --browser=chrome --with-testing_base --report')
+
+ def run_6(self):
+ os.system(
+ 'nosetests non_terminating_checks_test.py --with-selenium'
+ ' --browser=chrome')
+
+ def run_7(self):
+ os.system(
+ 'nosetests my_test_suite.py --with-selenium'
+ ' --browser=chrome --with-db_reporting')
+
+
+if __name__ == "__main__":
+ root = Tk()
+ root.minsize(612, 444)
+ app = App(root)
+ root.mainloop()
diff --git a/SeleniumBase/examples/masterqa_test.py b/SeleniumBase/examples/masterqa_test.py
new file mode 100755
index 00000000..a4a6b942
--- /dev/null
+++ b/SeleniumBase/examples/masterqa_test.py
@@ -0,0 +1,25 @@
+from seleniumbase import MasterQA
+
+
+class MasterQATests(MasterQA):
+
+ def test_xkcd(self):
+ self.open("http://xkcd.com/1512/")
+ for i in xrange(4):
+ self.click('a[rel="next"]')
+ for i in xrange(3):
+ self.click('a[rel="prev"]')
+ self.verify()
+ self.open("http://xkcd.com/1520/")
+ for i in xrange(2):
+ self.click('a[rel="next"]')
+ self.verify("Can you find the moon?")
+ self.click('a[rel="next"]')
+ self.verify("Do the drones look safe?")
+ self.click_link_text('Blag')
+ self.update_text("input#s", "Robots!\n")
+ self.verify("Does it say 'Hooray robots' on the page?")
+ self.open("http://xkcd.com/213/")
+ for i in xrange(5):
+ self.click('a[rel="prev"]')
+ self.verify("Does the page say 'Abnormal Expressions'?")
diff --git a/SeleniumBase/examples/my_first_test.py b/SeleniumBase/examples/my_first_test.py
new file mode 100755
index 00000000..4be67414
--- /dev/null
+++ b/SeleniumBase/examples/my_first_test.py
@@ -0,0 +1,99 @@
+from seleniumbase import BaseCase
+
+
+class MyTestClass(BaseCase):
+
+ def test_basic(self):
+ self.open('http://xkcd.com/353/') # Navigate to the web page
+ self.assert_element('img[alt="Python"]') # Assert element on page
+ self.click('a[rel="license"]') # Click element on page
+ self.assert_text('copy and reuse', 'div center') # Assert element text
+ self.open('http://xkcd.com/1481/')
+ image_object = self.find_element('#comic img') # Returns the element
+ caption = image_object.get_attribute('title') # Get element attribute
+ self.assertTrue('connections to the server' in caption)
+ self.click_link_text('Blag') # Click on link with the text
+ self.assert_text('xkcd', '#site-title')
+ header_text = self.get_text('header h2') # Grab text from page element
+ self.assertTrue('The blag of the webcomic' in header_text)
+ self.update_text('input#s', 'Robots!\n') # Fill in field with the text
+ self.assert_text('Hooray robots!', '#content')
+ self.open('http://xkcd.com/1319/')
+ self.assert_text('Automation', 'div#ctitle')
+
+ ####
+
+ #######################################################################
+ #
+ # **** NOTES / USEFUL INFO ****
+ #
+ # 1. By default, CSS Selectors are used to identify elements.
+ # You can use other identification options like PARTIAL_LINK_TEXT:
+ # [
+ # from selenium.webdriver.common.by import By
+ # ...
+ # self.click('Next', by=By.PARTIAL_LINK_TEXT)
+ # ]
+ # For the full list of `By` options, type ``dir(By)`` into a python
+ # command prompt after importing it (or in ipdb debugger mode). Ex:
+ # {
+ # >>> dir(By)
+ # ['CLASS_NAME', 'CSS_SELECTOR', 'ID', 'LINK_TEXT', 'NAME', ...
+ # }
+ # XPath is used by default if the arg starts with "/", "./", or "(":
+ # [
+ # self.click('/html/body/div[3]/div[4]/p[2]/a')
+ # ]
+ # But if you want XPath-clicking to be more clear in the code, use:
+ # [
+ # self.click_xpath('/html/body/div[3]/div[4]/p[2]/a')
+ # ]
+ #
+ # If you're completely new to CSS selectors, right-click on a
+ # web page and select "Inspect Element" to see the CSS in the html.
+ #
+ # 2. Most methods have the optional `timeout` argument. Ex:
+ # [
+ # self.get_text('center', timeout=15)
+ # ]
+ # The `timeout` argument tells the method how many seconds to wait
+ # for an element to appear before raising an exception. This is
+ # useful if a web page needs additional time to load an element.
+ # If you don't specify a `timeout`, a default timeout is used.
+ # Default timeouts are configured in seleniumbase/config/settings.py
+ #
+ # 3. There's usually more than one way to do the same thing. Ex:
+ # [
+ # header_text = self.get_text('header h2')
+ # self.assertTrue('The blag of the webcomic' in header_text)
+ # ]
+ # Can be simplified to:
+ # [
+ # self.assert_text('The blag of the webcomic', 'header_text')
+ # ]
+ #
+ # The following lines:
+ # [
+ # image_object = self.find_element('#comic img')
+ # caption = image_object.get_attribute('title')
+ # ]
+ # Can also be written as:
+ # [
+ # caption = self.get_attribute('#comic img', 'title')
+ # ]
+ #
+ # And the following line:
+ # [
+ # header_text = self.get_text('header h2')
+ # ]
+ # Can also be written as:
+ # [
+ # header_text = self.find_element('header h2').text
+ # ]
+ # ...and in many more ways!
+ #
+ # For backwards-compatibilty, some methods have multiple names.
+ # Ex: wait_for_element_visible() is the same as find_element().
+ # Both search for and return the element, and raise an exception if
+ # the element does not appear on the page within the timeout limit.
+ # (See seleniumbase/fixtures/base_case.py for the full method list.)
diff --git a/SeleniumBase/examples/my_test_suite.py b/SeleniumBase/examples/my_test_suite.py
new file mode 100755
index 00000000..be5e6df4
--- /dev/null
+++ b/SeleniumBase/examples/my_test_suite.py
@@ -0,0 +1,31 @@
+''' NOTE: This test suite contains 2 passing tests and 2 failing tests. '''
+
+from seleniumbase import BaseCase
+
+
+class MyTestSuite(BaseCase):
+
+ def test_1(self):
+ self.open("http://xkcd.com/1663/")
+ self.find_text("Garden", "div#ctitle", timeout=3)
+ for p in xrange(4):
+ self.click('a[rel="next"]')
+ self.find_text("Algorithms", "div#ctitle", timeout=3)
+
+ def test_2(self):
+ # This test should FAIL
+ print("\n(This test fails on purpose)")
+ self.open("http://xkcd.com/1675/")
+ raise Exception("FAKE EXCEPTION: This test fails on purpose.")
+
+ def test_3(self):
+ self.open("http://xkcd.com/1406/")
+ self.find_text("Universal Converter Box", "div#ctitle", timeout=3)
+ self.open("http://xkcd.com/608/")
+ self.find_text("Form", "div#ctitle", timeout=3)
+
+ def test_4(self):
+ # This test should FAIL
+ print("\n(This test fails on purpose)")
+ self.open("http://xkcd.com/1670/")
+ self.find_element("FakeElement.DoesNotExist", timeout=0.5)
diff --git a/SeleniumBase/examples/non_terminating_checks_test.py b/SeleniumBase/examples/non_terminating_checks_test.py
new file mode 100755
index 00000000..58ab8556
--- /dev/null
+++ b/SeleniumBase/examples/non_terminating_checks_test.py
@@ -0,0 +1,15 @@
+from seleniumbase import BaseCase
+
+
+class MyTestClass(BaseCase):
+
+ def test_non_terminating_checks(self):
+ self.open('http://xkcd.com/993/')
+ self.wait_for_element('#comic')
+ self.check_assert_element('img[alt="Brand Identity"]')
+ self.check_assert_element('img[alt="Rocket Ship"]') # Will Fail
+ self.check_assert_element('#comicmap')
+ self.check_assert_text('Fake Item', '#middleContainer') # Will Fail
+ self.check_assert_text('Random', '#middleContainer')
+ self.check_assert_element('a[name="Super Fake !!!"]') # Will Fail
+ self.process_checks()
diff --git a/SeleniumBase/examples/rate_limiting_test.py b/SeleniumBase/examples/rate_limiting_test.py
new file mode 100755
index 00000000..e803de4a
--- /dev/null
+++ b/SeleniumBase/examples/rate_limiting_test.py
@@ -0,0 +1,19 @@
+"""
+This test demonstrates the use of the "rate_limited" decorator.
+You can use this decorator on any method to rate-limit it.
+"""
+
+import unittest
+from seleniumbase.common import decorators
+
+
+class MyTestClass(unittest.TestCase):
+
+ @decorators.rate_limited(3.5) # The arg is max calls per second
+ def print_item(self, item):
+ print(item)
+
+ def test_rate_limited_printing(self):
+ print("\nRunning rate-limited print test:")
+ for item in xrange(1, 11):
+ self.print_item(item)
diff --git a/SeleniumBase/examples/run_my_first_test_in_chrome.sh b/SeleniumBase/examples/run_my_first_test_in_chrome.sh
new file mode 100755
index 00000000..dfd41c2c
--- /dev/null
+++ b/SeleniumBase/examples/run_my_first_test_in_chrome.sh
@@ -0,0 +1 @@
+nosetests my_first_test.py --browser=chrome --with-selenium --logging-level=INFO -s
\ No newline at end of file
diff --git a/SeleniumBase/examples/run_my_first_test_in_firefox.sh b/SeleniumBase/examples/run_my_first_test_in_firefox.sh
new file mode 100755
index 00000000..a8598246
--- /dev/null
+++ b/SeleniumBase/examples/run_my_first_test_in_firefox.sh
@@ -0,0 +1 @@
+nosetests my_first_test.py --browser=firefox --with-selenium --logging-level=INFO -s
\ No newline at end of file
diff --git a/SeleniumBase/examples/run_rate_limiting_test.sh b/SeleniumBase/examples/run_rate_limiting_test.sh
new file mode 100755
index 00000000..bb8fb46a
--- /dev/null
+++ b/SeleniumBase/examples/run_rate_limiting_test.sh
@@ -0,0 +1 @@
+nosetests rate_limiting_test.py -s
\ No newline at end of file
diff --git a/SeleniumBase/examples/run_test_fail_with_logging.sh b/SeleniumBase/examples/run_test_fail_with_logging.sh
new file mode 100755
index 00000000..592e9037
--- /dev/null
+++ b/SeleniumBase/examples/run_test_fail_with_logging.sh
@@ -0,0 +1 @@
+nosetests test_fail.py --with-selenium --with-testing_base --with-basic_test_info --with-page_source --with-screen_shots
\ No newline at end of file
diff --git a/SeleniumBase/examples/setup.cfg b/SeleniumBase/examples/setup.cfg
new file mode 100755
index 00000000..061dbdd1
--- /dev/null
+++ b/SeleniumBase/examples/setup.cfg
@@ -0,0 +1,6 @@
+[nosetests]
+
+; This is the config file for default values used during nosetest runs
+
+nocapture=1 ; Displays print statements from output. Undo this by using: --nologcapture
+logging-level=INFO ; INFO keeps the logs much cleaner than using DEBUG
diff --git a/SeleniumBase/examples/test_fail.py b/SeleniumBase/examples/test_fail.py
new file mode 100755
index 00000000..90cfc60e
--- /dev/null
+++ b/SeleniumBase/examples/test_fail.py
@@ -0,0 +1,11 @@
+""" This test was made to fail on purpose to demonstrate the
+ logging capabilities of the SeleniumBase Test Framework """
+
+from seleniumbase import BaseCase
+
+
+class MyTestClass(BaseCase):
+
+ def test_find_army_of_robots_on_xkcd_desert_island(self):
+ self.open("http://xkcd.com/731/")
+ self.assert_element("div#ARMY_OF_ROBOTS", timeout=0.7)
diff --git a/SeleniumBase/help_docs/ReadMe.md b/SeleniumBase/help_docs/ReadMe.md
new file mode 100755
index 00000000..7d806fb4
--- /dev/null
+++ b/SeleniumBase/help_docs/ReadMe.md
@@ -0,0 +1,3 @@
+## Additional Help Documents
+
+This folder contains additional documents to help guide you with requirements installation, setup, and more.
diff --git a/SeleniumBase/help_docs/happy_customers.md b/SeleniumBase/help_docs/happy_customers.md
new file mode 100755
index 00000000..9f73443b
--- /dev/null
+++ b/SeleniumBase/help_docs/happy_customers.md
@@ -0,0 +1,19 @@
+#### Some organizations that've used SeleniumBase include:
+* [HubSpot](http://www.hubspot.com/)
+* [Jana](http://jana.com/)
+* [Veracode](http://www.veracode.com/)
+* [CA Technologies](https://www.ca.com/)
+* [MIT](http://web.mit.edu/)
+* [Akamai](https://www.akamai.com/)
+* [VMware](http://www.vmware.com/)
+* [Raid The Room](http://raidtheroom.com/)
+* [Harvard Medical School](http://hms.harvard.edu/)
+* And more...
+
+**Case Study** (*HubSpot*):
+
+HubSpot saved over one million U.S. dollars by using SeleniumBase to automate the migration of website pages from one content management system to another.
+
+Learn how HubSpot uses SeleniumBase for website testing by reading: [Automated Testing with Selenium](http://dev.hubspot.com/blog/bid/88880/Automated-Integration-Testing-with-Selenium-at-HubSpot)
+
+For more reading about automation at HubSpot, see: [The Classic "QA Team" is Obsolete](http://product.hubspot.com/blog/the-classic-qa-team-is-obsolete)
diff --git a/SeleniumBase/help_docs/hidden_files_info.md b/SeleniumBase/help_docs/hidden_files_info.md
new file mode 100755
index 00000000..dd2d35be
--- /dev/null
+++ b/SeleniumBase/help_docs/hidden_files_info.md
@@ -0,0 +1,6 @@
+### Info about hidden files on a Mac
+
+Depending on your Mac settings, some files may be hidden from view in your Finder window, such as ``.gitignore``. To view all files, run the following command and then reopen the Finder window:
+```bash
+defaults write com.apple.finder AppleShowAllFiles -bool true
+```
diff --git a/SeleniumBase/help_docs/method_summary.md b/SeleniumBase/help_docs/method_summary.md
new file mode 100755
index 00000000..6bc89592
--- /dev/null
+++ b/SeleniumBase/help_docs/method_summary.md
@@ -0,0 +1,219 @@
+### SeleniumBase method summary
+
+Here's a summary of SeleniumBase method definitions, which are defined in [base_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py)
+
+In order to maintain backwards compatibility with scripts using earlier verions of SeleniumBase, some methods that had their names shortened can also be called by their original method name. *(Ex: wait_for_element_visible was later shortened to wait_for_element and then to find_element, but the longer method names remained to keep older scripts from failing.)*
+
+```python
+self.open(url)
+
+self.open_url(url)
+
+self.click(selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.double_click(selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.click_chain(selectors_list, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT, spacing=0)
+
+self.click_link_text(link_text, timeout=settings.SMALL_TIMEOUT)
+
+self.click_partial_link_text(partial_link_text, timeout=settings.SMALL_TIMEOUT)
+
+self.get_text(selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.get_attribute(selector, attribute, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+self.refresh_page()
+
+self.get_current_url()
+
+self.get_page_source()
+
+self.get_page_title()
+
+self.go_back()
+
+self.go_forward()
+
+self.get_image_url(selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.add_text(selector, new_value, timeout=settings.SMALL_TIMEOUT)
+
+self.send_keys(selector, new_value, timeout=settings.SMALL_TIMEOUT)
+
+self.update_text_value(selector, new_value,
+ timeout=settings.SMALL_TIMEOUT, retry=False)
+
+self.update_text(selector, new_value, timeout=settings.SMALL_TIMEOUT,
+ retry=False)
+
+self.is_element_present(selector, by=By.CSS_SELECTOR)
+
+self.is_element_visible(selector, by=By.CSS_SELECTOR)
+
+self.is_link_text_visible(link_text)
+
+self.is_partial_link_text_visible(partial_link_text)
+
+self.is_text_visible(text, selector, by=By.CSS_SELECTOR)
+
+self.find_visible_elements(selector, by=By.CSS_SELECTOR)
+
+self.execute_script(script)
+
+self.set_window_size(width, height)
+
+self.maximize_window()
+
+self.activate_jquery()
+
+self.highlight(selector, by=By.CSS_SELECTOR, loops=4, scroll=True)
+
+self.scroll_to(selector, by=By.CSS_SELECTOR)
+
+self.slow_scroll_to(selector, by=By.CSS_SELECTOR)
+
+self.scroll_click(selector, by=By.CSS_SELECTOR)
+
+self.click_xpath(xpath)
+
+self.jquery_click(selector, by=By.CSS_SELECTOR)
+
+self.jq_format(code)
+
+self.get_domain_url(url)
+
+self.download_file(file_url, destination_folder=None)
+
+self.save_file_as(file_url, new_file_name, destination_folder=None)
+
+self.convert_xpath_to_css(xpath)
+
+self.convert_to_css_selector(selector, by)
+
+self.set_value(selector, new_value, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+self.jquery_update_text_value(selector, new_value, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+self.jquery_update_text(selector, new_value, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+self.hover_on_element(selector)
+
+self.hover_and_click(hover_selector, click_selector,
+ hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+self.pick_select_option_by_text(dropdown_selector, option,
+ dropdown_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.pick_select_option_by_index(dropdown_selector, option,
+ dropdown_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+self.pick_select_option_by_value(dropdown_selector, option,
+ dropdown_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_element_present(selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.assert_element_present(selector, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_element_visible(selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.wait_for_element(selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.find_element(selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT)
+
+self.assert_element(
+ selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_text_visible(text, selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.wait_for_text(text, selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.find_text(text, selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.assert_text(text, selector, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_link_text_visible(link_text, timeout=settings.LARGE_TIMEOUT)
+
+self.wait_for_link_text(link_text, timeout=settings.LARGE_TIMEOUT)
+
+self.find_link_text(link_text, timeout=settings.LARGE_TIMEOUT)
+
+self.assert_link_text(link_text, timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_partial_link_text(partial_link_text,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.find_partial_link_text(partial_link_text,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.assert_partial_link_text(partial_link_text,
+ timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_element_absent(selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.assert_element_absent(selector, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_element_not_visible(selector, by=By.CSS_SELECTOR,
+ timeout=settings.LARGE_TIMEOUT)
+
+self.assert_element_not_visible(selector, by=By.CSS_SELECTOR,
+ timeout=settings.SMALL_TIMEOUT)
+
+########
+
+self.wait_for_ready_state_complete(timeout=settings.EXTREME_TIMEOUT)
+
+self.wait_for_and_accept_alert(timeout=settings.LARGE_TIMEOUT)
+
+self.wait_for_and_dismiss_alert(timeout=settings.LARGE_TIMEOUT)
+
+self.wait_for_and_switch_to_alert(timeout=settings.LARGE_TIMEOUT)
+
+self.switch_to_frame(frame, timeout=settings.SMALL_TIMEOUT)
+
+self.switch_to_window(window, timeout=settings.SMALL_TIMEOUT)
+
+self.switch_to_default_content()
+
+self.save_screenshot(name, folder=None)
+
+########
+
+self.check_assert_element(selector, by=By.CSS_SELECTOR,
+ timeout=settings.TINY_TIMEOUT)
+
+self.check_assert_text(text, selector, by=By.CSS_SELECTOR,
+ timeout=settings.TINY_TIMEOUT)
+
+self.process_checks()
+```
\ No newline at end of file
diff --git a/SeleniumBase/help_docs/mysql_installation.md b/SeleniumBase/help_docs/mysql_installation.md
new file mode 100755
index 00000000..6b96b7b2
--- /dev/null
+++ b/SeleniumBase/help_docs/mysql_installation.md
@@ -0,0 +1,54 @@
+### MySQL Installation Instructions
+
+
+#### [MySQL](http://www.mysql.com/) (OPTIONAL)
+
+(NOTE: If you're using this test framework from a local development machine and don't plan on writing to a MySQL DB from your local test runs, you can skip this step.)
+
+Mac:
+```bash
+brew install MySQL
+```
+
+Windows:
+[Download MySQL here](http://dev.mysql.com/downloads/windows/)
+
+That installs the MySQL library so that you can use database commands in your code. To make that useful, you'll want to have a MySQL DB that you can connect to.
+
+#### Access your MySQL DB
+
+If you want a visual tool to help make your MySQL life easier, [try MySQL Workbench](http://dev.mysql.com/downloads/workbench/).
+
+#### Prepare your MySQL DB
+
+You can use the [testcaserepository.sql](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/testcaserepository.sql) file to create the necessary tables for storing test data.
+
+If you were able to successfully install MySQL, you can now install the remaining MySQL requirements:
+```bash
+pip install -r server_requirements.txt
+```
+(NOTE: This install uses Selenium 2.53.6 rather than the usual Selenium 3+ from the standard requirements file due to compatibility issues with running browser tests on headless server machines.)
+
+#### Configure your MySQL DB for SeleniumBase
+
+You'll want to update your [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) file with your MySQL DB credentials so that tests can write to the database when they run.
+
+#### Allow tests to write to your MySQL database
+
+Add the ``--with-db_reporting`` argument on the command line when you want tests to write to your MySQL database.
+Example:
+```bash
+nosetests my_first_test.py --with-selenium --with-db_reporting
+```
+
+#### Windows mysql-python troubleshooting:
+
+If you're having trouble with Windows mysql-python installation using pip, you can also try the following steps to install from an alternative source:
+
+* Download the unofficial ``.whl`` format of MySQL-Python and Mysqlclient from [here](http://www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python).
+
+* Open a console and then cd to where you've downloaded the MySQL-Python .whl file.
+
+* Run the command ``pip install FILENAME.whl``
+
+* If pip.exe is not recognized, you may find it in the "Scripts" directory from where python has been installed.
\ No newline at end of file
diff --git a/SeleniumBase/help_docs/requirements_installation.md b/SeleniumBase/help_docs/requirements_installation.md
new file mode 100755
index 00000000..b9e3c291
--- /dev/null
+++ b/SeleniumBase/help_docs/requirements_installation.md
@@ -0,0 +1,70 @@
+## Installation instructions for Python, pip, brew, git, virtualenv, and virtualenvwrapper
+
+
+### [Python 2.7](https://www.python.org/downloads/)
+
+If you're a MAC user, that should already come preinstalled on your machine. Although Python 3 exists, you'll want Python 2 instead.
+
+If you're a WINDOWS user, [download Python 2.7 from here](https://www.python.org/downloads/release/python-2713/).
+
+
+### [Pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29)
+
+You might already have pip installed, but if you don't:
+
+On a MAC, run the following command:
+```bash
+sudo easy_install pip
+```
+
+If you're not using the latest version of pip & setuptools, you'll need to upgrade:
+```bash
+pip install -U pip setuptools
+```
+
+On WINDOWS, run the following command:
+```bash
+python -m pip install -U pip setuptools
+```
+
+If you're having any trouble with that, you can [GET PIP HERE](https://pip.pypa.io/en/latest/installing/).
+
+When done, make sure pip is on your path. ($PATH on Mac/Linux. System Environment Variables on WINDOWS.)
+
+
+### [Homebrew](http://brew.sh/) (MAC-ONLY) (OPTIONAL)
+
+Homebrew allows you to install things more easily, such as Git, Chromedriver, and PhantomJS.
+
+```bash
+ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+brew update
+```
+
+### [Git](http://www.git-scm.com)
+
+(NOTE: You can download the SeleniumBase repository right from GitHub and skip all the git-related commands. That's probably the fastest way if you want to quickly get a live demo of this tool up and running.)
+
+MAC-ONLY: (This step only works if you installed Homebrew in the previous step)
+```bash
+brew install git
+```
+
+(WINDOWS users: Skip the Homebrew part and [download Git here](http://git-scm.com/downloads).)
+
+
+### [VirtualEnv](http://virtualenv.readthedocs.org/en/latest/) and [VirtualEnvWrapper](http://virtualenvwrapper.readthedocs.org/en/latest/)
+
+(NOTE: Virtual environments allow each your Python projects to have a unique set of packaged dependencies.)
+
+MAC:
+```bash
+sudo easy_install --upgrade virtualenv
+sudo easy_install --upgrade virtualenvwrapper
+```
+
+WINDOWS:
+```bash
+pip install --upgrade virtualenv
+pip install --upgrade virtualenvwrapper-win
+```
diff --git a/SeleniumBase/help_docs/using_safari_driver.md b/SeleniumBase/help_docs/using_safari_driver.md
new file mode 100755
index 00000000..db0d8831
--- /dev/null
+++ b/SeleniumBase/help_docs/using_safari_driver.md
@@ -0,0 +1,18 @@
+### Info about using Safari Driver for running automated tests on a MAC
+
+(NOTE: SafariDriver requires Safari 10 running on OSX El Capitan or greater)
+
+You can find a nice overview on using Safari Driver [here on GitHub](https://github.com/SeleniumHQ/selenium/wiki/SafariDriver).
+
+That above link will tell you to [download the required Safari Driver browser extension (SafariDriver.safariextz) here at this link](http://selenium-release.storage.googleapis.com/index.html?path=2.48/).
+
+For that to work, you'll need to [download the Standalone Selenium Server from here](http://docs.seleniumhq.org/download/) and put that JAR file in ``/usr/local/bin/``. To make the next step easier, rename the downloaded JAR file to ``selenium-server-standalone.jar`` (if it's not already called that).
+
+Next, configure the Selenium Server JAR file into your PATH like this:
+
+```bash
+export SELENIUM_SERVER_JAR=/usr/local/bin/selenium-server-standalone.jar
+export PATH=$PATH:/usr/local/bin/selenium-server-standalone.jar
+```
+
+Now you're ready to run automated tests on Safari if you use ``--browser=safari`` on the command line when running your tests/scripts with SeleniumBase.
diff --git a/SeleniumBase/help_docs/verify_webdriver.md b/SeleniumBase/help_docs/verify_webdriver.md
new file mode 100755
index 00000000..7cb53e6b
--- /dev/null
+++ b/SeleniumBase/help_docs/verify_webdriver.md
@@ -0,0 +1,23 @@
+### Verify that web drivers were successfully installed
+
+*You can do this by checking inside a Python command prompt. (NOTE: xkcd is a webcomic)*
+
+#### Verifying ChromeDriver
+```bash
+python
+>>> from selenium import webdriver
+>>> browser = webdriver.Chrome()
+>>> browser.get("http://xkcd.com/1337/")
+>>> browser.close()
+>>> exit()
+```
+
+#### Verifying FirefoxDriver (Geckodriver)
+```bash
+python
+>>> from selenium import webdriver
+>>> browser = webdriver.Firefox()
+>>> browser.get("http://xkcd.com/1337/")
+>>> browser.close()
+>>> exit()
+```
diff --git a/SeleniumBase/help_docs/virtualenv_instructions.md b/SeleniumBase/help_docs/virtualenv_instructions.md
new file mode 100755
index 00000000..28ac944c
--- /dev/null
+++ b/SeleniumBase/help_docs/virtualenv_instructions.md
@@ -0,0 +1,43 @@
+### Virtual Environment Setup Instructions
+
+* If you haven't yet installed ``virtualenv`` or ``virtualenvwrapper``, **[follow these instructions first](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/requirements_installation.md#virtual_environment)**.
+
+MAC:
+
+(If using ``virtualenv``):
+
+```bash
+mkdir -p ~/Envs
+virtualenv ~/Envs/seleniumbase
+source ~/Envs/seleniumbase/bin/activate
+```
+
+(If using ``virtualenvwrapper``):
+
+```bash
+mkvirtualenv seleniumbase
+```
+
+WINDOWS:
+
+```bash
+mkvirtualenv seleniumbase
+```
+
+If you ever need to leave your virtual environment, use the following command:
+
+```bash
+deactivate
+```
+
+You can always jump back into your virtual environment later:
+
+(If using ``virtualenv``):
+```bash
+source ~/Envs/seleniumbase/bin/activate
+```
+
+(If using ``virtualenvwrapper``):
+```bash
+workon seleniumbase
+```
diff --git a/SeleniumBase/integrations/docker/ReadMe.md b/SeleniumBase/integrations/docker/ReadMe.md
new file mode 100755
index 00000000..005681b3
--- /dev/null
+++ b/SeleniumBase/integrations/docker/ReadMe.md
@@ -0,0 +1,79 @@
+## Docker setup instructions for SeleniumBase
+
+#### 1. Install the Docker Toolbox:
+
+You can get that from here:
+https://www.docker.com/products/docker-toolbox
+
+You might also want to install the Docker Engine:
+https://docs.docker.com/engine/installation/
+
+#### 2. Create your SeleniumBase Docker environment:
+
+ docker-machine create --driver virtualbox seleniumbase
+
+##### (If your Docker environment ever goes down for any reason, you can bring it back up with a restart.)
+
+ docker-machine restart seleniumbase
+
+#### 3. Configure your shell:
+
+ eval "$(docker-machine env seleniumbase)"
+
+#### 4. Go to the SeleniumBase home directory on the command line, which is where [Dockerfile](https://github.com/seleniumbase/SeleniumBase/blob/master/Dockerfile) is located. (This assumes you've already cloned the SeleniumBase repo.)
+
+#### 5. Create your Docker image from your Dockerfile: (Get ready to wait awhile)
+
+ docker build -t seleniumbase .
+
+#### 6. Run [the example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) inside your Docker: (Once the test completes after a few seconds, you'll automatically exit the Docker shell)
+
+ docker run seleniumbase ./run_docker_test_in_firefox.sh
+
+#### 7. Now run the same test with Chrome inside your Docker:
+
+ docker run seleniumbase ./run_docker_test_in_chrome.sh
+
+#### 8. Now run the same test with PhantomJS inside your Docker:
+
+ docker run seleniumbase ./run_docker_test_in_phantomjs.sh
+
+#### 9. You can also enter Docker and stay inside the shell:
+
+ docker run -i -t seleniumbase
+
+#### 10. Now you can run the example test from inside the Docker shell:
+
+ ./run_docker_test_in_chrome.sh
+
+#### 11. When you're satisfied, you may exit the Docker shell:
+
+ exit
+
+#### 12. (Optional) Since Docker images and containers take up a lot of space, you may want to clean up your machine from time to time when they’re not being used:
+
+Details on that can be found here:
+http://stackoverflow.com/questions/17236796/how-to-remove-old-docker-containers
+
+Here are a few of those cleanup commands:
+
+ docker images | grep "" | awk '{print $3}' | xargs docker rmi
+ docker rm 'docker ps --no-trunc -aq'
+
+If you want to completely remove all of your Docker containers and images, use these commands: (If there's nothing to delete, those commands will return an error.)
+
+ docker rm -f $(docker ps -a -q)
+ docker rmi -f $(docker images -q)
+
+Finally, if you want to wipe out your SeleniumBase Docker virtualbox, use these commands:
+
+ docker-machine kill seleniumbase
+ docker-machine rm seleniumbase
+
+For more cleanup commands, check out:
+https://codefresh.io/blog/everyday-hacks-docker/
+
+#### 13. (Optional) More reading on Docker can be found here:
+* https://docs.docker.com
+* https://docs.docker.com/mac/started/
+* https://docs.docker.com/installation/mac/
diff --git a/SeleniumBase/integrations/docker/docker-entrypoint.sh b/SeleniumBase/integrations/docker/docker-entrypoint.sh
new file mode 100755
index 00000000..b4756f92
--- /dev/null
+++ b/SeleniumBase/integrations/docker/docker-entrypoint.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -e
+echo "***** SeleniumBase Docker Machine *****"
+exec "$@"
diff --git a/SeleniumBase/integrations/docker/docker_config.cfg b/SeleniumBase/integrations/docker/docker_config.cfg
new file mode 100755
index 00000000..728c0821
--- /dev/null
+++ b/SeleniumBase/integrations/docker/docker_config.cfg
@@ -0,0 +1,6 @@
+[nosetests]
+with-selenium=1
+with-testing_base=1
+with-basic_test_info=1
+nocapture=1
+logging-level=INFO
diff --git a/SeleniumBase/integrations/docker/run_docker_test_in_chrome.sh b/SeleniumBase/integrations/docker/run_docker_test_in_chrome.sh
new file mode 100755
index 00000000..b070cb46
--- /dev/null
+++ b/SeleniumBase/integrations/docker/run_docker_test_in_chrome.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+# Run example test from inside Docker image
+echo "Running example SeleniumBase test from Docker with headless Chrome..."
+cd /SeleniumBase/examples/ && nosetests my_first_test.py --config=docker_config.cfg --browser=chrome --headless
+exec "$@"
diff --git a/SeleniumBase/integrations/docker/run_docker_test_in_firefox.sh b/SeleniumBase/integrations/docker/run_docker_test_in_firefox.sh
new file mode 100755
index 00000000..cb692185
--- /dev/null
+++ b/SeleniumBase/integrations/docker/run_docker_test_in_firefox.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+# Run example test from inside Docker image
+echo "Running example SeleniumBase test from Docker with headless Firefox..."
+cd /SeleniumBase/examples/ && nosetests my_first_test.py --config=docker_config.cfg --browser=firefox --headless
+exec "$@"
diff --git a/SeleniumBase/integrations/docker/run_docker_test_in_phantomjs.sh b/SeleniumBase/integrations/docker/run_docker_test_in_phantomjs.sh
new file mode 100755
index 00000000..7e903e86
--- /dev/null
+++ b/SeleniumBase/integrations/docker/run_docker_test_in_phantomjs.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+# Run example test from inside Docker image
+echo "Running example SeleniumBase test from Docker with PhantomJS..."
+cd /SeleniumBase/examples/ && nosetests my_first_test.py --config=docker_config.cfg --browser=phantomjs
+exec "$@"
diff --git a/SeleniumBase/integrations/google_cloud/ReadMe.md b/SeleniumBase/integrations/google_cloud/ReadMe.md
new file mode 100755
index 00000000..86c803dc
--- /dev/null
+++ b/SeleniumBase/integrations/google_cloud/ReadMe.md
@@ -0,0 +1,197 @@
+### Building a browser-based test automation server on the [Google Cloud Platform](https://cloud.google.com/) by using [SeleniumBase](https://github.com/seleniumbase/SeleniumBase)
+
+(This tutorial, [from a previous GCP Meetup live demo](http://www.meetup.com/Boston-Google-Cloud-Meetup/events/230839686/?showDescription=true), will teach you how to setup a Linux server for running automated browser tests. The cost of running this server is [$14.20/month on Google Cloud](https://console.cloud.google.com/launcher/details/bitnami-launchpad/jenkins) (enough to handle **6 parallel tests**). This is amazingly **20 times LESS expensive** than using competitors such as [BrowserStack](https://www.browserstack.com/pricing) or [Sauce Labs](https://saucelabs.com/pricing) **for the SAME number of parallel tests**.)
+
+#### Step 1. Open the Google Cloud Platform Cloud Launcher
+
+* Navigate to [https://console.cloud.google.com/launcher](https://console.cloud.google.com/launcher)
+* (If you already have an active Google Cloud project, the Google Cloud Launcher will probably default to using that. If you don't, [sign up for the free trial of Google Cloud Platform here](https://console.cloud.google.com/freetrial) to get started.)
+
+#### Step 2. Launch a Jenkins instance
+
+
+
+* Under "Cloud Launcher", Click on "Jenkins"
+* Click on "Launch on Compute Engine"
+* Give the instance a name
+* Give the instance a zone
+* Click "Create"
+
+#### Step 3. Connect with your new Jenkins instance
+
+
+
+* SSH into your new instance by selecting: "SSH" => "Open in browser window" from the instance page.
+
+#### Step 4. Clone the SeleniumBase repository from the root ("/") directory.
+
+```bash
+cd /
+sudo git clone https://github.com/seleniumbase/SeleniumBase.git
+```
+
+#### Step 5. Enter the "linux" folder
+
+```bash
+cd SeleniumBase/integrations/linux/
+```
+
+#### Step 6. Give Jenkins (aka "tomcat" user) sudo access (See [jenkins_permissions.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/jenkins_permissions.sh) for details)
+
+```bash
+./jenkins_permissions.sh
+```
+
+#### Step 7. Become "tomcat" (the Jenkins user) and enter a "bash" shell
+
+```bash
+sudo su tomcat
+bash
+```
+
+#### Step 8. Install dependencies (See [Linuxfile.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Linuxfile.sh) for details)
+
+```bash
+./Linuxfile.sh
+```
+
+#### Step 9. Start up the headless browser display mechanism: Xvfb (See [Xvfb_launcher.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Xvfb_launcher.sh) for details)
+
+```bash
+./Xvfb_launcher.sh
+```
+
+#### Step 10. Go to the SeleniumBase directory
+
+```bash
+cd /SeleniumBase
+```
+
+#### Step 11. Install the [requirements](https://github.com/seleniumbase/SeleniumBase/blob/master/server_requirements.txt) for SeleniumBase
+
+```bash
+sudo pip install -r server_requirements.txt --upgrade
+```
+
+#### Step 12. Install SeleniumBase (Make sure you already installed the requirements above)
+
+```bash
+sudo python server_setup.py install
+```
+
+#### Step 13. Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) in Chrome to verify installation (Takes ~10 seconds)
+
+
+
+```bash
+py.test examples/my_first_test.py --with-selenium --headless --browser=chrome
+```
+
+#### Step 14. If you like nosetests better than pytest, that works too
+
+```bash
+nosetests examples/my_first_test.py --with-selenium --headless --browser=chrome
+```
+
+#### Step 15. You can also verify that the example test runs on Firefox
+
+```bash
+nosetests examples/my_first_test.py --with-selenium --headless --browser=firefox
+```
+
+#### Step 16. Login to Jenkins
+
+* (The url, as well as username and password, should be accessible from your Google Cloud Platform VM instance page.)
+
+#### Step 17. Create a new Jenkins job
+
+
+
+* Click on "New Item"
+* Give your new Jenkins job a name (ex: "My_First_Test")
+* Select "Freestyle project"
+* Click "OK"
+
+#### Step 18. Setup your new Jenkins job
+
+* Under "Source Code Management", select "Git".
+* For the "Repository URL", put: ``https://github.com/seleniumbase/SeleniumBase.git``. (You'll eventually be using your own clone of the repository here.)
+* Under "Build", click the "Add build step" dropdown and then select "Execute shell".
+* For the "Command", put:
+```bash
+nosetests examples/my_first_test.py --with-selenium --headless --browser=chrome
+```
+* Click "Save" when you're done.
+
+#### Step 19. Run your new Jenkins job
+
+* Click on "Build Now"
+* (If all the setup was done correctly, you should see a blue dot appear after a few seconds, indicating that the test job passed.)
+
+#### Step 20. Future Work
+
+If you have a web application that you want to test, you'll be able to create SeleniumBase tests and add them to Jenkins as you saw here. You may want to create a Deploy job, which downloads the latest version of your repository, and then kicks off all tests to run after that. You could then tell that Deploy job to auto-run whenever a change is pushed to your repository by using: "Poll SCM". All your tests would then be able to run by using: "Build after other projects are built". You can also use MySQL to save test results in the DB so that you can query the data at any time.
+
+#### Congratulations! You're now well on your way to becoming a build & release / automation engineer!
+
+## MySQL DB setup instructions
+
+#### Step 21. Return to the Google Cloud Launcher and launch a MySQL Instance
+
+
+
+* Under "Featured Solutions", Click on "MySQL"
+* Click on "Launch on Compute Engine"
+* Give the instance a name
+* Give the instance a zone
+* Click "Create"
+
+#### Step 22. Get the Connection credentials for your new MySQL DB
+
+* Under the Google Cloud Platform menu, go to "Compute Engine"
+* Find your new MySQL instance and then write down the value written in the "External IP" section.
+* Under the Google Cloud Platform menu, go to "Deployment Manager"
+* Find your new MySQL instance and then click on it.
+* Write down the values for Admin username and password. (Username should be "root")
+
+#### Step 23. Get a MySQL GUI tool so that you can connect to your MySQL DB
+
+* You can download [MySQL Workbench](http://dev.mysql.com/downloads/tools/workbench/) for this.
+
+#### Step 24. Create a new connection to your MySQL DB
+
+* Use the MySQL DB credentials that you saved in Step 21 for this.
+
+#### Step 25. Create a new schema in your MySQL DB
+
+* You can name your schema ``test``.
+
+#### Step 26. Create the necessary tables in your MySQL schema
+
+* Run a SQL script in your MySQL schema using [testcaserepository.sql](https://raw.githubusercontent.com/seleniumbase/SeleniumBase/master/seleniumbase/core/testcaserepository.sql)
+
+#### Step 27. Have your local clone of SeleniumBase connect to your MySQL DB
+
+* Update the MySQL connection details in your [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) file to use the credentials that you saved in Step 21.
+* Run the following command again from the top-level SeleniumBase folder to make sure that SeleniumBase uses the updated credentials:
+
+```bash
+sudo python setup.py install
+```
+
+#### Step 28. Have your SeleniumBase Jenkins jobs use your MySQL DB
+
+* For the "Execute shell", use the following as your updated "Command":
+
+```bash
+nosetests examples/my_test_suite.py --with-selenium --headless --browser=chrome --with-db_reporting --with-testing_base
+```
+
+* Click "Save" when you're done.
+
+#### Step 29. Run your new Jenkins job
+
+* Click on "Build Now"
+* If all goes well, you should be seeing new rows appear in your MySQL DB.
+
+#### Step 30. Congratulations! If you made it this far, you're awesome!
diff --git a/SeleniumBase/integrations/linux/Linuxfile.sh b/SeleniumBase/integrations/linux/Linuxfile.sh
new file mode 100755
index 00000000..9c2a501d
--- /dev/null
+++ b/SeleniumBase/integrations/linux/Linuxfile.sh
@@ -0,0 +1,92 @@
+# SeleniumBase Debian Linux Dependency Installation
+# (Installs all required dependencies on Linux)
+
+# Make sure this script is only run on Linux
+value="$(uname)"
+if [ $value == "Linux" ]
+then
+ echo "Initializing Requirements Setup..."
+else
+ echo "Not on a Linux machine. Exiting..."
+ exit
+fi
+
+# Go home
+cd ~
+
+# Configure apt-get resources
+sudo sh -c "echo \"deb http://packages.linuxmint.com debian import\" >> /etc/apt/sources.list"
+sudo sh -c "echo \"deb http://downloads.sourceforge.net/project/ubuntuzilla/mozilla/apt all main\" >> /etc/apt/sources.list"
+
+# Update aptitude
+sudo aptitude update
+
+# Install core dependencies
+sudo aptitude install -y --force-yes xserver-xorg-core
+sudo aptitude install -y --force-yes x11-xkb-utils
+
+# Install Xvfb (headless display system)
+sudo aptitude install -y --force-yes xvfb
+
+# Install fonts for web browsers
+sudo aptitude install -y --force-yes xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic
+
+# Install Python core dependencies
+sudo apt-get update
+sudo apt-get install -y --force-yes python-setuptools
+
+# Install Firefox
+sudo gpg --keyserver pgp.mit.edu --recv-keys 3EE67F3D0FF405B2
+sudo gpg --export 3EE67F3D0FF405B2 > 3EE67F3D0FF405B2.gpg
+sudo apt-key add ./3EE67F3D0FF405B2.gpg
+sudo rm ./3EE67F3D0FF405B2.gpg
+sudo apt-get -qy --no-install-recommends install -y --force-yes firefox
+sudo apt-get -qy --no-install-recommends install -y --force-yes $(apt-cache depends firefox | grep Depends | sed "s/.*ends:\ //" | tr '\n' ' ')
+cd /tmp
+sudo wget --no-check-certificate -O firefox-esr.tar.bz2 'https://download.mozilla.org/?product=firefox-esr-latest&os=linux32&lang=en-US'
+sudo tar -xjf firefox-esr.tar.bz2 -C /opt/
+sudo rm -rf /usr/bin/firefox
+sudo ln -s /opt/firefox/firefox /usr/bin/firefox
+sudo rm -f /tmp/firefox-esr.tar.bz2
+sudo apt-get -f install -y --force-yes firefox
+
+# Install more dependencies
+sudo apt-get update
+sudo apt-get install -y --force-yes xvfb
+sudo apt-get install -y --force-yes build-essential chrpath libssl-dev libxft-dev
+sudo apt-get install -y --force-yes libfreetype6 libfreetype6-dev
+sudo apt-get install -y --force-yes libfontconfig1 libfontconfig1-dev
+sudo apt-get install -y --force-yes libmysqlclient-dev
+sudo apt-get install -y --force-yes python-dev
+sudo apt-get install -y --force-yes python-MySQLdb
+
+# Install PhantomJS
+cd ~
+export PHANTOM_JS="phantomjs-2.1.1-linux-x86_64"
+sudo wget https://bitbucket.org/ariya/phantomjs/downloads/$PHANTOM_JS.tar.bz2
+sudo tar xvjf $PHANTOM_JS.tar.bz2
+sudo mv -f $PHANTOM_JS /usr/local/share
+sudo ln -sf /usr/local/share/$PHANTOM_JS/bin/phantomjs /usr/local/bin
+
+# Install Chrome
+cd /tmp
+sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
+sudo apt-get -f install -y --force-yes
+sudo dpkg -i google-chrome-stable_current_amd64.deb
+
+# Install Chromedriver
+sudo wget -N http://chromedriver.storage.googleapis.com/2.28/chromedriver_linux64.zip -P ~/Downloads
+sudo unzip -o ~/Downloads/chromedriver_linux64.zip -d ~/Downloads
+sudo chmod +x ~/Downloads/chromedriver
+sudo rm -f /usr/local/share/chromedriver
+sudo rm -f /usr/local/bin/chromedriver
+sudo rm -f /usr/bin/chromedriver
+sudo mv -f ~/Downloads/chromedriver /usr/local/share/chromedriver
+sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
+sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver
+
+# Finalize apt-get dependancies
+sudo apt-get -f install -y --force-yes
+
+# Get pip
+sudo easy_install pip
diff --git a/SeleniumBase/integrations/linux/ReadMe.md b/SeleniumBase/integrations/linux/ReadMe.md
new file mode 100755
index 00000000..b8a90587
--- /dev/null
+++ b/SeleniumBase/integrations/linux/ReadMe.md
@@ -0,0 +1,3 @@
+## Running SeleniumBase on Debian GNU/Linux
+
+The files in this folder are currently used with the [Google Cloud setup instructions for SeleniumBase](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/google_cloud/ReadMe.md). You can also use these files standalone with any Debian GNU/Linux machine.
diff --git a/SeleniumBase/integrations/linux/Xvfb_launcher.sh b/SeleniumBase/integrations/linux/Xvfb_launcher.sh
new file mode 100755
index 00000000..48d1208f
--- /dev/null
+++ b/SeleniumBase/integrations/linux/Xvfb_launcher.sh
@@ -0,0 +1,5 @@
+# Activate Headless Display (Xvfb)
+
+sudo Xvfb -ac :99 -screen 0 1280x1024x16 > /dev/null 2>&1 &
+export DISPLAY=:99
+exec "$@"
diff --git a/SeleniumBase/integrations/linux/jenkins_permissions.sh b/SeleniumBase/integrations/linux/jenkins_permissions.sh
new file mode 100755
index 00000000..9316219b
--- /dev/null
+++ b/SeleniumBase/integrations/linux/jenkins_permissions.sh
@@ -0,0 +1,10 @@
+# This file will add "tomcat" to the sudoers file.
+# "tomcat" is the Jenkins user name by default
+
+# To become tomcat from a different user, use the following:
+# sudo su tomcat
+# bash
+
+sudo sh -c "echo \"%tomcat ALL=(ALL:ALL) ALL\" >> /etc/sudoers"
+sudo sh -c "echo \"%tomcat ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers"
+sudo sh -c "echo \"tomcat ALL=NOPASSWD: ALL\" >> /etc/sudoers"
diff --git a/SeleniumBase/integrations/node_js/ReadMe.md b/SeleniumBase/integrations/node_js/ReadMe.md
new file mode 100755
index 00000000..8d529674
--- /dev/null
+++ b/SeleniumBase/integrations/node_js/ReadMe.md
@@ -0,0 +1,42 @@
+## Creating a SeleniumBase Test Launcher using NodeJS
+
+Great news: It's really easy to create a customized web app for kicking off SeleniumBase jobs using NodeJS. This tutorial will walk you through all the steps that you need. (I'll assume that you've already installed SeleniumBase by following the instructions from the [top-level ReadMe](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md) file.)
+
+#### 1. Install NodeJS
+
+* Navigate to [https://nodejs.org/en/](https://nodejs.org/en/)
+* Click to download and install NodeJS
+
+#### 2. Install Express for NodeJS
+
+```bash
+npm install -g express
+```
+
+#### 3. Install the Example Test Launcher for SeleniumBase from the ``integrations/node_js`` folder
+
+```bash
+npm install
+```
+
+(You should see a ``node_modules`` folder appear in your ``node_js`` folder.)
+
+#### 4. Run the NodeJS server for your SeleniumBase Test Launcher web app
+
+```bash
+node server.js
+```
+
+(You can always stop the server by using ``CTRL-C``.)
+
+#### 5. Open the SeleniumBase Test Launcher web app
+
+* Navigate to [http://127.0.0.1:3000/](http://127.0.0.1:3000/)
+
+#### 6. Run an example test
+
+Click on one of the buttons to run a SeleniumBase example test
+
+#### 7. Enjoy your web app
+
+Congratulations! You now have a web app for kicking off SeleniumBase tests! NodeJS makes it easy!
diff --git a/SeleniumBase/integrations/node_js/index.html b/SeleniumBase/integrations/node_js/index.html
new file mode 100644
index 00000000..4853aec5
--- /dev/null
+++ b/SeleniumBase/integrations/node_js/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
Select an option:
+
+
+
+
+
+
diff --git a/SeleniumBase/integrations/node_js/my_first_test.py b/SeleniumBase/integrations/node_js/my_first_test.py
new file mode 100755
index 00000000..ef7648af
--- /dev/null
+++ b/SeleniumBase/integrations/node_js/my_first_test.py
@@ -0,0 +1,22 @@
+from seleniumbase import BaseCase
+
+
+class MyTestClass(BaseCase):
+
+ def test_basic(self):
+ self.open('http://xkcd.com/353/')
+ self.assert_element('img[alt="Python"]')
+ self.click('a[rel="license"]')
+ self.assert_text('copy and reuse', 'div center')
+ self.open('http://xkcd.com/1481/')
+ image_object = self.find_element('#comic img')
+ caption = image_object.get_attribute('title')
+ self.assertTrue('connections to the server' in caption)
+ self.click_link_text('Blag')
+ self.assert_text('xkcd', '#site-title')
+ header_text = self.get_text('header h2')
+ self.assertTrue('The blag of the webcomic' in header_text)
+ self.update_text('input#s', 'Robots!\n')
+ self.assert_text('Hooray robots!', '#content')
+ self.open('http://xkcd.com/1319/')
+ self.assert_text('Automation', 'div#ctitle')
diff --git a/SeleniumBase/integrations/node_js/package.json b/SeleniumBase/integrations/node_js/package.json
new file mode 100755
index 00000000..94289264
--- /dev/null
+++ b/SeleniumBase/integrations/node_js/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "app",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "start": "node ./bin/www"
+ },
+ "dependencies": {
+ "body-parser": "~1.15.1",
+ "cookie-parser": "~1.4.3",
+ "debug": "~2.2.0",
+ "express": "~4.13.4",
+ "pug": "~2.0.0-alpha6",
+ "morgan": "~1.7.0",
+ "serve-favicon": "~2.3.0"
+ }
+}
\ No newline at end of file
diff --git a/SeleniumBase/integrations/node_js/server.js b/SeleniumBase/integrations/node_js/server.js
new file mode 100644
index 00000000..c777950d
--- /dev/null
+++ b/SeleniumBase/integrations/node_js/server.js
@@ -0,0 +1,54 @@
+var http = require('http');
+var express = require('express');
+var path = require('path');
+var app = express();
+var exec = require('child_process').exec;
+
+function run_my_first_test_in_firefox() {
+ exec("nosetests my_first_test.py --with-selenium --browser=firefox");
+}
+
+function run_my_first_test_in_chrome() {
+ exec("nosetests my_first_test.py --with-selenium --browser=chrome");
+}
+
+function run_my_first_test_in_firefox_with_demo_mode() {
+ exec("nosetests my_first_test.py --with-selenium --browser=firefox --demo_mode");
+}
+
+function run_my_first_test_in_chrome_with_demo_mode() {
+ exec("nosetests my_first_test.py --with-selenium --browser=chrome --demo_mode");
+}
+
+app.get('/', function(req, res) {
+ res.sendFile(path.join(__dirname + '/index.html'));
+})
+
+app.get('/run_my_first_test_in_firefox', function(req, res) {
+ res.sendFile(path.join(__dirname + '/index.html'));
+ res.redirect('/');
+ run_my_first_test_in_firefox()
+})
+
+app.get('/run_my_first_test_in_chrome', function(req, res) {
+ res.sendFile(path.join(__dirname + '/index.html'));
+ res.redirect('/');
+ run_my_first_test_in_chrome()
+})
+
+app.get('/run_my_first_test_in_firefox_with_demo_mode', function(req, res) {
+ res.sendFile(path.join(__dirname + '/index.html'));
+ res.redirect('/');
+ run_my_first_test_in_firefox_with_demo_mode()
+})
+
+app.get('/run_my_first_test_in_chrome_with_demo_mode', function(req, res) {
+ res.sendFile(path.join(__dirname + '/index.html'));
+ res.redirect('/');
+ run_my_first_test_in_chrome_with_demo_mode()
+})
+
+app.listen(3000, "127.0.0.1", function() {
+ console.log('Server running at http://127.0.0.1:3000/');
+});
+
diff --git a/SeleniumBase/integrations/selenium_grid/ReadMe.md b/SeleniumBase/integrations/selenium_grid/ReadMe.md
new file mode 100644
index 00000000..5a9e3a4c
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/ReadMe.md
@@ -0,0 +1,34 @@
+## Notes on using the Selenium Grid Hub
+
+The Selenium Grid Hub allows you to distribute tests to run in parallel across multiple machines. Each machine can then run its own allocation of tests in parallel. This allows you to run an entire test suite quickly, which may be important if you have a lot of tests to run. Machines can be personal computers, data centers, or virtual machines in the cloud. You can also create your own virtual machine by using a tool such as Docker (see the [Docker ReadMe](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/docker/ReadMe.md)).
+
+### Running the Selenium Grid Hub
+
+First, download the latest selenium-server-standalone jar file to this folder (integrations/selenium_grid):
+```bash
+python download_selenium.py
+```
+Now you can start up the Grid Hub:
+```bash
+./grid-hub start
+```
+Now you can add a Grid Node to the Grid Hub:
+```bash
+./grid-node start
+```
+(NOTE: If the Grid Node is not running on the same machine as the Grid Hub, update the address from the script.)
+You should be able to see the Grid Console up and running from here: [http://0.0.0.0:4444/grid/console](http://0.0.0.0:4444/grid/console) (NOTE: That's the address if you're running locally from localhost.)
+
+You can remove a Grid Node from the Grid Hub with:
+```bash
+./grid-node stop
+```
+You can stop the Grid Hub at anytime with:
+```bash
+./grid-hub stop
+```
+
+#### More detailed info about connecting to the Selenium Grid Hub can be found here:
+* [https://theintern.github.io/intern/#selenium-grid](https://theintern.github.io/intern/#selenium-grid)
+* [https://github.com/SeleniumHQ/selenium/wiki/Grid2](https://github.com/SeleniumHQ/selenium/wiki/Grid2)
+* [https://github.com/SeleniumHQ/selenium/wiki](https://github.com/SeleniumHQ/selenium/wiki/Grid2)
diff --git a/SeleniumBase/integrations/selenium_grid/download_selenium.py b/SeleniumBase/integrations/selenium_grid/download_selenium.py
new file mode 100644
index 00000000..826c255a
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/download_selenium.py
@@ -0,0 +1,11 @@
+""" Download the selenium server jar file """
+
+import os
+from seleniumbase.core import selenium_launcher
+
+if not selenium_launcher.is_available_locally():
+ selenium_launcher.download_selenium()
+
+for filename in os.listdir("."):
+ if filename.startswith("selenium-server-standalone-"):
+ os.rename(filename, "selenium-server-standalone.jar")
diff --git a/SeleniumBase/integrations/selenium_grid/font_color b/SeleniumBase/integrations/selenium_grid/font_color
new file mode 100755
index 00000000..3dd0a581
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/font_color
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+# Text color variables
+txtund=$(tput sgr 0 1) # Underline
+txtbld=$(tput bold) # Bold
+regred=$(tput setaf 1) # Red
+regblu=$(tput setaf 4) # Blue
+reggrn=$(tput setaf 2) # Green
+regwht=$(tput setaf 7) # White
+txtrst=$(tput sgr0) # Reset
+info=${regwht}*${txtrst} # Feedback
+pass=${regblu}*${txtrst}
+warn=${regred}*${txtrst}
+ques=${regblu}?${txtrst}
+
+FAIL_MSG="${regred}[FAILURE]${txtrst}"
+SUCCESS_MSG="${reggrn}[SUCCESS]${txtrst}"
diff --git a/SeleniumBase/integrations/selenium_grid/grid-hub b/SeleniumBase/integrations/selenium_grid/grid-hub
new file mode 100755
index 00000000..1a3ebf26
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/grid-hub
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+
+#
+# Usage: grid-hub {start|stop}
+#
+
+source $(dirname $0)/font_color
+
+EXPECTED_ARGS=1
+E_BADARGS=65
+
+DO_showUsage() {
+ echo "Usage: $(basename $0) {start|stop}"
+ exit $E_BADARGS
+}
+
+if [ $# -ne $EXPECTED_ARGS ]; then
+ DO_showUsage
+fi
+
+################################################################################
+
+WEBDRIVER_SERVER_JAR=./selenium-server-standalone.jar
+WEBDRIVER_HUB_PARAMS="-role hub -port 4444"
+WEBDRIVER_HUB_PIDFILE="/tmp/webdriver_hub.pid"
+
+if [ ! -f $WEBDRIVER_SERVER_JAR ]; then
+ echo "You must place the Selenium-WebDriver standalone JAR file at ${WEBDRIVER_SERVER_JAR} before proceeding."
+ exit 1
+fi
+
+case "$1" in
+ start)
+ echo "Starting Selenium-WebDriver Grid hub..."
+ if [ -f $WEBDRIVER_HUB_PIDFILE ]; then
+ echo "${FAIL_MSG} Selenium-WebDriver Grid hub already running with PID $(cat $WEBDRIVER_HUB_PIDFILE). Run 'grid-hub stop' or 'grid-hub restart'."
+ exit 1
+ else
+ START_HUB_CMD="java -Djava.util.logging.config.file=test/logging.properties -jar ${WEBDRIVER_SERVER_JAR} ${WEBDRIVER_HUB_PARAMS}"
+ $START_HUB_CMD &
+ PID=$!
+ echo $PID > "${WEBDRIVER_HUB_PIDFILE}"
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid hub started successfully."
+ echo "To see full log output, remove the java.util.logging.config.file parameter from script/grid-hub"
+ fi
+ ;;
+ stop)
+ echo "Stopping Selenium-WebDriver Grid hub..."
+ if [ -f $WEBDRIVER_HUB_PIDFILE ]; then
+ PID=$(cat $WEBDRIVER_HUB_PIDFILE)
+ kill $PID
+ rm $WEBDRIVER_HUB_PIDFILE
+ sleep 1
+ if [[ $(ps -A | egrep "^${PID}") ]]; then
+ echo "${FAIL_MSG} Tried to kill the hub with PID ${PID}, but was unsuccessful. You need to kill it with something stronger, like 'kill -9'"
+ exit 1
+ else
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid hub stopped successfully."
+ exit 0
+ fi
+ else
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid hub has already been stopped."
+ exit 0
+ fi
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ DO_showUsage
+esac
diff --git a/SeleniumBase/integrations/selenium_grid/grid-node b/SeleniumBase/integrations/selenium_grid/grid-node
new file mode 100755
index 00000000..d7b0816c
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/grid-node
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+
+#
+# Usage: grid-node {start|stop}
+#
+
+source $(dirname $0)/font_color
+
+EXPECTED_ARGS=1
+E_BADARGS=65
+
+DO_showUsage() {
+ echo "Usage: $(basename $0) {start|stop}"
+ exit $E_BADARGS
+}
+
+if [ $# -ne $EXPECTED_ARGS ]; then
+ DO_showUsage
+fi
+
+################################################################################
+
+WEBDRIVER_SERVER_JAR=./selenium-server-standalone.jar
+WEBDRIVER_NODE_PARAMS="-role webdriver -hubHost 127.0.0.1 -hubPort 4444 -host 127.0.0.1 -browserName=firefox"
+WEBDRIVER_NODE_PIDFILE="/tmp/webdriver_node.pid"
+
+if [ ! -f $WEBDRIVER_SERVER_JAR ]; then
+ echo "You must place the Selenium-WebDriver standalone JAR file at ${WEBDRIVER_SERVER_JAR} before proceeding."
+ exit 1
+fi
+
+case "$1" in
+ start)
+ echo "Starting Selenium-WebDriver Grid node..."
+ if [ -f $WEBDRIVER_NODE_PIDFILE ]; then
+ echo "${FAIL_MSG} Selenium-WebDriver Grid node already running with PID $(cat $WEBDRIVER_NODE_PIDFILE). Run 'grid-node stop' or 'grid-node restart'."
+ exit 1
+ else
+ START_NODE_CMD="java -Djava.util.logging.config.file=test/logging.properties -jar ${WEBDRIVER_SERVER_JAR} ${WEBDRIVER_NODE_PARAMS}"
+ $START_NODE_CMD &
+ PID=$!
+ echo $PID > "${WEBDRIVER_NODE_PIDFILE}"
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid node started successfully."
+ echo "To see full log output, remove the java.util.logging.config.file parameter from script/grid-node"
+ fi
+ ;;
+ stop)
+ echo "Stopping Selenium-WebDriver Grid node..."
+ if [ -f $WEBDRIVER_NODE_PIDFILE ]; then
+ PID=$(cat $WEBDRIVER_NODE_PIDFILE)
+ kill $PID
+ rm $WEBDRIVER_NODE_PIDFILE
+ sleep 1
+ if [[ $(ps -A | egrep "^${PID}") ]]; then
+ echo "${FAIL_MSG} Tried to kill the node with PID ${PID}, but was unsuccessful. You need to kill it with something stronger, like 'kill -9'"
+ exit 1
+ else
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid node stopped successfully."
+ exit 0
+ fi
+ else
+ echo "${SUCCESS_MSG} Selenium-WebDriver Grid node has already been stopped."
+ exit 0
+ fi
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ DO_showUsage
+esac
diff --git a/SeleniumBase/integrations/selenium_grid/selenium_server_config_example.cfg b/SeleniumBase/integrations/selenium_grid/selenium_server_config_example.cfg
new file mode 100644
index 00000000..e95423df
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/selenium_server_config_example.cfg
@@ -0,0 +1,12 @@
+[nosetests]
+with-xunit=1
+with-selenium=1
+server=[IF NOT RUNNING THE TESTS LOCALLY, ENTER_YOUR_SELENIUM_SERVER_HOSTNAME_HERE - MIGHT BE YOUR OWN, OR ON AN EC2 MACHINE, PORT 4444 LIKELY, OR YOU MIGHT BE USING BROWSERSTACK: *.browserstack.com, PORT 80 LIKELY. IF RUNNING LOCALLY REMOVE THIS ENTIRE LINE AND THE LINE WITH THE "PORT"!]
+port=4444
+with-testing_base=1
+with-page_source=1
+with-screen_shots=1
+with-s3_logging=0
+with-db_reporting=0
+with-basic_test_info=1
+nocapture=0
diff --git a/SeleniumBase/integrations/selenium_grid/start-selenium-node.bat b/SeleniumBase/integrations/selenium_grid/start-selenium-node.bat
new file mode 100644
index 00000000..e9025f0b
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/start-selenium-node.bat
@@ -0,0 +1,2 @@
+cd c:\
+java -jar selenium-server-standalone.jar -role node -hub http://[ENTER URL OF THE GRID HUB SERVER]:4444/grid/register -browser browserName=chrome,maxInstances=5 -browser browserName=firefox,maxInstances=5 -browser browserName="internet explorer",maxInstances=1
\ No newline at end of file
diff --git a/SeleniumBase/integrations/selenium_grid/start-selenium-server.sh b/SeleniumBase/integrations/selenium_grid/start-selenium-server.sh
new file mode 100755
index 00000000..52aad034
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_grid/start-selenium-server.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+java -jar selenium-server-standalone.jar -role hub
\ No newline at end of file
diff --git a/SeleniumBase/integrations/selenium_ide/ReadMe.md b/SeleniumBase/integrations/selenium_ide/ReadMe.md
new file mode 100755
index 00000000..1a05b54e
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_ide/ReadMe.md
@@ -0,0 +1,29 @@
+## Converting Selenium IDE recordings into SeleniumBase test scripts
+
+[Selenium IDE](http://docs.seleniumhq.org/projects/ide/) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/). Selenium IDE comes with an option to export recordings as various WebDriver test scripts, one of which is ``Python2/unittest/WebDriver``. Unfortunately, these natively-exported scripts tend to be very messy and don't run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments.
+
+#### Step 1: Make a recording with Selenium IDE
+
+
+
+#### Step 2: Export your recording as a Python 2 Webdriver script
+
+* ``File`` => ``Export Test Case As`` => ``Python 2 / unittest / WebDriver``
+
+#### Step 3: Drop your exported file into the ``selenium_ide`` folder
+
+* Just copy & paste!
+
+(The full path of the folder is ``SeleniumBase/integrations/selenium_ide``)
+
+#### Step 4: Run ``convert_ide.py`` on the exported Python script
+
+```bash
+python convert_ide.py [MY_TEST.py]
+```
+
+You should see a [MY_TEST_SB.py] file appear in the folder. (``_SB`` is added to the file name so that the original file stays intact in case you still need it.)
+
+#### Step 5: Enjoy your new clean & reliable SeleniumBase test script
+
+* You can now copy your new SeleniumBase test script into your test suite. It's ready to be run!
diff --git a/SeleniumBase/integrations/selenium_ide/convert_ide.py b/SeleniumBase/integrations/selenium_ide/convert_ide.py
new file mode 100755
index 00000000..99f9846b
--- /dev/null
+++ b/SeleniumBase/integrations/selenium_ide/convert_ide.py
@@ -0,0 +1,273 @@
+"""
+Converts a Selenium IDE WebDriver-exported test file into a SeleniumBase file
+
+Usage:
+python convert_ide.py [MY_TEST.py]
+Output:
+[MY_TEST_SB.py] (Adds "_SB" to the file name)
+"""
+
+import codecs
+import re
+import sys
+
+
+def main():
+ expected_arg = "[A Selenium IDE recording exported to Python WebDriver]"
+ num_args = len(sys.argv)
+ if num_args < 2 or num_args > 2:
+ raise Exception("\n* INVALID RUN COMMAND! * Usage:\n"
+ "python convert_ide.py %s\n" % expected_arg)
+ elif num_args == 2:
+ if not sys.argv[1].endswith('.py'):
+ raise Exception("Not a Python file!")
+ webdriver_python_file = sys.argv[1]
+
+ seleniumbase_lines = []
+ seleniumbase_lines.append("from seleniumbase import BaseCase")
+ seleniumbase_lines.append("") # Flake8 is very specific on whitespace
+ seleniumbase_lines.append("")
+
+ ide_base_url = ""
+ in_test_method = False
+ has_unicode = False
+
+ f = open(webdriver_python_file, 'r')
+ all_code = f.read()
+ f.close()
+ if "def test_" not in all_code:
+ raise Exception("Not a valid Python test file!")
+ code_lines = all_code.split('\n')
+ for line in code_lines:
+
+ # Handle utf-8 encoding if present
+ data = re.findall('^\s*# -\*- coding: utf-8 -\*-\s*$', line)
+ if data:
+ has_unicode = True
+ continue
+
+ # Handle class definition
+ data = re.findall('^class\s\S+\(unittest\.TestCase\):\s*$', line)
+ if data:
+ data = data[0].replace("unittest.TestCase", "BaseCase")
+ seleniumbase_lines.append(data)
+ continue
+
+ # Get base_url if defined
+ data = re.match('^\s*self.base_url = "(\S+)"\s*$', line)
+ if data:
+ ide_base_url = data.group(1)
+ continue
+
+ # Handle method definitions
+ data = re.match('^\s*def\s(\S+)\(self[,\s\S]*\):\s*$', line)
+ if data:
+ method_name = data.group(1)
+ if method_name.startswith('test_'):
+ in_test_method = True
+ seleniumbase_lines.append("")
+ seleniumbase_lines.append(data.group())
+ else:
+ in_test_method = False
+ continue
+
+ # If not in a test method, skip
+ if not in_test_method:
+ continue
+
+ # If a comment, skip
+ if line.strip().startswith("#"):
+ continue
+
+ # If a blank line, skip
+ if len(line.strip()) == 0:
+ continue
+
+ # If .clear(), skip because .update_text() already does this
+ if line.strip().endswith(".clear()"):
+ continue
+
+ # Skip edge case
+ data = re.findall('^\s*driver = self.driver\s*$', line)
+ if data:
+ continue
+
+ # Handle page loads
+ data = re.match(
+ '^(\s*)driver\.get\((self\.base_url \+ \"/\S*\")\)\s*$', line)
+ if data:
+ whitespace = data.group(1)
+ url = data.group(2)
+ url = url.replace("self.base_url", '"%s"' % ide_base_url)
+ if '/" + "/' in url:
+ url = url.replace('/" + "/', '/')
+ if "/' + '/" in url:
+ url = url.replace("/' + '/", "/")
+ command = '''%sself.open(%s)''' % (whitespace, url)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle more page loads
+ data = re.match(
+ '^(\s*)driver\.get\(\"(\S*)\"\)\s*$', line)
+ if data:
+ whitespace = data.group(1)
+ url = data.group(2)
+ command = '''%sself.open('%s')''' % (whitespace, url)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_id() + .click()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_id\(\"(\S+)\"\)'''
+ '''\.click\(\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '#%s' % data.group(2)
+ command = '''%sself.click('%s')''' % (whitespace, css_selector)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_id() + .send_keys()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_id\(\"(\S+)\"\)'''
+ '''\.send_keys\(\"([\S\s]+)\"\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '#%s' % data.group(2)
+ text = data.group(3)
+ command = '''%sself.update_text('%s', '%s')''' % (
+ whitespace, css_selector, text)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_name() + .click()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_name\(\"(\S+)\"\)'''
+ '''\.click\(\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '[name="%s"]' % data.group(2)
+ command = '''%sself.click('%s')''' % (whitespace, css_selector)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_name() + .send_keys()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_name\(\"(\S+)\"\)'''
+ '''\.send_keys\(\"([\S\s]+)\"\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '[name="%s"]' % data.group(2)
+ text = data.group(3)
+ command = '''%sself.update_text('%s', '%s')''' % (
+ whitespace, css_selector, text)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_css_selector() + .click()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_css_selector\(\"([\S\s]+)\"\)'''
+ '''\.click\(\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '%s' % data.group(2)
+ command = '''%sself.click('%s')''' % (whitespace, css_selector)
+ if command.count('\\"') == command.count('"'):
+ command = command.replace('\\"', '"')
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_css_selector() + .send_keys()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_css_selector\(\"([\S\s]+)\"\)'''
+ '''\.send_keys\(\"([\S\s]+)\"\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '%s' % data.group(2)
+ text = data.group(3)
+ command = '''%sself.update_text('%s', '%s')''' % (
+ whitespace, css_selector, text)
+ if command.count('\\"') == command.count('"'):
+ command = command.replace('\\"', '"')
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle Select / by_css_selector() / select_by_visible_text()
+ data = re.match(
+ '''^(\s*)Select\(driver\.find_element_by_css_selector\('''
+ '''\"([\S\s]+)\"\)\)\.select_by_visible_text\('''
+ '''\"([\S\s]+)\"\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ css_selector = '%s' % data.group(2)
+ visible_text = '%s' % data.group(3)
+ command = '''%sself.pick_select_option_by_text('%s', '%s')''' % (
+ whitespace, css_selector, visible_text)
+ if command.count('\\"') == command.count('"'):
+ command = command.replace('\\"', '"')
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_xpath() + .click()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_xpath\(u?\"([\S\s]+)\"\)'''
+ '''\.click\(\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ xpath = '%s' % data.group(2)
+ if '(u"' in line:
+ uni = "u"
+ has_unicode = True
+ command = '''%sself.click_xpath(%s"%s")''' % (
+ whitespace, uni, xpath)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Handle .find_element_by_link_text() + .click()
+ data = re.match(
+ '''^(\s*)driver\.find_element_by_link_text\(u?\"([\S\s]+)\"\)'''
+ '''\.click\(\)\s*$''', line)
+ if data:
+ whitespace = data.group(1)
+ link_text = '''%s''' % data.group(2)
+ uni = ""
+ if '(u"' in line:
+ uni = "u"
+ has_unicode = True
+ command = '''%sself.click_link_text(%s"%s")''' % (
+ whitespace, uni, link_text)
+ seleniumbase_lines.append(command)
+ continue
+
+ # Replace "self.base_url" with actual url if not already done
+ if 'self.base_url' in line:
+ line = line.replace("self.base_url", '"%s"' % ide_base_url)
+
+ # Convert driver. to self.driver. if not already done
+ if 'driver.' in line and 'self.driver' not in line:
+ line = line.replace('driver.', 'self.driver.')
+
+ # Add all other lines to final script without making changes
+ seleniumbase_lines.append(line)
+
+ seleniumbase_code = ""
+ if has_unicode:
+ seleniumbase_code = "# -*- coding: utf-8 -*-\n"
+ for line in seleniumbase_lines:
+ seleniumbase_code += line
+ seleniumbase_code += "\n"
+ # print seleniumbase_code # (For debugging)
+
+ # Create SeleniumBase test file
+ base_file_name = webdriver_python_file.split('.py')[0]
+ converted_file_name = base_file_name + "_SB.py"
+ out_file = codecs.open(converted_file_name, "w+")
+ out_file.writelines(seleniumbase_code)
+ out_file.close()
+ print("%s successfully created from %s\n" % (
+ converted_file_name, webdriver_python_file))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/SeleniumBase/pytest.ini b/SeleniumBase/pytest.ini
new file mode 100644
index 00000000..4eaf59cf
--- /dev/null
+++ b/SeleniumBase/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+addopts = --capture=no --with-selenium --ignore conftest.py
diff --git a/SeleniumBase/requirements.txt b/SeleniumBase/requirements.txt
new file mode 100755
index 00000000..f6c805ab
--- /dev/null
+++ b/SeleniumBase/requirements.txt
@@ -0,0 +1,18 @@
+pip>=9.0.1
+setuptools>=35.0.1
+ipython==5.3.0
+selenium==3.4.1
+nose>=1.3.7
+pytest>=3.0.7
+pytest-html>=1.14.2
+six>=1.10.0
+flake8>=3.3.0
+requests>=2.13.0
+urllib3>=1.20
+BeautifulSoup>=3.2.1
+unittest2>=1.1.0
+chardet>=3.0.2
+boto>=2.46.1
+ipdb==0.10.2
+pyvirtualdisplay>=0.2.1
+-e .
diff --git a/SeleniumBase/seleniumbase/__init__.py b/SeleniumBase/seleniumbase/__init__.py
new file mode 100755
index 00000000..aa4824fd
--- /dev/null
+++ b/SeleniumBase/seleniumbase/__init__.py
@@ -0,0 +1,2 @@
+from seleniumbase.fixtures.base_case import BaseCase # noqa
+from seleniumbase.masterqa.master_qa import MasterQA # noqa
diff --git a/SeleniumBase/seleniumbase/common/__init__.py b/SeleniumBase/seleniumbase/common/__init__.py
new file mode 100755
index 00000000..e69de29b
diff --git a/SeleniumBase/seleniumbase/common/decorators.py b/SeleniumBase/seleniumbase/common/decorators.py
new file mode 100755
index 00000000..8c08deb1
--- /dev/null
+++ b/SeleniumBase/seleniumbase/common/decorators.py
@@ -0,0 +1,66 @@
+import logging
+import math
+import threading
+import time
+from functools import wraps
+
+
+def retry_on_exception(tries=6, delay=1, backoff=2, max_delay=32):
+ '''
+ Decorator for implementing exponential backoff for retrying on failures.
+
+ tries: Max number of tries to execute the wrapped function before failing.
+ delay: Delay time in seconds before the FIRST retry.
+ backoff: Multiplier to extend the initial delay by for each retry.
+ max_delay: Max time in seconds to wait between retries.
+ '''
+ tries = math.floor(tries)
+ if tries < 1:
+ raise ValueError('"tries" must be greater than or equal to 1.')
+ if delay < 0:
+ raise ValueError('"delay" must be greater than or equal to 0.')
+ if backoff < 1:
+ raise ValueError('"backoff" must be greater than or equal to 1.')
+ if max_delay < delay:
+ raise ValueError('"max_delay" must be greater than or equal to delay.')
+
+ def decorated_function_with_retry(func):
+ @wraps(func)
+ def function_to_retry(*args, **kwargs):
+ local_tries, local_delay = tries, delay
+ while local_tries > 1:
+ try:
+ return func(*args, **kwargs)
+ except Exception, e:
+ if local_delay > max_delay:
+ local_delay = max_delay
+ logging.exception('%s: Retrying in %d seconds...'
+ % (str(e), local_delay))
+ time.sleep(local_delay)
+ local_tries -= 1
+ local_delay *= backoff
+ return func(*args, **kwargs)
+ return function_to_retry
+ return decorated_function_with_retry
+
+
+def rate_limited(max_per_second):
+ min_interval = 1.0 / float(max_per_second)
+
+ def decorate(func):
+ last_time_called = [0.0]
+ rate_lock = threading.Lock() # To support multi-threading
+
+ def rate_limited_function(*args, **kargs):
+ try:
+ rate_lock.acquire(True)
+ elapsed = time.clock() - last_time_called[0]
+ wait_time_remaining = min_interval - elapsed
+ if wait_time_remaining > 0:
+ time.sleep(wait_time_remaining)
+ last_time_called[0] = time.clock()
+ finally:
+ rate_lock.release()
+ return func(*args, **kargs)
+ return rate_limited_function
+ return decorate
diff --git a/SeleniumBase/seleniumbase/config/__init__.py b/SeleniumBase/seleniumbase/config/__init__.py
new file mode 100755
index 00000000..e69de29b
diff --git a/SeleniumBase/seleniumbase/config/settings.py b/SeleniumBase/seleniumbase/config/settings.py
new file mode 100755
index 00000000..5d8d1ada
--- /dev/null
+++ b/SeleniumBase/seleniumbase/config/settings.py
@@ -0,0 +1,132 @@
+"""
+You'll probably want to customize this to your own environment and needs.
+
+NOSETESTS USERS: IF YOU MAKE CHANGES TO THIS FILE, YOU NEED TO RERUN
+``python setup.py install`` IN ORDER FOR YOUR CHANGES TO TAKE EFFECT.
+"""
+
+
+# #####>>>>>----- REQUIRED/IMPORTANT SETTINGS -----<<<<<#####
+
+# Default seconds to wait for page elements to appear before performing actions
+TINY_TIMEOUT = 0.1
+MINI_TIMEOUT = 2
+SMALL_TIMEOUT = 5
+LARGE_TIMEOUT = 10
+EXTREME_TIMEOUT = 30
+
+# Default time to wait after each browser action performed during Demo Mode
+# Use Demo Mode when you want others to see what your automation is doing
+# Usage: --demo_mode when run from the command line when using --with-selenium
+# This value can be overwritten on the command line by using --demo_sleep=FLOAT
+DEFAULT_DEMO_MODE_TIMEOUT = 1.0
+
+# Number of times to repeat the highlight animation. (Seen during Demo Mode)
+# Each loop is about 0.18 seconds.
+# This value can be overwritten on the command line by using --highlights=TIMES
+HIGHLIGHTS = 4
+
+# If True, existing logs from past test runs will be saved and take up space.
+# If False, only the logs from the most recent test run will be saved locally.
+# This has no effect on Jenkins/S3/MySQL, which may still be saving test logs.
+ARCHIVE_EXISTING_LOGS = False
+
+# If True, existing downloads from past runs will be saved and take up space.
+# If False, only the downloads from the most recent run will be saved locally.
+ARCHIVE_EXISTING_DOWNLOADS = False
+
+# Default names for files saved during test failures when logging is turned on.
+# (These files will get saved to the "latest_logs/" folder)
+# Usage: "--with-testing_base"
+SCREENSHOT_NAME = "screenshot.jpg"
+BASIC_INFO_NAME = "basic_test_info.txt"
+PAGE_SOURCE_NAME = "page_source.html"
+
+# Default names for folders and files saved when reports are turned on.
+# Usage: "--report" (Also requires: "--with-testing_base")
+# These settings are also used with MasterQA
+LATEST_REPORT_DIR = "latest_report"
+REPORT_ARCHIVE_DIR = "archived_reports"
+HTML_REPORT = "report.html"
+RESULTS_TABLE = "results_table.csv"
+
+'''
+This adds wait_for_ready_state_complete() after various browser actions.
+By default, Selenium waits for the 'interactive' state before continuing.
+Setting this to True may improve reliability at the cost of speed.
+WARNING: Some websites are in a perpetual "interactive" state due to
+dynamic content that never fully finishes loading (Use "False" there).
+'''
+# Called after self.open(url) or self.open_url(url), NOT self.driver.open(url)
+WAIT_FOR_RSC_ON_PAGE_LOADS = True
+# Called after self.click(selector), NOT element.click()
+WAIT_FOR_RSC_ON_CLICKS = True
+
+'''
+This adds wait_for_angularjs() after wait_for_ready_state_complete()
+after various browser actions. Setting this to True may improve reliability
+of AngularJs applications at the cost of speed.
+NOTE: This requires WAIT_FOR_RSC_ON_PAGE_LOADS and/or WAIT_FOR_RSC_ON_CLICKS
+to be True since it's part of wait_for_ready_state_complete().
+'''
+# Called after each wait_for_ready_state_complete()
+WAIT_FOR_ANGULARJS = False
+
+
+# #####>>>>>----- MasterQA SETTINGS -----<<<<<#####
+# ##### (Used when importing MasterQA as the parent class)
+
+# The default message that appears when you don't specify a custom message
+MASTERQA_DEFAULT_VALIDATION_MESSAGE = "Does the page look good?"
+
+# The time delay (in seconds) before the validation pop-up appears
+# This value can be overwritten on the command line. Ex: --verify_delay=0.5
+MASTERQA_WAIT_TIME_BEFORE_VERIFY = 1.0
+
+# If True, the automation will start in full-screen mode
+MASTERQA_START_IN_FULL_SCREEN_MODE = True
+
+# The maximimum idle time allowed (in seconds) before timing out and exiting
+MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = 600
+
+
+# #####>>>>>----- RECOMMENDED SETTINGS -----<<<<<#####
+# ##### (For database reporting and saving test logs)
+
+# MySQL DB Credentials
+# (For saving data from tests)
+DB_HOST = "127.0.0.1"
+DB_USERNAME = "root"
+DB_PASSWORD = "test"
+DB_SCHEMA = "test"
+
+
+# Amazon S3 Bucket Credentials
+# (For saving screenshots and other log files from tests)
+S3_LOG_BUCKET = "[S3 BUCKET NAME]"
+S3_BUCKET_URL = "https://[S3 BUCKET NAME].s3.amazonaws.com/"
+S3_SELENIUM_ACCESS_KEY = "[S3 ACCESS KEY]"
+S3_SELENIUM_SECRET_KEY = "[S3 SECRET KEY]"
+
+
+# #####>>>>>----- OPTIONAL SETTINGS -----<<<<<#####
+# ##### (For reading emails, notifying people via chat apps, etc.)
+
+# Default Email Credentials
+# (If tests send out emails, you can scan and verify them by using IMAP)
+# Here's a list of imap strings for known email providers:
+# - Gmail: imap.gmail.com
+# - Outlook/Live: imap-mail.outlook.com
+# - Yahoo Mail: imap.mail.yahoo.com
+# - AT&T: imap.mail.att.net
+# - Comcast: imap.comcast.net
+# - Verizon: incoming.verizon.net
+EMAIL_USERNAME = "[TEST ACCOUNT GMAIL USERNAME]@gmail.com"
+EMAIL_PASSWORD = "[TEST ACCOUNT GMAIL PASSWORD]"
+EMAIL_IMAP_STRING = "imap.gmail.com"
+EMAIL_IMAP_PORT = 993
+
+# HipChat Reporting Credentials
+# (For HipChat notifications if your team uses HipChat)
+# (room_id and owner_to_mention get entered during nosetest options)
+HIPCHAT_AUTH_TOKEN = "[ENTER YOUR HIPCHAT AUTH TOKEN HERE]"
diff --git a/SeleniumBase/seleniumbase/core/__init__.py b/SeleniumBase/seleniumbase/core/__init__.py
new file mode 100755
index 00000000..e69de29b
diff --git a/SeleniumBase/seleniumbase/core/application_manager.py b/SeleniumBase/seleniumbase/core/application_manager.py
new file mode 100755
index 00000000..c6b9112b
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/application_manager.py
@@ -0,0 +1,26 @@
+"""
+Method for generating application strings used in the Testcase Database.
+"""
+
+import time
+
+
+class ApplicationManager:
+ """
+ This class contains methods to generate application strings.
+ """
+
+ @classmethod
+ def generate_application_string(cls, test):
+ """ Generate an application string based on some of the given information
+ that can be pulled from the test object: app_env, start_time. """
+
+ app_env = 'test'
+ if hasattr(test, 'env'):
+ app_env = test.env
+ elif hasattr(test, 'environment'):
+ app_env = test.environment
+
+ start_time = int(time.time() * 1000)
+
+ return "%s.%s" % (app_env, start_time)
diff --git a/SeleniumBase/seleniumbase/core/browser_launcher.py b/SeleniumBase/seleniumbase/core/browser_launcher.py
new file mode 100755
index 00000000..a326e633
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/browser_launcher.py
@@ -0,0 +1,76 @@
+from selenium import webdriver
+from selenium.common.exceptions import WebDriverException
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from seleniumbase.core import download_helper
+from seleniumbase.fixtures import constants
+
+
+def _create_firefox_profile(downloads_path):
+ profile = webdriver.FirefoxProfile()
+ profile.set_preference("reader.parse-on-load.enabled", False)
+ profile.set_preference("pdfjs.disabled", True)
+ profile.set_preference(
+ "security.mixed_content.block_active_content", False)
+ profile.set_preference(
+ "browser.download.manager.showAlertOnComplete", False)
+ profile.set_preference("browser.download.panel.shown", False)
+ profile.set_preference(
+ "browser.download.animateNotifications", False)
+ profile.set_preference("browser.download.dir", downloads_path)
+ profile.set_preference("browser.download.folderList", 2)
+ profile.set_preference(
+ "browser.helperApps.neverAsk.saveToDisk",
+ ("application/pdf, application/zip, application/octet-stream, "
+ "text/csv, text/xml, application/xml, text/plain, "
+ "text/octet-stream, "
+ "application/"
+ "vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
+ return profile
+
+
+def get_driver(browser_name):
+ '''
+ Spins up a new web browser and returns the driver.
+ Tests that run with pytest spin up the browser from here.
+ Can also be used to spin up additional browsers for the same test.
+ '''
+ downloads_path = download_helper.get_downloads_folder()
+ download_helper.reset_downloads_folder()
+
+ if browser_name == constants.Browser.FIREFOX:
+ try:
+ try:
+ # Use Geckodriver for Firefox if it's on the PATH
+ profile = _create_firefox_profile(downloads_path)
+ firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
+ firefox_capabilities['marionette'] = True
+ firefox_driver = webdriver.Firefox(
+ firefox_profile=profile, capabilities=firefox_capabilities)
+ except WebDriverException:
+ # Don't use Geckodriver: Only works for old versions of Firefox
+ profile = _create_firefox_profile(downloads_path)
+ firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
+ firefox_capabilities['marionette'] = False
+ firefox_driver = webdriver.Firefox(
+ firefox_profile=profile, capabilities=firefox_capabilities)
+ return firefox_driver
+ except:
+ return webdriver.Firefox()
+ if browser_name == constants.Browser.INTERNET_EXPLORER:
+ return webdriver.Ie()
+ if browser_name == constants.Browser.EDGE:
+ return webdriver.Edge()
+ if browser_name == constants.Browser.SAFARI:
+ return webdriver.Safari()
+ if browser_name == constants.Browser.PHANTOM_JS:
+ return webdriver.PhantomJS()
+ if browser_name == constants.Browser.GOOGLE_CHROME:
+ try:
+ chrome_options = webdriver.ChromeOptions()
+ prefs = {"download.default_directory": downloads_path}
+ chrome_options.add_experimental_option("prefs", prefs)
+ chrome_options.add_argument("--allow-file-access-from-files")
+ chrome_options.add_argument("--allow-running-insecure-content")
+ return webdriver.Chrome(chrome_options=chrome_options)
+ except Exception:
+ return webdriver.Chrome()
diff --git a/SeleniumBase/seleniumbase/core/download_helper.py b/SeleniumBase/seleniumbase/core/download_helper.py
new file mode 100755
index 00000000..df1de221
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/download_helper.py
@@ -0,0 +1,35 @@
+import os
+import shutil
+import time
+from seleniumbase.config import settings
+from seleniumbase.fixtures import constants
+
+# Folder for saving downloaded files.
+# If initiated by WebDriver clicks, works ONLY for Chrome and Firefox.
+# Browser used doesn't matter if done with self.download_file(file_url)
+# or self.save_file_as(file_url, new_file_name)
+DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER
+ARCHIVE_DIR = constants.Files.ARCHIVED_DOWNLOADS_FOLDER
+
+abs_path = os.path.abspath('.')
+downloads_path = os.path.join(abs_path, DOWNLOADS_DIR)
+
+
+def get_downloads_folder():
+ return downloads_path
+
+
+def reset_downloads_folder():
+ ''' Clears the downloads folder.
+ If settings.ARCHIVE_EXISTING_DOWNLOADS is set to True, archives it. '''
+ if os.path.exists(downloads_path):
+ archived_downloads_folder = os.path.join(downloads_path, '..',
+ ARCHIVE_DIR)
+ if not os.path.exists(archived_downloads_folder):
+ os.makedirs(archived_downloads_folder)
+ archived_downloads_folder = "%sdownloads_%s" % (
+ archived_downloads_folder, int(time.time()))
+ shutil.move(downloads_path, archived_downloads_folder)
+ os.makedirs(downloads_path)
+ if not settings.ARCHIVE_EXISTING_DOWNLOADS:
+ shutil.rmtree(archived_downloads_folder)
diff --git a/SeleniumBase/seleniumbase/core/log_helper.py b/SeleniumBase/seleniumbase/core/log_helper.py
new file mode 100755
index 00000000..b3c8a537
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/log_helper.py
@@ -0,0 +1,75 @@
+import codecs
+import sys
+import traceback
+from seleniumbase.config import settings
+
+
+def log_screenshot(test_logpath, driver):
+ screenshot_name = settings.SCREENSHOT_NAME
+ screenshot_path = "%s/%s" % (test_logpath, screenshot_name)
+ driver.get_screenshot_as_file(screenshot_path)
+
+
+def log_test_failure_data(test_logpath, driver, browser):
+ basic_info_name = settings.BASIC_INFO_NAME
+ basic_file_path = "%s/%s" % (test_logpath, basic_info_name)
+ log_file = codecs.open(basic_file_path, "w+", "utf-8")
+ last_page = get_last_page(driver)
+ data_to_save = []
+ data_to_save.append("Last_Page: %s" % last_page)
+ data_to_save.append("Browser: %s " % browser)
+ data_to_save.append("Traceback: " + ''.join(
+ traceback.format_exception(sys.exc_info()[0],
+ sys.exc_info()[1],
+ sys.exc_info()[2])))
+ log_file.writelines("\r\n".join(data_to_save))
+ log_file.close()
+
+
+def log_page_source(test_logpath, driver):
+ html_file_name = settings.PAGE_SOURCE_NAME
+ try:
+ page_source = driver.page_source
+ except Exception:
+ # Since we can't get the page source from here, skip saving it
+ return
+ html_file_path = "%s/%s" % (test_logpath, html_file_name)
+ html_file = codecs.open(html_file_path, "w+", "utf-8")
+ rendered_source = get_html_source_with_base_href(driver, page_source)
+ html_file.write(rendered_source)
+ html_file.close()
+
+
+def get_last_page(driver):
+ try:
+ last_page = driver.current_url
+ except Exception:
+ last_page = '[WARNING! Browser Not Open!]'
+ if len(last_page) < 5:
+ last_page = '[WARNING! Browser Not Open!]'
+ return last_page
+
+
+def get_base_url(full_url):
+ protocol = full_url.split('://')[0]
+ simple_url = full_url.split('://')[1]
+ base_url = simple_url.split('/')[0]
+ full_base_url = "%s://%s" % (protocol, base_url)
+ return full_base_url
+
+
+def get_base_href_html(full_url):
+ ''' The base href line tells the html what the base page really is.
+ This is important when trying to open the page outside it's home. '''
+ base_url = get_base_url(full_url)
+ return '' % base_url
+
+
+def get_html_source_with_base_href(driver, page_source):
+ ''' Combines the domain base href with the html source.
+ This is needed for the page html to render correctly. '''
+ last_page = get_last_page(driver)
+ if '://' in last_page:
+ base_href_html = get_base_href_html(last_page)
+ return '%s\n%s' % (base_href_html, page_source)
+ return ''
diff --git a/SeleniumBase/seleniumbase/core/mysql.py b/SeleniumBase/seleniumbase/core/mysql.py
new file mode 100755
index 00000000..10652680
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/mysql.py
@@ -0,0 +1,68 @@
+"""
+Wrapper for MySQL functions to make life easier
+"""
+
+import time
+import mysql_conf as conf
+
+
+class DatabaseManager():
+ """
+ This class wraps database functions for easy use.
+ It connects to the testcase database.
+ """
+
+ def __init__(self, database_env='test', conf_creds=None):
+ """
+ Gets database information from mysql_conf.py and creates a connection.
+ """
+ import MySQLdb
+ db_server, db_user, db_pass, db_schema = \
+ conf.APP_CREDS[conf.Apps.TESTCASE_REPOSITORY][database_env]
+ retry_count = 3
+ backoff = 1.2 # Time to wait (in seconds) between retries
+ count = 0
+ while count < retry_count:
+ try:
+ self.conn = MySQLdb.connect(host=db_server,
+ user=db_user,
+ passwd=db_pass,
+ db=db_schema)
+ self.conn.autocommit(True)
+ self.cursor = self.conn.cursor()
+ return
+ except Exception:
+ time.sleep(backoff)
+ count = count + 1
+ if retry_count == 3:
+ raise Exception("Unable to connect to Database after 3 retries.")
+
+ def fetchall_query_and_close(self, query, values):
+ """
+ Executes a query, gets all the values and then closes up the connection
+ """
+ self.cursor.execute(query, values)
+ retval = self.cursor.fetchall()
+ self.__close_db()
+ return retval
+
+ def fetchone_query_and_close(self, query, values):
+ """
+ Executes a query, gets the first value, and closes up the connection
+ """
+ self.cursor.execute(query, values)
+ retval = self.cursor.fetchone()
+ self.__close_db()
+ return retval
+
+ def execute_query_and_close(self, query, values):
+ """
+ Executes a query and closes the connection
+ """
+ retval = self.cursor.execute(query, values)
+ self.__close_db()
+ return retval
+
+ def __close_db(self):
+ self.cursor.close()
+ self.conn.close()
diff --git a/SeleniumBase/seleniumbase/core/mysql_conf.py b/SeleniumBase/seleniumbase/core/mysql_conf.py
new file mode 100755
index 00000000..7682c11d
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/mysql_conf.py
@@ -0,0 +1,26 @@
+"""
+This file contains database credentials for the various databases
+that the tests need to access
+"""
+
+from seleniumbase.config import settings
+
+# Environments
+TEST = "test"
+
+
+class Apps:
+ TESTCASE_REPOSITORY = "testcase_repository"
+
+
+APP_CREDS = {
+
+ Apps.TESTCASE_REPOSITORY: {
+ TEST: (
+ settings.DB_HOST,
+ settings.DB_USERNAME,
+ settings.DB_PASSWORD,
+ settings.DB_SCHEMA)
+ },
+
+}
diff --git a/SeleniumBase/seleniumbase/core/report_helper.py b/SeleniumBase/seleniumbase/core/report_helper.py
new file mode 100755
index 00000000..39633fac
--- /dev/null
+++ b/SeleniumBase/seleniumbase/core/report_helper.py
@@ -0,0 +1,230 @@
+import os
+import shutil
+import sys
+import time
+from selenium import webdriver
+from seleniumbase.config import settings
+from seleniumbase.core.style_sheet import style
+from seleniumbase.fixtures import page_actions
+
+LATEST_REPORT_DIR = settings.LATEST_REPORT_DIR
+ARCHIVE_DIR = settings.REPORT_ARCHIVE_DIR
+HTML_REPORT = settings.HTML_REPORT
+RESULTS_TABLE = settings.RESULTS_TABLE
+
+
+def get_timestamp():
+ return str(int(time.time() * 1000))
+
+
+def process_successes(test, test_count, duration):
+ return(
+ '"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"' % (
+ test_count,
+ "Passed!",
+ "*",
+ "*",
+ "*",
+ test.browser,
+ get_timestamp()[:-3],
+ duration,
+ test.id(),
+ "*"))
+
+
+def process_failures(test, test_count, browser_type, duration):
+ bad_page_image = "failure_%s.jpg" % test_count
+ bad_page_data = "failure_%s.txt" % test_count
+ page_actions.save_screenshot(
+ test.driver, bad_page_image, folder=LATEST_REPORT_DIR)
+ page_actions.save_test_failure_data(
+ test.driver, bad_page_data, browser_type, folder=LATEST_REPORT_DIR)
+ exc_info = '(Unknown Failure)'
+ exception = sys.exc_info()[1]
+ if exception:
+ if hasattr(exception, 'msg'):
+ exc_info = exception.msg
+ elif hasattr(exception, 'message'):
+ exc_info = exception.message
+ else:
+ pass
+ return(
+ '"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"' % (
+ test_count,
+ "FAILED!",
+ bad_page_data,
+ bad_page_image,
+ test.driver.current_url,
+ test.browser,
+ get_timestamp()[:-3],
+ duration,
+ test.id(),
+ exc_info))
+
+
+def clear_out_old_report_logs(archive_past_runs=True, get_log_folder=False):
+ abs_path = os.path.abspath('.')
+ file_path = abs_path + "/%s" % LATEST_REPORT_DIR
+ if not os.path.exists(file_path):
+ os.makedirs(file_path)
+
+ if archive_past_runs:
+ archive_timestamp = int(time.time())
+ if not os.path.exists("%s/../%s/" % (file_path, ARCHIVE_DIR)):
+ os.makedirs("%s/../%s/" % (file_path, ARCHIVE_DIR))
+ archive_dir = "%s/../%s/report_%s" % (
+ file_path, ARCHIVE_DIR, archive_timestamp)
+ shutil.move(file_path, archive_dir)
+ os.makedirs(file_path)
+ if get_log_folder:
+ return archive_dir
+ else:
+ # Just delete bad pages to make room for the latest run.
+ filelist = [f for f in os.listdir(
+ "./%s" % LATEST_REPORT_DIR) if f.startswith("failure_") or (
+ f == HTML_REPORT) or (f.startswith("automation_failure")) or (
+ f == RESULTS_TABLE)]
+ for f in filelist:
+ os.remove("%s/%s" % (file_path, f))
+
+
+def add_bad_page_log_file(page_results_list):
+ abs_path = os.path.abspath('.')
+ file_path = abs_path + "/%s" % LATEST_REPORT_DIR
+ log_file = "%s/%s" % (file_path, RESULTS_TABLE)
+ f = open(log_file, 'w')
+ h_p1 = '''"Num","Result","Stacktrace","Screenshot",'''
+ h_p2 = '''"URL","Browser","Epoch Time","Duration",'''
+ h_p3 = '''"Test Case Address","Additional Info"\n'''
+ page_header = h_p1 + h_p2 + h_p3
+ f.write(page_header)
+ for line in page_results_list:
+ f.write("%s\n" % line)
+ f.close()
+
+
+def archive_new_report_logs():
+ log_string = clear_out_old_report_logs(get_log_folder=True)
+ log_folder = log_string.split('/')[-1]
+ abs_path = os.path.abspath('.')
+ file_path = abs_path + "/%s" % ARCHIVE_DIR
+ report_log_path = "%s/%s" % (file_path, log_folder)
+ return report_log_path
+
+
+def add_results_page(html):
+ abs_path = os.path.abspath('.')
+ file_path = abs_path + "/%s" % LATEST_REPORT_DIR
+ results_file_name = HTML_REPORT
+ results_file = "%s/%s" % (file_path, results_file_name)
+ f = open(results_file, 'w')
+ f.write(html)
+ f.close()
+ return results_file
+
+
+def build_report(report_log_path, page_results_list,
+ successes, failures, browser_type,
+ show_report):
+
+ web_log_path = "file://%s" % report_log_path
+ successes_count = len(successes)
+ failures_count = len(failures)
+ total_test_count = successes_count + failures_count
+
+ tf_color = "#11BB11"
+ if failures_count > 0:
+ tf_color = "#EE3A3A"
+
+ summary_table = '''
'
+ any_screenshots = False
+ for line in page_results_list:
+ line = line.split(',')
+ if line[1] == '"FAILED!"':
+ if not any_screenshots:
+ any_screenshots = True
+ failure_table += '''