diff --git a/.gitignore b/.gitignore
index 0b4216d8f16..e34a2acfbe2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,9 +39,12 @@ nosetests.xml
# Web Drivers
chromedriver
geckodriver
+operadriver
MicrosoftWebDriver.exe
+IEDriverServer.exe
chromedriver.exe
geckodriver.exe
+operadriver.exe
# Logs
logs
diff --git a/.travis.yml b/.travis.yml
index f16d03dc5d2..ab1508c2361 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,9 @@ before_script:
# - "wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 && tar -xvf ./phantomjs-2.1.1-linux-x86_64.tar.bz2 && export PATH=$PWD/phantomjs-2.1.1-linux-x86_64/bin:$PATH"
- "seleniumbase install chromedriver"
- "seleniumbase install geckodriver"
+ - "seleniumbase mkdir browser_tests"
script:
+ - "pytest browser_tests/boilerplates/boilerplate_test.py --headless --with-db_reporting"
- "pytest examples/my_first_test.py --browser=chrome -s --headless --with-db_reporting"
- "nosetests examples/boilerplates/boilerplate_test.py --browser=chrome --headless"
- "pytest examples/my_first_test.py --browser=firefox -s --headless --with-db_reporting"
diff --git a/README.md b/README.md
index 34786a81224..a93f6dc2bbc 100755
--- a/README.md
+++ b/README.md
@@ -1,15 +1,29 @@
-## SeleniumBase Automation Framework
+
-
+## SeleniumBase · [](https://pypi.python.org/pypi/seleniumbase) [](https://travis-ci.org/seleniumbase/SeleniumBase)
-[](https://pypi.python.org/pypi/seleniumbase) [](https://travis-ci.org/seleniumbase/SeleniumBase) [](https://gitter.im/seleniumbase/SeleniumBase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+SeleniumBase simplifies test automation with [WebDriver](https://docs.microsoft.com/en-us/microsoft-edge/webdriver) & [Pytest](https://github.com/pytest-dev/pytest).
-SeleniumBase makes it easy to build & run [Selenium-WebDriver](https://github.com/SeleniumHQ/selenium) automation with [Pytest](https://docs.pytest.org/en/latest/).
+#### Quick start in a few steps: (requires [Python](https://www.python.org/downloads/))
-**Watch [my_first_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) run in [Demo Mode](#seleniumbase_demo_mode):**
+```bash
+python -m pip install seleniumbase --upgrade
+seleniumbase install chromedriver
+seleniumbase mkdir browser_tests
+cd browser_tests
+pytest my_first_test.py --browser=chrome
+```
+
+The ``seleniumbase mkdir [NAME]`` command creates a new folder with some sample tests you can run. If you wish to use a different browser, you can use ``seleniumbase install [DRIVER]`` with other drivers such as ``edgedriver`` (Microsoft Edge) or ``geckodriver`` (Firefox). Then use ``--browser=edge`` or ``--browser=firefox`` to run tests on that browser (assuming you already have that web browser installed). ``--browser=chrome`` is the default option if not specified.
+
+**Slow-motion demo of [my_first_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) running:**

-### [**Get Started**](#seleniumbase_installation), or Learn More:
+There are many more examples to try out from the [SeleniumBase/examples](https://github.com/seleniumbase/SeleniumBase/blob/master/examples) directory, which you can run easily if you clone SeleniumBase.
+
+For more detailed steps on getting started, see the [**Detailed Instructions**](#seleniumbase_installation) section.
+
+### Learn More:
**No more repetitive WebDriver code:**
SeleniumBase automatically handles common WebDriver actions such as spinning up web browsers, waiting for page objects to load, saving screenshots during test failures, using a proxy server, and more. ([Read about customizing test runs](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).)
@@ -29,6 +43,8 @@ pytest my_first_test.py --browser=chrome
nosetests my_test_suite.py --browser=firefox
```
+Python methods that start with ``test_`` will automatically be run when using ``pytest`` or ``nosetests`` on a Python file, (or on folders containing Python files).
+
**No more messy code:**
This long line of standard WebDriver code,
```python
@@ -53,15 +69,15 @@ SeleniumBase is compatible with [Selenium Grid](https://github.com/seleniumbase/
SeleniumBase makes it easy to automate tedious business tasks. (*To learn about businesses using SeleniumBase, [Click Here](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/happy_customers.md).*)
**Extensively tested and made with love:**
-SeleniumBase was originally built for [testing HubSpot's platform](https://product.hubspot.com/blog/the-classic-qa-team-is-obsolete) and automating business processes. In 2014, SeleniumBase was open-sourced and spun off as its own independent entity to benefit users everywhere.
+SeleniumBase was originally built for [testing HubSpot's platform](https://product.hubspot.com/blog/bid/88880/Automated-Integration-Testing-with-Selenium-at-HubSpot) and automating business processes. In 2014, SeleniumBase was open-sourced and spun off as its own independent entity to benefit users everywhere.
-**Contains lots of additional features:**
+**Plenty of features:**
([Read more about SeleniumBase features here](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/features_list.md))
-
+
-## Get Started:
+## Detailed Instructions:
Before installation, **[install Python](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/install_python_pip_git.md)** and **[install a web driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/webdriver_installation.md)**.
@@ -86,14 +102,14 @@ cd SeleniumBase
If you're installing SeleniumBase from a cloned copy on your machine, use:
```
-pip install -r requirements.txt
+pip install -r requirements.txt --upgrade
python setup.py develop
```
If you're installing SeleniumBase from the [Python Package Index](https://pypi.python.org/pypi/seleniumbase), use:
```bash
-pip install seleniumbase
+pip install seleniumbase --upgrade
```
If you're installing SeleniumBase directly from GitHub, use:
@@ -611,10 +627,10 @@ Now you can parse through the email if you're looking for specific text or want
**Congratulations** on learning how to use **SeleniumBase**!
-**Questions or Comments?**
+**Questions or Comments?**
[](https://gitter.im/seleniumbase/SeleniumBase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[https://github.com/mdmintz](https://github.com/mdmintz)
[https://www.linkedin.com/in/mdmintz](https://www.linkedin.com/in/mdmintz)
-
+
diff --git a/console_scripts/ReadMe.md b/console_scripts/ReadMe.md
index 117ae1b9def..de92c6d7782 100755
--- a/console_scripts/ReadMe.md
+++ b/console_scripts/ReadMe.md
@@ -25,7 +25,7 @@ Installs the specified webdriver.
``seleniumbase mkdir [DIRECTORY_NAME]``
* Example:
-``seleniumbase mkdir gui_tests``
+``seleniumbase mkdir browser_tests``
* Output:
Creates a new folder for running SeleniumBase scripts.
diff --git a/console_scripts/run.py b/console_scripts/run.py
index c8c89b6af48..08832f0d962 100644
--- a/console_scripts/run.py
+++ b/console_scripts/run.py
@@ -6,7 +6,7 @@
Examples:
seleniumbase install chromedriver
-seleniumbase mkdir gui_tests
+seleniumbase mkdir browser_tests
seleniumbase convert my_old_webdriver_unittest.py
seleniumbase grid-hub start
seleniumbase grid-node start --hub=127.0.0.1
@@ -47,14 +47,17 @@ def show_install_usage():
print("")
print(" Usage:")
print(" seleniumbase install [DRIVER_NAME]")
- print(" (Drivers: chromedriver, geckodriver, edgedriver)")
+ print(" (Drivers: chromedriver, geckodriver, edgedriver")
+ print(" iedriver, operadriver)")
print(" Example:")
print(" seleniumbase install chromedriver")
print(" Output:")
print(" Installs the specified webdriver.")
print(" (chromedriver is required for Chrome automation)")
print(" (geckodriver is required for Firefox automation)")
- print(" (edgedriver is required for MS Edge automation)")
+ print(" (edgedriver is required for Microsoft Edge automation)")
+ print(" (iedriver is required for InternetExplorer automation)")
+ print(" (operadriver is required for Opera Browser automation)")
print("")
@@ -64,7 +67,7 @@ def show_mkdir_usage():
print(" Usage:")
print(" seleniumbase mkdir [DIRECTORY_NAME]")
print(" Example:")
- print(" seleniumbase mkdir gui_tests")
+ print(" seleniumbase mkdir browser_tests")
print(" Output:")
print(" Creates a new folder for running SeleniumBase scripts.")
print(" The new folder contains default config files,")
diff --git a/console_scripts/sb_install.py b/console_scripts/sb_install.py
index e92832dc5f2..49752075ef8 100755
--- a/console_scripts/sb_install.py
+++ b/console_scripts/sb_install.py
@@ -8,11 +8,14 @@
(chromedriver is required for Chrome automation)
(geckodriver is required for Firefox automation)
(edgedriver is required for MS Edge automation)
+ (iedriver is required for Internet Explorer automation)
+ (operadriver is required for Opera Browser automation)
"""
import os
import platform
import requests
+import shutil
import sys
import tarfile
import zipfile
@@ -28,14 +31,17 @@ def invalid_run_command():
exp = (" ** install **\n\n")
exp += " Usage:\n"
exp += " seleniumbase install [DRIVER_NAME]\n"
- exp += " (Drivers: chromedriver, geckodriver, edgedriver)\n"
+ exp += " (Drivers: chromedriver, geckodriver, edgedriver,\n"
+ exp += " iedriver, operadriver)\n"
exp += " Example:\n"
exp += " seleniumbase install chromedriver\n"
exp += " Output:\n"
exp += " Installs the specified webdriver.\n"
exp += " (chromedriver is required for Chrome automation)\n"
exp += " (geckodriver is required for Firefox automation)\n"
- exp += " (edgedriver is required for MS Edge automation)\n"
+ exp += " (edgedriver is required for Microsoft Edge automation)\n"
+ exp += " (iedriver is required for InternetExplorer automation)\n"
+ exp += " (operadriver is required for Opera Browser automation)\n"
print("")
raise Exception('INVALID RUN COMMAND!\n\n%s' % exp)
@@ -60,16 +66,20 @@ def main():
file_name = None
download_url = None
downloads_folder = DRIVER_DIR
+ sys_plat = sys.platform
+ expected_contents = None
+ platform_code = None
+ inner_folder = None
if name == "chromedriver":
- if "darwin" in sys.platform:
+ if "darwin" in sys_plat:
file_name = "chromedriver_mac64.zip"
- elif "linux" in sys.platform:
+ elif "linux" in sys_plat:
file_name = "chromedriver_linux64.zip"
- elif "win32" in sys.platform or "win64" in sys.platform:
- file_name = "chromedriver_win32.zip" # Works for win32 and win64
+ elif "win32" in sys_plat or "win64" in sys_plat or "x64" in sys_plat:
+ file_name = "chromedriver_win32.zip" # Works for win32 / win_x64
else:
- raise Exception("Cannon determine which version of Chromedriver "
+ raise Exception("Cannot determine which version of Chromedriver "
"to download!")
latest_version = requests.get(
@@ -85,34 +95,85 @@ def main():
print("Found %s" % download_url)
elif name == "geckodriver" or name == "firefoxdriver":
latest_version = "v0.21.0"
- if "darwin" in sys.platform:
+ if "darwin" in sys_plat:
file_name = "geckodriver-%s-macos.tar.gz" % latest_version
- elif "linux" in sys.platform:
+ elif "linux" in sys_plat:
arch = platform.architecture()[0]
if "64" in arch:
file_name = "geckodriver-%s-linux64.tar.gz" % latest_version
else:
file_name = "geckodriver-%s-linux32.tar.gz" % latest_version
- elif "win32" in sys.platform:
+ elif "win32" in sys_plat:
file_name = "geckodriver-%s-win32.zip" % latest_version
- elif "win64" in sys.platform:
+ elif "win64" in sys_plat or "x64" in sys_plat:
file_name = "geckodriver-%s-win64.zip" % latest_version
else:
- raise Exception("Cannon determine which version of Geckodriver "
+ raise Exception("Cannot determine which version of Geckodriver "
"(Firefox Driver) to download!")
- download_url = ("http://github.com/mozilla/geckodriver/"
+ download_url = ("https://github.com/mozilla/geckodriver/"
"releases/download/"
"%s/%s" % (latest_version, file_name))
elif name == "edgedriver" or name == "microsoftwebdriver":
- if "win32" in sys.platform or "win64" in sys.platform:
- version_code = "F/8/A/F8AF50AB-3C3A-4BC4-8773-DC27B32988DD"
+ name = "edgedriver"
+ version_code = "F/8/A/F8AF50AB-3C3A-4BC4-8773-DC27B32988DD"
+ if "win32" in sys_plat or "win64" in sys_plat or "x64" in sys_plat:
file_name = "MicrosoftWebDriver.exe"
- download_url = ("https://download.microsoft.com/download/"
- "%s/%s" % (version_code, file_name))
else:
raise Exception("Sorry! Microsoft WebDriver / EdgeDriver is "
"only for Windows-based operating systems!")
+ download_url = ("https://download.microsoft.com/download/"
+ "%s/%s" % (version_code, file_name))
+ elif name == "iedriver":
+ major_version = "3.11"
+ full_version = "3.11.1"
+ if "win32" in sys_plat:
+ file_name = "IEDriverServer_Win32_%s.zip" % full_version
+ elif "win64" in sys_plat or "x64" in sys_plat:
+ file_name = "IEDriverServer_x64_%s.zip" % full_version
+ else:
+ raise Exception("Sorry! IEDriver is only for "
+ "Windows-based operating systems!")
+ download_url = ("http://selenium-release.storage.googleapis.com/"
+ "%s/%s" % (major_version, file_name))
+ elif name == "operadriver" or name == "operachromiumdriver":
+ name = "operadriver"
+ latest_version = "v.2.37"
+ if "darwin" in sys_plat:
+ file_name = "operadriver_mac64.zip"
+ platform_code = "mac64"
+ inner_folder = "operadriver_%s/" % platform_code
+ expected_contents = (['operadriver_mac64/',
+ 'operadriver_mac64/operadriver',
+ 'operadriver_mac64/sha512_sum'])
+ elif "linux" in sys_plat:
+ file_name = "operadriver_linux64.zip"
+ platform_code = "linux64"
+ inner_folder = "operadriver_%s/" % platform_code
+ expected_contents = (['operadriver_linux64/',
+ 'operadriver_linux64/operadriver',
+ 'operadriver_linux64/sha512_sum'])
+ elif "win32" in sys_plat:
+ file_name = "operadriver_win32.zip"
+ platform_code = "win32"
+ inner_folder = "operadriver_%s/" % platform_code
+ expected_contents = (['operadriver_win32/',
+ 'operadriver_win32/operadriver.exe',
+ 'operadriver_win32/sha512_sum'])
+ elif "win64" in sys_plat or "x64" in sys_plat:
+ file_name = "operadriver_win64.zip"
+ platform_code = "win64"
+ inner_folder = "operadriver_%s/" % platform_code
+ expected_contents = (['operadriver_win64/',
+ 'operadriver_win64/operadriver.exe',
+ 'operadriver_win64/sha512_sum'])
+ else:
+ raise Exception("Cannot determine which version of Operadriver "
+ "to download!")
+
+ download_url = ("https://github.com/operasoftware/operachromiumdriver/"
+ "releases/download/"
+ "%s/%s" % (latest_version, file_name))
else:
invalid_run_command()
@@ -135,6 +196,8 @@ def main():
zip_ref = zipfile.ZipFile(zip_file_path, 'r')
contents = zip_ref.namelist()
if len(contents) == 1:
+ if name == "operadriver":
+ raise Exception("Zip file for OperaDriver is missing content!")
for f_name in contents:
# remove existing version if exists
new_file = downloads_folder + '/' + str(f_name)
@@ -153,6 +216,46 @@ def main():
make_executable(new_file)
print("%s is now ready for use!" % new_file)
print("")
+ elif name == "operadriver":
+ if len(contents) != 3:
+ raise Exception("Unexpected content in OperaDriver Zip file!")
+ elif sorted(contents) != sorted(expected_contents):
+ raise Exception("Unexpected content in OperaDriver Zip file!")
+ # Zip file is valid. Proceed.
+ driver_path = None
+ driver_file = None
+ for f_name in contents:
+ # remove existing version if exists
+ str_name = str(f_name).split(inner_folder)[1]
+ new_file = downloads_folder + '/' + str_name
+ if str_name == "operadriver" or str_name == "operadriver.exe":
+ driver_file = str_name
+ driver_path = new_file
+ if os.path.exists(new_file):
+ os.remove(new_file)
+ if not driver_file or not driver_path:
+ raise Exception("Operadriver missing from Zip file!")
+ print('Extracting %s from %s ...' % (contents, file_name))
+ zip_ref.extractall(downloads_folder)
+ zip_ref.close()
+ os.remove(zip_file_path)
+ print('Unzip Complete!\n')
+ inner_driver = downloads_folder + '/' + inner_folder + driver_file
+ inner_sha = downloads_folder + '/' + inner_folder + "sha512_sum"
+ shutil.copyfile(inner_driver, driver_path)
+ print("%s saved!\n" % driver_path)
+ print("Making %s executable ..." % driver_path)
+ make_executable(driver_path)
+ print("%s is now ready for use!" % driver_path)
+ # clean up extra files
+ if os.path.exists(inner_driver):
+ os.remove(inner_driver)
+ if os.path.exists(inner_sha):
+ os.remove(inner_sha)
+ if os.path.exists(downloads_folder + '/' + inner_folder):
+ # only works if the directory is empty
+ os.rmdir(downloads_folder + '/' + inner_folder)
+ print("")
elif len(contents) == 0:
raise Exception("Zip file %s is empty!" % zip_file_path)
else:
@@ -172,7 +275,7 @@ def main():
tar.extractall(downloads_folder)
tar.close()
os.remove(tar_file_path)
- print('Untar Complete!\n')
+ print('Unzip Complete!\n')
for f_name in contents:
new_file = downloads_folder + '/' + str(f_name)
print("%s saved!\n" % new_file)
diff --git a/console_scripts/sb_mkdir.py b/console_scripts/sb_mkdir.py
index f8605cd781e..6381cdbb130 100755
--- a/console_scripts/sb_mkdir.py
+++ b/console_scripts/sb_mkdir.py
@@ -21,7 +21,7 @@ def invalid_run_command():
exp += " Usage:\n"
exp += " seleniumbase mkdir [DIRECTORY_NAME]\n"
exp += " Example:\n"
- exp += " seleniumbase mkdir gui_tests\n"
+ exp += " seleniumbase mkdir browser_tests\n"
exp += " Output:\n"
exp += " Creates a new folder for running SeleniumBase scripts.\n"
exp += " The new folder contains default config files,\n"
@@ -180,7 +180,8 @@ def main():
data.append("class BingTests(BaseCase):")
data.append("")
data.append(" def test_bing(self):")
- data.append(" self.open('https://www.bing.com/')")
+ data.append(" self.open('https://bing.com')")
+ data.append(" self.assert_text('Bing', Page.logo_box)")
data.append(" self.update_text(Page.search_box, 'github')")
data.append(" self.assert_element('li[query=\"github\"]')")
data.append(" self.click(Page.search_button)")
@@ -196,6 +197,7 @@ def main():
data = []
data.append("class Page(object):")
+ data.append(" logo_box = '#sbox div[class*=logo]'")
data.append(" search_box = 'input.b_searchbox'")
data.append(" search_button = 'input[name=\"go\"]'")
data.append(" search_results = '#b_results'")
@@ -213,7 +215,15 @@ def main():
data.append("class GoogleTests(BaseCase):")
data.append("")
data.append(" def test_google_dot_com(self):")
- data.append(" self.open('https://www.google.com')")
+ data.append(" self.open('https://google.com')")
+ data.append(" try:")
+ data.append(" # Remove the Privacy Checkup box if present.")
+ data.append(" self.assert_text('Privacy Checkup', "
+ "HomePage.dialog_box, timeout=3)")
+ data.append(" self.click('link=NO, THANKS')")
+ data.append(" except Exception:")
+ data.append(" # Google may have removed it. Continue test.")
+ data.append(" pass")
data.append(" self.assert_element(HomePage.search_button)")
data.append(
" self.assert_element(HomePage.feeling_lucky_button)")
@@ -234,6 +244,7 @@ def main():
data = []
data.append("class HomePage(object):")
+ data.append(" dialog_box = '[role=\"dialog\"] div'")
data.append(" search_box = 'input[title=\"Search\"]'")
data.append(" search_button = 'input[value=\"Google Search\"]'")
data.append(
diff --git a/examples/boilerplates/samples/bing_objects.py b/examples/boilerplates/samples/bing_objects.py
index 80a895b79e6..49090545512 100755
--- a/examples/boilerplates/samples/bing_objects.py
+++ b/examples/boilerplates/samples/bing_objects.py
@@ -3,6 +3,7 @@
'''
class Page(object):
+ logo_box = '#sbox div[class*=logo]'
search_box = 'input.b_searchbox'
search_button = 'input[name="go"]'
search_results = '#b_results'
diff --git a/examples/boilerplates/samples/bing_test.py b/examples/boilerplates/samples/bing_test.py
index 8e8ddc570b6..9811b3f758e 100755
--- a/examples/boilerplates/samples/bing_test.py
+++ b/examples/boilerplates/samples/bing_test.py
@@ -9,7 +9,8 @@
class BingTests(BaseCase):
def test_bing(self):
- self.open('https://www.bing.com/')
+ self.open('https://bing.com')
+ self.assert_text('Bing', Page.logo_box)
self.update_text(Page.search_box, 'github')
self.assert_element('li[query="github"]')
self.click(Page.search_button)
diff --git a/examples/boilerplates/samples/google_objects.py b/examples/boilerplates/samples/google_objects.py
index c3bb454599d..067711e1236 100755
--- a/examples/boilerplates/samples/google_objects.py
+++ b/examples/boilerplates/samples/google_objects.py
@@ -4,6 +4,7 @@
class HomePage(object):
+ dialog_box = '[role="dialog"] div'
search_box = 'input[title="Search"]'
search_button = 'input[value="Google Search"]'
feeling_lucky_button = '''input[value="I'm Feeling Lucky"]'''
diff --git a/examples/boilerplates/samples/google_test.py b/examples/boilerplates/samples/google_test.py
index 4160ef55162..48121fcdefb 100755
--- a/examples/boilerplates/samples/google_test.py
+++ b/examples/boilerplates/samples/google_test.py
@@ -9,7 +9,13 @@
class GoogleTests(BaseCase):
def test_google_dot_com(self):
- self.open('https://www.google.com')
+ self.open('https://google.com')
+ try:
+ # Remove the Privacy Checkup box if present.
+ self.assert_text('Privacy Checkup', HomePage.dialog_box, timeout=3)
+ self.click('link=NO, THANKS')
+ except Exception:
+ pass # Google may have removed the Privacy Checkup. Continue test.
self.assert_element(HomePage.search_button)
self.assert_element(HomePage.feeling_lucky_button)
self.update_text(HomePage.search_box, 'github\n')
diff --git a/examples/master_qa/ReadMe.md b/examples/master_qa/ReadMe.md
new file mode 100755
index 00000000000..66ef45cea00
--- /dev/null
+++ b/examples/master_qa/ReadMe.md
@@ -0,0 +1,7 @@
+## MasterQA
+
+MasterQA combines web automation with manual verification to create a flexible testing experience that greatly enhances pure manual QA.
+
+### More info on MasterQA can be found [here](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/masterqa/ReadMe.md).
+
+
diff --git a/help_docs/webdriver_installation.md b/help_docs/webdriver_installation.md
index e8b4dcf27d6..a19d07a751a 100755
--- a/help_docs/webdriver_installation.md
+++ b/help_docs/webdriver_installation.md
@@ -21,6 +21,8 @@ Here's where you can go to manually install web drivers from the source:
* For Safari, get [Safari Driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/using_safari_driver.md) on your System PATH.
+* For Opera, get [Opera Chromium Driver](https://github.com/operasoftware/operachromiumdriver/releases) on your System PATH..
+
* For PhantomJS headless browser automation, get [PhantomJS](http://phantomjs.org/download.html) on your System PATH. (NOTE: PhantomJS is no longer officially supported by SeleniumHQ)
**Mac**:
diff --git a/integrations/selenium_ide/convert_ide.py b/integrations/selenium_ide/convert_ide.py
index 484c07afdb2..6a971ce9485 100755
--- a/integrations/selenium_ide/convert_ide.py
+++ b/integrations/selenium_ide/convert_ide.py
@@ -319,6 +319,7 @@ def main():
if data:
whitespace = data.group(1)
xpath = '%s' % data.group(2)
+ uni = ""
if '(u"' in line:
uni = "u"
has_unicode = True
diff --git a/seleniumbase/config/settings.py b/seleniumbase/config/settings.py
index 83c37a3a528..b777bd3ff16 100755
--- a/seleniumbase/config/settings.py
+++ b/seleniumbase/config/settings.py
@@ -60,7 +60,7 @@
# 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". (Can be overwritten by using "--demo_sleep=TIME".)
-DEFAULT_DEMO_MODE_TIMEOUT = 1.0
+DEFAULT_DEMO_MODE_TIMEOUT = 0.5
# Number of times to repeat the demo_mode highlight animation.
# Each loop is about 0.18 seconds. (Override by using "--highlights=TIMES".)
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index bb848bb2f64..3b365b8c5c2 100755
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -12,16 +12,20 @@
from seleniumbase.fixtures import page_utils
import drivers # webdriver storage folder for SeleniumBase
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
+PLATFORM = sys.platform
LOCAL_CHROMEDRIVER = None
LOCAL_GECKODRIVER = None
LOCAL_EDGEDRIVER = None
-if "darwin" in sys.platform or "linux" in sys.platform:
+LOCAL_OPERADRIVER = None
+if "darwin" in PLATFORM or "linux" in PLATFORM:
LOCAL_CHROMEDRIVER = DRIVER_DIR + '/chromedriver'
LOCAL_GECKODRIVER = DRIVER_DIR + '/geckodriver'
-elif "win32" in sys.platform or "win64" in sys.platform:
+ LOCAL_OPERADRIVER = DRIVER_DIR + '/operadriver'
+elif "win32" in PLATFORM or "win64" in PLATFORM or "x64" in PLATFORM:
LOCAL_EDGEDRIVER = DRIVER_DIR + '/MicrosoftWebDriver.exe'
LOCAL_CHROMEDRIVER = DRIVER_DIR + '/chromedriver.exe'
LOCAL_GECKODRIVER = DRIVER_DIR + '/geckodriver.exe'
+ LOCAL_OPERADRIVER = DRIVER_DIR + '/operadriver.exe'
else:
# Cannot determine system
pass # SeleniumBase will use web drivers from the System PATH by default
@@ -217,6 +221,11 @@ def get_remote_driver(browser_name, headless, servername, port, proxy_string):
command_executor=address,
desired_capabilities=(
webdriver.DesiredCapabilities.SAFARI))
+ elif browser_name == constants.Browser.OPERA:
+ return webdriver.Remote(
+ command_executor=address,
+ desired_capabilities=(
+ webdriver.DesiredCapabilities.OPERA))
elif browser_name == constants.Browser.PHANTOM_JS:
with warnings.catch_warnings():
# Ignore "PhantomJS has been deprecated" UserWarning
@@ -282,6 +291,12 @@ def get_local_driver(browser_name, headless, proxy_string):
return webdriver.Edge(capabilities=edge_capabilities)
elif browser_name == constants.Browser.SAFARI:
return webdriver.Safari()
+ elif browser_name == constants.Browser.OPERA:
+ if LOCAL_OPERADRIVER and os.path.exists(LOCAL_OPERADRIVER):
+ make_driver_executable_if_not(LOCAL_OPERADRIVER)
+ return webdriver.Opera(executable_path=LOCAL_OPERADRIVER)
+ else:
+ return webdriver.Opera()
elif browser_name == constants.Browser.PHANTOM_JS:
with warnings.catch_warnings():
# Ignore "PhantomJS has been deprecated" UserWarning
diff --git a/seleniumbase/fixtures/constants.py b/seleniumbase/fixtures/constants.py
index fdc06ae1131..156f476cb56 100755
--- a/seleniumbase/fixtures/constants.py
+++ b/seleniumbase/fixtures/constants.py
@@ -18,33 +18,37 @@ class Files:
class ValidBrowsers:
- valid_browsers = ["firefox", "ie", "edge", "safari", "chrome", "phantomjs"]
+ valid_browsers = (
+ ["chrome", "edge", "firefox", "ie", "opera", "phantomjs", "safari"])
class Browser:
+ GOOGLE_CHROME = "chrome"
+ EDGE = "edge"
FIREFOX = "firefox"
INTERNET_EXPLORER = "ie"
- EDGE = "edge"
- SAFARI = "safari"
- GOOGLE_CHROME = "chrome"
+ OPERA = "opera"
PHANTOM_JS = "phantomjs"
+ SAFARI = "safari"
VERSION = {
+ "chrome": None,
+ "edge": None,
"firefox": None,
"ie": None,
- "edge": None,
- "safari": None,
- "chrome": None,
- "phantomjs": None
+ "opera": None,
+ "phantomjs": None,
+ "safari": None
}
LATEST = {
+ "chrome": None,
+ "edge": None,
"firefox": None,
"ie": None,
- "edge": None,
- "safari": None,
- "chrome": None,
- "phantomjs": None
+ "opera": None,
+ "phantomjs": None,
+ "safari": None
}
diff --git a/seleniumbase/masterqa/ReadMe.md b/seleniumbase/masterqa/ReadMe.md
index f28d8b82b70..ee0a58a5790 100755
--- a/seleniumbase/masterqa/ReadMe.md
+++ b/seleniumbase/masterqa/ReadMe.md
@@ -1,6 +1,6 @@
# MasterQA
-### MasterQA combines SeleniumBase automation with manual verification to greatly improve the productivity and sanity of QA teams. (**Works only for Chrome right now.**)
+### MasterQA combines SeleniumBase automation with manual verification to greatly improve the productivity and sanity of QA teams. (**Works only for Chrome & Edge right now.**)

diff --git a/setup.py b/setup.py
index d1d2e1fd215..3d562531f9c 100755
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
setup(
name='seleniumbase',
- version='1.13.2',
+ version='1.13.3',
description='Web Automation & Testing Framework - http://seleniumbase.com',
long_description='Web Automation and Testing Framework - seleniumbase.com',
platforms='Mac * Windows * Linux * Docker',