diff --git a/.gitignore b/.gitignore index fa82efcbca0..ac5ae9a6653 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ nosetests.xml # Other selenium-server-standalone.jar +geckodriver.log downloaded_files archived_files logs diff --git a/.travis.yml b/.travis.yml index d5c79d27668..8f6bc894a71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,13 @@ python: - "2.7" install: - "pip install --upgrade pip" - - "pip install -r requirements.txt" - - "python setup.py install" + - "pip install -r server_requirements.txt" + - "python server_setup.py install" before_script: + - "flake8 seleniumbase/*.py" - "flake8 seleniumbase/*/*.py" + - "flake8 seleniumbase/*/*/*.py" + - "flake8 seleniumbase/*/*/*/*.py" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" script: diff --git a/Dockerfile b/Dockerfile index 7c391245220..b2db1640597 100755 --- a/Dockerfile +++ b/Dockerfile @@ -73,6 +73,17 @@ RUN apt-get -qy --no-install-recommends install \ && ln -s /opt/firefox/firefox /usr/bin/firefox \ && rm -f /tmp/firefox-esr.tar.bz2 +#====================================== +# Install Geckodriver / Firefox Driver +#====================================== +RUN export BASE_URL=https://github.com/mozilla/geckodriver/releases/download \ + && export VERSION=$(curl -sL \ + https://api.github.com/repos/mozilla/geckodriver/releases/latest | \ + grep tag_name | cut -d '"' -f 4) \ + && curl -sL \ + $BASE_URL/$VERSION/geckodriver-$VERSION-linux64.tar.gz | tar -xz \ +&& mv geckodriver /usr/local/bin/geckodriver + #=================== # Install PhantomJS #=================== @@ -96,9 +107,12 @@ RUN exec "$@" #===================== COPY seleniumbase /SeleniumBase/seleniumbase/ COPY examples /SeleniumBase/examples/ -COPY requirements.txt /SeleniumBase/requirements.txt -COPY setup.py /SeleniumBase/setup.py -RUN cd /SeleniumBase && ls && pip install -r requirements.txt +COPY docker_requirements.txt /SeleniumBase/docker_requirements.txt +COPY server_setup.py /SeleniumBase/server_setup.py +RUN pip install --upgrade pip +RUN pip install --upgrade setuptools +RUN cd /SeleniumBase && ls && pip install -r docker_requirements.txt +RUN cd /SeleniumBase && python server_setup.py install #========================================== # Create entrypoint and grab example tests diff --git a/README.md b/README.md index edf1f3d1cb3..60aacf1eb8e 100755 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![pypi](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) [![GitHub stars](https://img.shields.io/github/stars/seleniumbase/seleniumbase.svg "GitHub stars")](https://github.com/seleniumbase/SeleniumBase/stargazers) [![Python version](https://img.shields.io/badge/python-2.7-22AADD.svg "Python version")](https://docs.python.org/2/) [![MIT License](http://img.shields.io/badge/license-MIT-22BBCC.svg "MIT License")](https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE) [![Join the chat at https://gitter.im/seleniumbase/SeleniumBase](https://badges.gitter.im/seleniumbase/SeleniumBase.svg)](https://gitter.im/seleniumbase/SeleniumBase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -**Automation Platform for Testing and Business** +**Web Automation Platform for Testing and More** -(And the power to [speed up your manual testing](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/masterqa/ReadMe.md)) +(And the power to [speed up manual testing](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/masterqa/ReadMe.md)) ![](http://cdn2.hubspot.net/hubfs/100006/images/sb_demo.gif "SeleniumBase") @@ -29,13 +29,19 @@ SeleniumBase makes it easy to automate tedious business tasks. (*To learn about * To install ``python``, ``pip``, ``git``, and either ``virtualenv`` or ``virtualenvwrapper``, **[follow these instructions](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/requirements_installation.md)**. -* Download web browsers such as [Chrome](https://www.google.com/chrome/browser/desktop/index.html) (or [Chromium](https://download-chromium.appspot.com/)) and also [Firefox v46.*](https://ftp.mozilla.org/pub/firefox/releases/46.0.1/) (or [Firefox ESR](https://www.mozilla.org/en-US/firefox/organizations/)) because Firefox versions 47.0 and above are no longer compatible with normal Selenium WebDriver. (If you go with Firefox 46.*, make sure to turn off auto-updates or else you'll be back at v47+ quickly!) +* Download web browsers such as [Chrome](https://www.google.com/chrome/browser/desktop/index.html) (or [Chromium](https://download-chromium.appspot.com/)) and [Firefox](https://www.mozilla.org/firefox/new/). -(NOTE: Firefox versions 47.0 and above are no longer compatible with normal Selenium WebDriver. [Get Firefox 46.*](https://ftp.mozilla.org/pub/firefox/releases/46.0.1/) instead, or you can [Get Firefox ESR](https://www.mozilla.org/en-US/firefox/organizations/). (If you go with Firefox 46.*, make sure to turn off auto-updates or else you'll be back at v47 quickly!) For more information regarding this, [read this post](http://stackoverflow.com/questions/37693106/selenium-2-53-not-working-on-firefox-47) from Stack Overflow. There's a [new version of Firefox driver](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/WebDriver) coming soon. +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)**: -* If you want to run automation on browsers other than Firefox, you'll need to download [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/downloads), [PhantomJS](http://phantomjs.org/download.html), [Edge Driver (Microsoft WebDriver)](https://www.microsoft.com/en-us/download/details.aspx?id=48212), and/or [Safari Driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/using_safari_driver.md) separately. (Firefox drivers come with Selenium by default.) +* For Chrome, get [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) on your System Path. -* For everything you download (such as ``pip`` and ``Chromedriver``) make sure those files get on your system [PATH](http://java.com/en/download/help/path.xml). (``Environmental Variables`` on a Windows machine) +* 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 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. Mac: @@ -124,7 +130,7 @@ nosetests my_first_test.py --with-selenium --browser=firefox -s nosetests my_first_test.py --with-selenium --browser=phantomjs -s ``` -After the test completes, in the console output you'll see a dot (``.``) on a new line, representing a passing test. (On test failures you'll see an ``F`` instead, and on test errors you'll see an ``E``). It looks more like a moving progress bar when you're running a ton of unit tests side by side. This is part of nosetests. After all tests complete (in this case there is only one), you'll see the "``Ran 1 test in ...``" line, followed by an "``OK``" if all nosetests passed. The ``--with-selenium`` option is required for running GUI tests. If no browser is specified, Firefox will become the default. The ``-s`` option is optional, and that makes sure that any standard output is printed immediately on the command line when tests have print statements in them, which makes debugging much easier. +After the test completes, in the console output you'll see a dot (``.``) on a new line, representing a passing test. (On test failures you'll see an ``F`` instead, and on test errors you'll see an ``E``). It looks more like a moving progress bar when you're running a ton of unit tests side by side. This is part of nosetests. After all tests complete (in this case there is only one), you'll see the "``Ran 1 test in ...``" line, followed by an "``OK``" if all nosetests passed. The ``--with-selenium`` option is required for running GUI tests. If no browser is specified, Chrome will become the default. The ``-s`` option is optional, and that makes sure that any standard output is printed immediately on the command line when tests have print statements in them, which makes debugging much easier. NOTE: The following two lines of code can be simplified... ``` python diff --git a/conftest.py b/conftest.py index 99612540638..57c3eefec7c 100755 --- a/conftest.py +++ b/conftest.py @@ -12,10 +12,10 @@ def pytest_addoption(parser): parser.addoption('--browser', action="store", dest='browser', choices=constants.Browser.VERSION.keys(), - default=constants.Browser.FIREFOX, - help="""Specifies the web browser to use. Default=FireFox. - If you want to use Chrome, explicitly indicate that. - Example: (--browser=chrome)""") + default=constants.Browser.GOOGLE_CHROME, + help="""Specifies the web browser to use. Default: Chrome. + If you want to use Firefox, explicitly indicate that. + Example: (--browser=firefox)""") parser.addoption('--is_pytest', action="store_true", dest='is_pytest', default=True, diff --git a/docker_requirements.txt b/docker_requirements.txt new file mode 100755 index 00000000000..186a2c5c4dc --- /dev/null +++ b/docker_requirements.txt @@ -0,0 +1,15 @@ +pip>=9.0.1 +setuptools>=34.3.1 +selenium==2.53.6 +nose>=1.3.7 +pytest>=3.0.6 +six>=1.10.0 +flake8==3.3.0 +requests==2.13.0 +urllib3==1.20 +BeautifulSoup==3.2.1 +unittest2==1.1.0 +chardet==2.3.0 +boto==2.46.1 +ipdb==0.10.2 +pyvirtualdisplay==0.2.1 diff --git a/examples/gui_test_runner.py b/examples/gui_test_runner.py index 342ace1b972..4e2e13bfc7a 100755 --- a/examples/gui_test_runner.py +++ b/examples/gui_test_runner.py @@ -101,6 +101,7 @@ def run_7(self): 'nosetests my_test_suite.py --with-selenium' ' --browser=chrome --with-db_reporting') + if __name__ == "__main__": root = Tk() root.minsize(612, 444) diff --git a/examples/my_first_test.py b/examples/my_first_test.py index 273dcbe6047..34c25ce2284 100755 --- a/examples/my_first_test.py +++ b/examples/my_first_test.py @@ -8,11 +8,11 @@ def test_basic(self): self.assert_element('img[alt="Python"]') # Asserts element on page self.click('a[rel="license"]') # Clicks element on page xkcd_license = self.get_text('center') # Gets text from page element - assert('reuse any of my drawings' in xkcd_license) + self.assertTrue('reuse any of my drawings' in xkcd_license) self.open('http://xkcd.com/1481/') image_object = self.find_element('#comic img') # Returns the element caption = image_object.get_attribute('title') # Gets attr from element - assert('connections to the server' in caption) + self.assertTrue('connections to the server' in caption) self.click_link_text('Blag') # Clicks link containing the text self.assert_text('The blag', 'header h2') # Asserts text in element self.update_text('input#s', 'Robots!\n') # Updates textfield with text diff --git a/help_docs/requirements_installation.md b/help_docs/requirements_installation.md index 4418de2ba72..449def1af05 100755 --- a/help_docs/requirements_installation.md +++ b/help_docs/requirements_installation.md @@ -3,19 +3,27 @@ ### [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 (both of these major versions are being improved in parallel). Python 2.7.10 is the one I've been using on my Mac. +If you're a MAC user, that should already come preinstalled on your machine. Although Python 3 exists, you'll want Python 2 (both of these major versions are being improved in parallel). Python 2.7.13 is the one I've been using on my Mac. -If you're a WINDOWS user, [download the latest 2.* version from here](https://www.python.org/downloads/release/python-2710/). +If you're a WINDOWS user, [download the latest 2.* version from here](https://www.python.org/downloads/release/python-2713/). -### [Pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29) -If "pip" did not come with your Python installation, you can [GET PIP HERE](https://pip.pypa.io/en/latest/installing/). +### [Pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29) -On a MAC, you can also install pip easily with the following command: +On a MAC, run the following command: ```bash sudo easy_install pip ``` -Then make sure it's on your path. + +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) diff --git a/help_docs/verify_webdriver.md b/help_docs/verify_webdriver.md index ef18b6d57d4..7cb53e6b5b1 100755 --- a/help_docs/verify_webdriver.md +++ b/help_docs/verify_webdriver.md @@ -2,21 +2,21 @@ *You can do this by checking inside a Python command prompt. (NOTE: xkcd is a webcomic)* -#### Verifying FirefoxDriver (comes with Selenium by default) +#### Verifying ChromeDriver ```bash python >>> from selenium import webdriver ->>> browser = webdriver.Firefox() +>>> browser = webdriver.Chrome() >>> browser.get("http://xkcd.com/1337/") >>> browser.close() >>> exit() ``` -#### Verifying ChromeDriver (you had to install this separately) +#### Verifying FirefoxDriver (Geckodriver) ```bash python >>> from selenium import webdriver ->>> browser = webdriver.Chrome() +>>> browser = webdriver.Firefox() >>> browser.get("http://xkcd.com/1337/") >>> browser.close() >>> exit() diff --git a/integrations/google_cloud/ReadMe.md b/integrations/google_cloud/ReadMe.md index 8b6b0da3ffa..86c803dce4e 100755 --- a/integrations/google_cloud/ReadMe.md +++ b/integrations/google_cloud/ReadMe.md @@ -76,7 +76,7 @@ sudo pip install -r server_requirements.txt --upgrade #### Step 12. Install SeleniumBase (Make sure you already installed the requirements above) ```bash -sudo python setup.py install +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) diff --git a/integrations/linux/Linuxfile.sh b/integrations/linux/Linuxfile.sh index 45c14cab0da..9c2a501d87f 100755 --- a/integrations/linux/Linuxfile.sh +++ b/integrations/linux/Linuxfile.sh @@ -75,7 +75,7 @@ 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.20/chromedriver_linux64.zip -P ~/Downloads +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 diff --git a/requirements.txt b/requirements.txt index 80e1ca1c364..9f6eb281f43 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,16 @@ -pip>=8.1.2 -setuptools>=28.2.0 -selenium==2.53.6 +pip>=9.0.1 +setuptools>=34.3.1 +selenium==3.3.0 nose>=1.3.7 -pytest>=3.0.2 +pytest>=3.0.6 six>=1.10.0 -flake8==3.0.4 -requests==2.11.1 -urllib3==1.17 +flake8==3.3.0 +requests==2.13.0 +urllib3==1.20 BeautifulSoup==3.2.1 unittest2==1.1.0 chardet==2.3.0 -simplejson==3.8.2 -boto==2.42.0 -ipdb==0.9.4 -pyvirtualdisplay==0.2 +boto==2.46.1 +ipdb==0.10.2 +pyvirtualdisplay==0.2.1 -e . diff --git a/seleniumbase/config/settings.py b/seleniumbase/config/settings.py index ccdb0877d02..85f58f36113 100755 --- a/seleniumbase/config/settings.py +++ b/seleniumbase/config/settings.py @@ -49,15 +49,17 @@ 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). ''' +''' +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 = False +WAIT_FOR_RSC_ON_PAGE_LOADS = True # Called after self.click(selector), NOT element.click() -WAIT_FOR_RSC_ON_CLICKS = False +WAIT_FOR_RSC_ON_CLICKS = True # #####>>>>>----- MasterQA SETTINGS -----<<<<<##### diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index e50faa78653..1d5530c9691 100755 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -1,6 +1,7 @@ from selenium import webdriver from seleniumbase.core import download_helper from seleniumbase.fixtures import constants +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities def get_driver(browser_name): @@ -31,7 +32,9 @@ def get_driver(browser_name): ("application/pdf, application/zip, application/octet-stream, " "text/csv, text/xml, application/xml, text/plain, " "text/octet-stream")) - return webdriver.Firefox(profile) + firefox_capabilities = DesiredCapabilities.FIREFOX + return webdriver.Firefox( + firefox_profile=profile, capabilities=firefox_capabilities) except: return webdriver.Firefox() if browser_name == constants.Browser.INTERNET_EXPLORER: diff --git a/seleniumbase/core/mysql_conf.py b/seleniumbase/core/mysql_conf.py index 57fc5ceff1b..7682c11d4e8 100755 --- a/seleniumbase/core/mysql_conf.py +++ b/seleniumbase/core/mysql_conf.py @@ -12,6 +12,7 @@ class Apps: TESTCASE_REPOSITORY = "testcase_repository" + APP_CREDS = { Apps.TESTCASE_REPOSITORY: { diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 8b7c8f03384..d98a6285cab 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -28,8 +28,10 @@ from seleniumbase.fixtures import page_actions from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import xpath_to_css +from selenium.common.exceptions import StaleElementReferenceException from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys from selenium.webdriver import ActionChains @@ -68,7 +70,14 @@ def click(self, selector, by=By.CSS_SELECTOR, self.driver, selector, by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url - element.click() + try: + element.click() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.05) + element = page_actions.wait_for_element_visible( + self.driver, selector, by, timeout=timeout) + element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: @@ -85,10 +94,20 @@ def double_click(self, selector, by=By.CSS_SELECTOR, self.driver, selector, by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url - actions = ActionChains(self.driver) - actions.move_to_element(element) - actions.double_click(element) - actions.perform() + try: + actions = ActionChains(self.driver) + actions.move_to_element(element) + actions.double_click(element) + actions.perform() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.05) + element = page_actions.wait_for_element_visible( + self.driver, selector, by, timeout=timeout) + actions = ActionChains(self.driver) + actions.move_to_element(element) + actions.double_click(element) + actions.perform() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: @@ -139,7 +158,14 @@ def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): element = self.wait_for_link_text_visible(link_text, timeout=timeout) self._demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT) pre_action_url = self.driver.current_url - element.click() + try: + element.click() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.05) + element = self.wait_for_link_text_visible( + link_text, timeout=timeout) + element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: @@ -152,13 +178,28 @@ def get_text(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout) - return element.text + try: + element_text = element.text + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.06) + element = page_actions.wait_for_element_visible( + self.driver, selector, by, timeout) + element_text = element.text + return element_text def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_present( self.driver, selector, by, timeout) - attribute_value = element.get_attribute(attribute) + try: + attribute_value = element.get_attribute(attribute) + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.06) + element = page_actions.wait_for_element_present( + self.driver, selector, by, timeout) + attribute_value = element.get_attribute(attribute) if attribute_value is not None: return attribute_value else: @@ -173,7 +214,28 @@ def add_text(self, selector, new_value, by=By.CSS_SELECTOR, selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url - element.send_keys(new_value) + try: + if not new_value.endswith('\n'): + element.send_keys(new_value) + else: + new_value = new_value[:-1] + element.send_keys(new_value) + element.send_keys(Keys.RETURN) + if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: + self.wait_for_ready_state_complete() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.06) + element = self.wait_for_element_visible( + selector, by=by, timeout=timeout) + if not new_value.endswith('\n'): + element.send_keys(new_value) + else: + new_value = new_value[:-1] + element.send_keys(new_value) + element.send_keys(Keys.RETURN) + if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: + self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() @@ -198,10 +260,39 @@ def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR, element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) - element.clear() + try: + element.clear() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.06) + element = self.wait_for_element_visible( + selector, by=by, timeout=timeout) + element.clear() self._demo_mode_pause_if_active(tiny=True) pre_action_url = self.driver.current_url - element.send_keys(new_value) + try: + if not new_value.endswith('\n'): + element.send_keys(new_value) + else: + new_value = new_value[:-1] + element.send_keys(new_value) + element.send_keys(Keys.RETURN) + if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: + self.wait_for_ready_state_complete() + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.06) + element = self.wait_for_element_visible( + selector, by=by, timeout=timeout) + element.clear() + if not new_value.endswith('\n'): + element.send_keys(new_value) + else: + new_value = new_value[:-1] + element.send_keys(new_value) + element.send_keys(Keys.RETURN) + if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: + self.wait_for_ready_state_complete() if (retry and element.get_attribute('value') != new_value and ( not new_value.endswith('\n'))): logging.debug('update_text_value is falling back to jQuery!') @@ -362,7 +453,14 @@ def scroll_to(self, selector, by=By.CSS_SELECTOR, ''' Fast scroll to destination ''' element = self.wait_for_element_visible( selector, by=by, timeout=timeout) - self._scroll_to_element(element) + try: + self._scroll_to_element(element) + except StaleElementReferenceException: + self.wait_for_ready_state_complete() + time.sleep(0.05) + element = self.wait_for_element_visible( + selector, by=by, timeout=timeout) + self._scroll_to_element(element) def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index 98d23c631f5..7fe0b914165 100755 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -390,6 +390,7 @@ def wait_for_ready_state_complete(driver, timeout=settings.EXTREME_TIMEOUT): for x in range(int(timeout * 10)): ready_state = driver.execute_script("return document.readyState") if ready_state == u'complete': + time.sleep(0.01) # Better be sure everything is done loading return True else: now_ms = time.time() * 1000.0 diff --git a/seleniumbase/masterqa/master_qa.py b/seleniumbase/masterqa/master_qa.py index e5df3a719af..d5f5ef1b22d 100755 --- a/seleniumbase/masterqa/master_qa.py +++ b/seleniumbase/masterqa/master_qa.py @@ -2,6 +2,7 @@ import shutil import sys import time +from selenium.common.exceptions import WebDriverException from selenium.webdriver.remote.errorhandler import NoAlertPresentException from seleniumbase import BaseCase from seleniumbase.core.style_sheet import style @@ -85,11 +86,16 @@ def manual_page_check(self, *args): self.wait_for_special_alert_absent() text = self.execute_script('''return window.master_qa_result''') else: - self.execute_script('''if(confirm("%s")){window.alert("Success!")} - else{window.alert("Failure!")}''' % question) + try: + self.execute_script( + '''if(confirm("%s")){window.master_qa_result="Success!"} + else{window.master_qa_result="Failure!"}''' % question) + except WebDriverException: + # Fix for https://github.com/mozilla/geckodriver/issues/431 + pass time.sleep(0.05) self.wait_for_special_alert_absent() - text = self.wait_for_and_accept_alert() + text = self.execute_script('''return window.master_qa_result''') self.manual_check_count += 1 if "Success!" in text: self.manual_check_successes += 1 diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py index 1d5c8c9c881..bbd02263368 100755 --- a/seleniumbase/plugins/selenium_plugin.py +++ b/seleniumbase/plugins/selenium_plugin.py @@ -33,47 +33,57 @@ class SeleniumBrowser(Plugin): def options(self, parser, env): super(SeleniumBrowser, self).options(parser, env=env) - parser.add_option('--browser', action='store', - dest='browser', - choices=constants.Browser.VERSION.keys(), - default=constants.Browser.FIREFOX, - help="""Specifies the browser. Default: FireFox. - If you want to use Chrome, indicate that.""") - parser.add_option('--browser_version', action='store', - dest='browser_version', - default="latest", - help="""The browser version to use. Explicitly select - a version number or use "latest".""") - parser.add_option('--server', action='store', dest='servername', - default='localhost', - help="""Designates the server used by the test. - Default: localhost.""") - parser.add_option('--port', action='store', dest='port', - default='4444', - help="""Designates the port used by the test. - Default: 4444.""") - parser.add_option('--headless', action="store_true", - dest='headless', - default=False, - help="""Using this makes Webdriver run headlessly, - which is useful inside a Linux Docker.""") - parser.add_option('--demo_mode', action="store_true", - dest='demo_mode', - default=False, - help="""Using this slows down the automation so that - you can see what it's actually doing.""") - parser.add_option('--demo_sleep', action='store', dest='demo_sleep', - default=None, - help="""Setting this overrides the Demo Mode sleep - time that happens after browser actions.""") - parser.add_option('--highlights', action='store', - dest='highlights', default=None, - help="""Setting this overrides the default number of - highlight animation loops to have per call.""") - parser.add_option('--verify_delay', action='store', - dest='verify_delay', default=None, - help="""Setting this overrides the default wait time - before each MasterQA verification pop-up.""") + parser.add_option( + '--browser', action='store', + dest='browser', + choices=constants.Browser.VERSION.keys(), + default=constants.Browser.GOOGLE_CHROME, + help="""Specifies the web browser to use. Default: Chrome. + If you want to use Firefox, explicitly indicate that. + Example: (--browser=firefox)""") + parser.add_option( + '--browser_version', action='store', + dest='browser_version', + default="latest", + help="""The browser version to use. Explicitly select + a version number or use "latest".""") + parser.add_option( + '--server', action='store', dest='servername', + default='localhost', + help="""Designates the server used by the test. + Default: localhost.""") + parser.add_option( + '--port', action='store', dest='port', + default='4444', + help="""Designates the port used by the test. + Default: 4444.""") + parser.add_option( + '--headless', action="store_true", + dest='headless', + default=False, + help="""Using this makes Webdriver run headlessly, + which is useful inside a Linux Docker.""") + parser.add_option( + '--demo_mode', action="store_true", + dest='demo_mode', + default=False, + help="""Using this slows down the automation so that + you can see what it's actually doing.""") + parser.add_option( + '--demo_sleep', action='store', dest='demo_sleep', + default=None, + help="""Setting this overrides the Demo Mode sleep + time that happens after browser actions.""") + parser.add_option( + '--highlights', action='store', + dest='highlights', default=None, + help="""Setting this overrides the default number of + highlight animation loops to have per call.""") + parser.add_option( + '--verify_delay', action='store', + dest='verify_delay', default=None, + help="""Setting this overrides the default wait time + before each MasterQA verification pop-up.""") def configure(self, options, conf): super(SeleniumBrowser, self).configure(options, conf) diff --git a/server_requirements.txt b/server_requirements.txt index 7da19949270..d93fdf76fee 100755 --- a/server_requirements.txt +++ b/server_requirements.txt @@ -1,18 +1,16 @@ -pip>=8.1.2 -setuptools>=28.2.0 +pip>=9.0.1 +setuptools>=34.3.1 selenium==2.53.6 nose>=1.3.7 -pytest>=3.0.2 +pytest>=3.0.6 six>=1.10.0 -flake8==3.0.4 -requests==2.11.1 -urllib3==1.17 +flake8==3.3.0 +requests==2.13.0 +urllib3==1.20 BeautifulSoup==3.2.1 unittest2==1.1.0 chardet==2.3.0 -simplejson==3.8.2 -boto==2.42.0 -ipdb==0.9.4 -pyvirtualdisplay==0.2 +boto==2.46.1 +ipdb==0.10.2 +pyvirtualdisplay==0.2.1 MySQL-python==1.2.5 --e . diff --git a/server_setup.py b/server_setup.py new file mode 100755 index 00000000000..4dec5133a18 --- /dev/null +++ b/server_setup.py @@ -0,0 +1,55 @@ +""" +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.3.0', + url='http://seleniumbase.com', + author='Michael Mintz', + author_email='@mintzworld', + maintainer='Michael Mintz', + description='Reliable Browser Automation - http://seleniumbase.com', + license='The MIT License', + install_requires=[ + 'pip>=9.0.1', + 'setuptools>=34.3.1', + 'selenium==2.53.6', + 'nose>=1.3.7', + 'pytest>=3.0.6', + 'six>=1.10.0', + 'flake8==3.3.0', + 'requests==2.13.0', + 'urllib3==1.20', + 'BeautifulSoup==3.2.1', + 'unittest2==1.1.0', + 'chardet==2.3.0', + 'boto==2.46.1', + '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'), + ] + } + ) diff --git a/setup.py b/setup.py index 7c6cfee147b..f9c1bc13df1 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='seleniumbase', - version='1.2.15', + version='1.3.0', url='http://seleniumbase.com', author='Michael Mintz', author_email='@mintzworld', @@ -14,22 +14,21 @@ description='Reliable Browser Automation - http://seleniumbase.com', license='The MIT License', install_requires=[ - 'pip>=8.1.2', - 'setuptools>=28.2.0', - 'selenium==2.53.6', + 'pip>=9.0.1', + 'setuptools>=34.3.1', + 'selenium==3.3.0', 'nose>=1.3.7', - 'pytest>=3.0.2', + 'pytest>=3.0.6', 'six>=1.10.0', - 'flake8==3.0.4', - 'requests==2.11.1', - 'urllib3==1.17', + 'flake8==3.3.0', + 'requests==2.13.0', + 'urllib3==1.20', 'BeautifulSoup==3.2.1', 'unittest2==1.1.0', 'chardet==2.3.0', - 'simplejson==3.8.2', - 'boto==2.42.0', - 'ipdb==0.9.4', - 'pyvirtualdisplay==0.2', + 'boto==2.46.1', + 'ipdb==0.10.2', + 'pyvirtualdisplay==0.2.1', ], packages=['seleniumbase', 'seleniumbase.core',