diff --git a/README.md b/README.md index 331cb7ab..65c212bb 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -Browser automated testing with Python done efficiently.
+Automated Web-UI testing with Python, made awesome.
[![](https://img.shields.io/pypi/v/seleniumbase.svg)](https://pypi.python.org/pypi/seleniumbase) [![Build Status](https://travis-ci.org/seleniumbase/SeleniumBase.svg?branch=master)](https://travis-ci.org/seleniumbase/SeleniumBase)

@@ -65,7 +65,7 @@ SeleniumBase was originally built for [testing HubSpot's platform](https://produ ## Get Started: -(Before installation, **[install Python](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/install_python_pip_git.md)** and **[get WebDriver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/webdriver_installation.md)** on your system PATH.) +Before installation, **[install Python](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/install_python_pip_git.md)** and **[get a WebDriver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/webdriver_installation.md)** on your system PATH. ### ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") **Step 1:** Clone SeleniumBase @@ -79,30 +79,33 @@ cd SeleniumBase (A [Git](https://git-scm.com/) GUI tool like [SourceTree](http://www.sourcetreeapp.com/) may help.) -### ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") **Step 2:** Create a Virtual Env. +### ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") **Step 2:** Create a Virtual Environment (OPTIONAL) To learn how to create a Python virtual environment, [see this ReadMe](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/virtualenv_instructions.md). ### ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") **Step 3:** Install SeleniumBase -Run the following commands from the top-level SeleniumBase folder: +If you're installing SeleniumBase from a cloned copy on your machine, use: ``` pip install -r requirements.txt python setup.py develop ``` -If you already have an older version installed, add ``--upgrade`` to the ``pip`` command in order to update previously-installed Python packages. If you're not using a virtual environment, you may need to add ``--user`` to the end of your ``pip`` command. (Do that if you see any errors during installation.) - -(If you can't install ChromeDriver or GeckoDriver, use `server_requirements.txt` and `server_setup.py` to run tests using the older Selenium 2.53.6 on a version of Firefox that's older than 47.) - -To install SeleniumBase from the [Python Package Index](https://pypi.python.org/pypi/seleniumbase) use the following command: (Add ``--upgrade`` and/or ``--user`` to the command as needed.) +If you're installing SeleniumBase from the [Python Package Index](https://pypi.python.org/pypi/seleniumbase), use: ```bash pip install seleniumbase ``` -(NOTE: If you're using Python 3.x instead of Python 2.7, use ``pip3`` in place of ``pip`` and ``python3`` in place of ``python`` in the above commands.) +If you're installing SeleniumBase directly from GitHub, use: +```bash +pip install -e git+https://github.com/seleniumbase/SeleniumBase.git@master#egg=seleniumbase +``` + +(If you already have an older version installed, you may want to add ``--upgrade`` to your ``pip`` command to update existing Python packages. If you're not using a virtual environment, you may need to add ``--user`` to your ``pip`` command if you're getting errors during installation.) + +(If you want to use Python 3.x instead of Python 2.7, use ``pip3`` in place of ``pip`` and ``python3`` in place of ``python``.) @@ -119,12 +122,11 @@ class MyTestClass(BaseCase): self.open('http://xkcd.com/353/') self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') - text = self.get_text("div center") - self.assertTrue("reuse any of my drawings" in text) - self.open('http://xkcd.com/1481/') - title = self.get_attribute('#comic img', 'title') - self.assertTrue('connections to the server' in title) - self.click_link_text('Blag') + self.assert_text('free to copy', 'div center') + self.open("http://xkcd.com/1481/") + title = self.get_attribute("#comic img", "title") + self.assertTrue("86,400 seconds per day" in title) + self.click('link=Blag') self.assert_text('The blag of the webcomic', 'h2') self.update_text('input#s', 'Robots!\n') self.assert_text('Hooray robots!', '#content') diff --git a/_config.yml b/_config.yml index b78f25a4..c8869ac3 100644 --- a/_config.yml +++ b/_config.yml @@ -1,3 +1,3 @@ theme: jekyll-theme-cayman title: SeleniumBase -description: Test Automation Framework \ No newline at end of file +description: Automated Web-UI Testing Framework \ No newline at end of file diff --git a/examples/my_first_test.py b/examples/my_first_test.py index 05cd492d..a6377884 100755 --- a/examples/my_first_test.py +++ b/examples/my_first_test.py @@ -7,13 +7,12 @@ class MyTestClass(BaseCase): 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 - text = self.get_text("div center") # Grab text from page element - self.assertTrue("reuse any of my drawings" in text) - self.open('http://xkcd.com/1481/') - title = self.get_attribute('#comic img', 'title') # Grab an attribute - self.assertTrue('connections to the server' in title) - self.click_link_text('Blag') # Click on link with the text - self.assert_text('The blag of the webcomic', 'h2') # Assert text in h2 + self.assert_text('free to copy', 'div center') # Assert text on page + self.open("http://xkcd.com/1481/") + title = self.get_attribute("#comic img", "title") # Grab an attribute + self.assertTrue("86,400 seconds per day" in title) + self.click('link=Blag') # Click on link with the text + self.assert_text('The blag of the webcomic', 'h2') 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/') @@ -26,33 +25,25 @@ class MyTestClass(BaseCase): # **** NOTES / USEFUL INFO **** # # 1. By default, CSS Selectors are used to identify elements. - # You can use other identification options like PARTIAL_LINK_TEXT: + # Other options include: "LINK_TEXT", "PARTIAL_LINK_TEXT", "NAME", + # "CLASS_NAME", and "ID", but most of those can be expressed as CSS. + # Here's an example of changing the "by": # [ # 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. + # web page and select "Inspect" to see the CSS in the html. # # 2. Most methods have the optional `timeout` argument. Ex: # [ - # self.get_text('center', timeout=15) + # self.get_text('div 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 @@ -62,33 +53,28 @@ class MyTestClass(BaseCase): # # 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) + # self.assert_text('free to copy', 'div center') # ] - # Can be simplified to: + # Is the same as: # [ - # self.assert_text('The blag of the webcomic', 'header_text') + # text = self.get_text("div center") + # self.assertTrue("free to copy" in text) # ] - # - # The following line: + # Or: # [ - # title = self.get_attribute('#comic img', 'title') - # ] - # Can also be written as: - # [ - # element = self.find_element('#comic img') - # title = element.get_attribute('title') + # text = self.find_element('div center').text + # assert("free to copy" in text) # ] # # And the following line: # [ - # text = self.get_text("div center") + # title = self.get_attribute("#comic img", "title") # ] # Can also be written as: # [ - # text = self.find_element('div center').text + # element = self.find_element("#comic img") + # title = element.get_attribute("title") # ] - # ...and in many more ways! # # For backwards-compatibilty, some methods have multiple names. # Ex: wait_for_element_visible() is the same as find_element(). diff --git a/help_docs/install.md b/help_docs/install.md new file mode 100755 index 00000000..5fda7afe --- /dev/null +++ b/help_docs/install.md @@ -0,0 +1,22 @@ +## ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") SeleniumBase Installation + +If you're installing SeleniumBase from a cloned copy on your machine, use: +``` +pip install -r requirements.txt + +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 +``` + +If you're installing SeleniumBase directly from GitHub, use: +```bash +pip install -e git+https://github.com/seleniumbase/SeleniumBase.git@master#egg=seleniumbase +``` + +(If you already have an older version installed, you may want to add ``--upgrade`` to your ``pip`` command to update existing Python packages. If you're not using a virtual environment, you may need to add ``--user`` to your ``pip`` command if you're getting errors during installation.) + +(If you want to use Python 3.x instead of Python 2.7, use ``pip3`` in place of ``pip`` and ``python3`` in place of ``python``.) diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index 3b3c4699..70843c33 100755 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -254,6 +254,9 @@ self.switch_to_default_content() self.save_screenshot(name, folder=None) +self.get_new_driver(browser=None, headless=None, servername=None, port=None, + proxy_string=None) + ######## self.delayed_assert_element(selector, by=By.CSS_SELECTOR, diff --git a/help_docs/mysql_installation.md b/help_docs/mysql_installation.md index 667e7e5a..1a675e86 100755 --- a/help_docs/mysql_installation.md +++ b/help_docs/mysql_installation.md @@ -31,7 +31,7 @@ You can use the [testcaserepository.sql](https://github.com/seleniumbase/Seleniu If you were able to successfully install MySQL, you can now install the remaining MySQL requirements: ```bash -pip install -r server_requirements.txt +pip install -r 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.) @@ -44,7 +44,7 @@ You'll want to update your [settings.py](https://github.com/seleniumbase/Seleniu 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 +nosetests my_first_test.py --with-db_reporting ``` #### Windows mysql-python troubleshooting: diff --git a/help_docs/webdriver_installation.md b/help_docs/webdriver_installation.md index e48e3375..44b366a3 100755 --- a/help_docs/webdriver_installation.md +++ b/help_docs/webdriver_installation.md @@ -3,17 +3,19 @@ To run automation on various web browsers, you'll need to download a driver file for each one and place it on your System **[PATH](http://java.com/en/download/help/path.xml)**. On a Mac, ``/usr/local/bin`` is a good spot. On Windows, make sure you set the System Path under Environment Variables to include the location where you placed the driver files. You may want to download newer versions of drivers as they become available. -* For Chrome, get [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) on your System Path. +* For Chrome, get [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) on your System PATH. -* For Firefox, get [Geckodriver](https://github.com/mozilla/geckodriver/releases) on your System Path. +* For Firefox, get [Geckodriver](https://github.com/mozilla/geckodriver/releases) on your System PATH. -* For Microsoft Edge, get [Edge Driver (Microsoft WebDriver)](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) on your System Path. +* For Microsoft Edge, get [Edge Driver (Microsoft WebDriver)](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) on your System PATH. -* For Safari, get [Safari Driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/using_safari_driver.md) on your System Path. +* For Safari, get [Safari Driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/using_safari_driver.md) 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) +* 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: +(NOTE: If you can't get a WebDriver on your system PATH, you'll need to edit ``requirements.txt`` and ``setup.py`` to use selenium==2.53.6 instead of selenium 3.x, which will limit you to running tests on [Firefox 46.x](https://ftp.mozilla.org/pub/firefox/releases/46.0.1/) & earlier.) + +**Mac**: * On a Mac, you can install drivers more easily by using ``brew`` (aka ``homebrew``), but you have to install that first. [Brew installation instructions are here](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/install_python_pip_git.md). @@ -23,7 +25,7 @@ brew install chromedriver brew install geckodriver ``` -(NOTE: If your existing version of chromedriver is less than 2.36, **upgrading is recommended!**) +(NOTE: If your existing version of chromedriver is less than 2.37, **upgrading is required!**) ```bash brew upgrade chromedriver @@ -31,7 +33,7 @@ brew upgrade chromedriver brew upgrade geckodriver ``` -Linux: +**Linux**: ```bash wget http://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip diff --git a/integrations/node_js/my_first_test.py b/integrations/node_js/my_first_test.py index 4ad8bce2..034a885d 100755 --- a/integrations/node_js/my_first_test.py +++ b/integrations/node_js/my_first_test.py @@ -7,11 +7,10 @@ class MyTestClass(BaseCase): self.open('http://xkcd.com/353/') self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') - text = self.get_text("div center") - self.assertTrue("reuse any of my drawings" in text) - self.open('http://xkcd.com/1481/') - title = self.get_attribute('#comic img', 'title') - self.assertTrue('connections to the server' in title) + self.assert_text('free to copy', 'div center') + self.open("http://xkcd.com/1481/") + title = self.get_attribute("#comic img", "title") + self.assertTrue("86,400 seconds per day" in title) self.click('link=Blag') self.assert_text('The blag of the webcomic', 'h2') self.update_text('input#s', 'Robots!\n') diff --git a/requirements.txt b/requirements.txt index a0598622..2ebd060c 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -pip>=9.0.1 -setuptools>=38.5.2 +pip>=9.0.3 +setuptools>=39.0.1 ipython==5.5.0 selenium==3.8.1 nose==1.3.7 -pytest==3.4.2 +pytest==3.5.0 pytest-html==1.16.1 pytest-xdist==1.22.2 six==1.10.0 @@ -13,6 +13,6 @@ BeautifulSoup4==4.6.0 unittest2==1.1.0 chardet==3.0.4 boto==2.48.0 -ipdb==0.10.2 +ipdb==0.11 pyvirtualdisplay==0.2.1 -e . diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index bdae3950..9f26cca5 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -54,7 +54,6 @@ try: except Exception: # Selenium 2 (Keep compatibility with seleneium 2.53.6 if still being used) ENI_Exception = selenium_exceptions.ElementNotSelectableException -from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select @@ -69,15 +68,13 @@ class BaseCase(unittest.TestCase): def __init__(self, *args, **kwargs): super(BaseCase, self).__init__(*args, **kwargs) - try: - self.driver = WebDriver() - except Exception: - pass + self.driver = None self.environment = None self._last_url_of_delayed_assert = "data:," self._page_check_count = 0 self._page_check_failures = [] self._html_report_extra = [] + self._extra_drivers = [] def open(self, url): self.driver.get(url) @@ -1438,6 +1435,39 @@ class BaseCase(unittest.TestCase): def save_screenshot(self, name, folder=None): return page_actions.save_screenshot(self.driver, name, folder) + def get_new_driver(self, browser=None, headless=None, + servername=None, port=None, + proxy_string=None): + """ This method spins up an extra browser for tests that require + more than one. The first browser is already provided by tests + that import base_case.BaseCase from seleniumbase. """ + if browser is None: + browser = self.browser + if headless is None: + headless = self.headless + if servername is None: + servername = self.servername + if port is None: + port = self.port + if proxy_string is None: + proxy_string = self.proxy_string + use_grid = False + if servername != "localhost": + # Use Selenium Grid (Use --server=127.0.0.1 for localhost Grid) + use_grid = True + valid_browsers = constants.ValidBrowsers.valid_browsers + if browser not in valid_browsers: + raise Exception("Browser: {%s} is not a valid browser option. " + "Valid options = {%s}" % (browser, valid_browsers)) + new_driver = browser_launcher.get_driver(browser, + headless, + use_grid, + servername, + port, + proxy_string) + self._extra_drivers.append(new_driver) + return new_driver + ############ def _get_new_timeout(self, timeout): @@ -1909,8 +1939,14 @@ class BaseCase(unittest.TestCase): if self.with_page_source: log_helper.log_page_source( test_logpath, self.driver) - # Finally close the browser try: + # Finally close the browser + if self._extra_drivers: + for extra_driver in self._extra_drivers: + try: + extra_driver.quit() + except Exception: + pass # Extra driver was already quit self.driver.quit() except AttributeError: pass @@ -1957,6 +1993,12 @@ class BaseCase(unittest.TestCase): # Using Nosetests try: # Finally close the browser + if self._extra_drivers: + for extra_driver in self._extra_drivers: + try: + extra_driver.quit() + except Exception: + pass # Extra driver was already quit self.driver.quit() except AttributeError: pass diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py index 6cf77540..72e4bf67 100644 --- a/seleniumbase/plugins/pytest_plugin.py +++ b/seleniumbase/plugins/pytest_plugin.py @@ -11,7 +11,7 @@ def pytest_addoption(parser): 'SeleniumBase specific configuration options') parser.addoption('--browser', action="store", dest='browser', - choices=constants.Browser.VERSION.keys(), + choices=constants.ValidBrowsers.valid_browsers, default=constants.Browser.GOOGLE_CHROME, help="""Specifies the web browser to use. Default: Chrome. If you want to use Firefox, explicitly indicate that. diff --git a/server_requirements.txt b/server_requirements.txt deleted file mode 100755 index b63eba78..00000000 --- a/server_requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -pip>=9.0.1 -setuptools>=38.5.2 -ipython==5.5.0 -selenium==2.53.6 -nose==1.3.7 -pytest==3.4.2 -pytest-html==1.16.1 -pytest-xdist==1.22.2 -six==1.10.0 -flake8==3.5.0 -requests==2.18.4 -BeautifulSoup4==4.6.0 -unittest2==1.1.0 -chardet==3.0.4 -boto==2.48.0 -ipdb==0.10.2 -pyvirtualdisplay==0.2.1 diff --git a/server_setup.py b/server_setup.py deleted file mode 100755 index 7ff4570d..00000000 --- a/server_setup.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -The setup package to install SeleniumBase dependencies and plugins -(Uses the older Selenium 2.53.6 for compatibility reasons) -""" - -from setuptools import setup, find_packages # noqa - -setup( - name='seleniumbase', - version='1.7.5', - description='Web Automation & Testing Framework - http://seleniumbase.com', - long_description='Web Automation and Testing Framework - seleniumbase.com', - platforms='Mac * Windows * Linux * Docker', - url='http://seleniumbase.com', - author='Michael Mintz', - author_email='mdmintz@gmail.com', - maintainer='Michael Mintz', - license='The MIT License', - install_requires=[ - 'pip>=9.0.1', - 'setuptools>=38.5.2', - 'ipython==5.5.0', - 'selenium==2.53.6', - 'nose==1.3.7', - 'pytest==3.4.2', - 'pytest-html==1.16.1', - 'pytest-xdist==1.22.2', - 'six==1.10.0', - 'flake8==3.5.0', - 'requests==2.18.4', - 'BeautifulSoup4==4.6.0', - 'unittest2==1.1.0', - 'chardet==3.0.4', - 'boto==2.48.0', - 'ipdb==0.10.2', - 'pyvirtualdisplay==0.2.1', - ], - packages=['seleniumbase', - 'seleniumbase.core', - 'seleniumbase.plugins', - 'seleniumbase.fixtures', - 'seleniumbase.masterqa', - 'seleniumbase.common', - 'seleniumbase.config'], - entry_points={ - 'nose.plugins': [ - 'base_plugin = seleniumbase.plugins.base_plugin:Base', - 'selenium = seleniumbase.plugins.selenium_plugin:SeleniumBrowser', - 'page_source = seleniumbase.plugins.page_source:PageSource', - 'screen_shots = seleniumbase.plugins.screen_shots:ScreenShots', - 'test_info = seleniumbase.plugins.basic_test_info:BasicTestInfo', - ('db_reporting = ' - 'seleniumbase.plugins.db_reporting_plugin:DBReporting'), - 's3_logging = seleniumbase.plugins.s3_logging_plugin:S3Logging', - ('hipchat_reporting = seleniumbase.plugins' - '.hipchat_reporting_plugin:HipchatReporting'), - ], - 'pytest11': ['seleniumbase = seleniumbase.plugins.pytest_plugin'] - } - ) - -# print(os.system("cat seleniumbase.egg-info/PKG-INFO")) -print("\n*** SeleniumBase Installation Complete! ***\n") diff --git a/setup.py b/setup.py index 9bb41bcc..e6c075ce 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ """ The setup package to install SeleniumBase dependencies and plugins -(Uses the newer Selenium 3.8.1) +(Uses selenium 3.x and is compatible with Python 2.7+ and Python 3.4+) """ from setuptools import setup, find_packages # noqa setup( name='seleniumbase', - version='1.7.5', + version='1.8.0', description='Web Automation & Testing Framework - http://seleniumbase.com', long_description='Web Automation and Testing Framework - seleniumbase.com', platforms='Mac * Windows * Linux * Docker', @@ -17,12 +17,12 @@ setup( maintainer='Michael Mintz', license='The MIT License', install_requires=[ - 'pip>=9.0.1', - 'setuptools>=38.5.2', + 'pip>=9.0.3', + 'setuptools>=39.0.1', 'ipython==5.5.0', 'selenium==3.8.1', 'nose==1.3.7', - 'pytest==3.4.2', + 'pytest==3.5.0', 'pytest-html==1.16.1', 'pytest-xdist==1.22.2', 'six==1.10.0', @@ -32,7 +32,7 @@ setup( 'unittest2==1.1.0', 'chardet==3.0.4', 'boto==2.48.0', - 'ipdb==0.10.2', + 'ipdb==0.11', 'pyvirtualdisplay==0.2.1', ], packages=['seleniumbase',