Skip to content

Commit

Permalink
Enable PhantomJS for running Selenium tests
Browse files Browse the repository at this point in the history
This patch enables the PhantomJS webdriver for running
the Selenium test suite.

Use it with the --selenium-phantomjs command-line switch
when executing the selenium and integration suite.

Change-Id: I443e6f6d7d1911df500b360f7c22686b417fbeae
Blueprint: enable-phantomjs-selenium
  • Loading branch information
r1chardj0n3s committed Nov 25, 2015
1 parent ab7d5c3 commit 861f9c2
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ dist
AUTHORS
ChangeLog
tags
ghostdriver.log
10 changes: 10 additions & 0 deletions doc/source/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ for a Debian OS flavour, or for Fedora/Red Hat flavours:

$ sudo yum install xorg-x11-server-Xvfb

If you can't run a virtual display, or would prefer not to, you can use the
PhantomJS web driver instead:

$ ./run_tests.sh --with-selenium --selenium-phantomjs

If you need to install PhantomJS, you may do so with `npm` like this:

$ npm -g install phantomjs


Writing tests
=============

Expand Down
106 changes: 53 additions & 53 deletions horizon/test/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,71 +25,71 @@

LOG = logging.getLogger(__name__)

# Select the WebDriver to use based on the --selenium-phantomjs switch.
# NOTE: Several distributions can't ship Selenium, or the Firefox
# component of it, due to its non-free license. So they have to patch
# it out of test-requirements.txt Avoid import failure and force not
# running selenium tests if we attempt to run selenium tests using the
# Firefox driver and it is not available.
try:
# NOTE: Several distribution can't ship selenium due to its
# non-free license. So they have to patch it out of test-requirements.txt
# Avoid import failure and force not running selenium tests.
# The entire file is encapsulated in the try block because the classes
# inherit from the firefox class contained in selenium.webdriver, and
# python will throw a NameError if the import is skipped.
from selenium.common import exceptions as selenium_exceptions
from selenium.webdriver import firefox
if os.environ.get('SELENIUM_PHANTOMJS'):
from selenium.webdriver import PhantomJS as WebDriver
else:
from selenium.common import exceptions as selenium_exceptions
from selenium.webdriver import firefox

class FirefoxBinary(firefox.firefox_binary.FirefoxBinary):
"""Workarounds selenium firefox issues.
class FirefoxBinary(firefox.firefox_binary.FirefoxBinary):
"""Workarounds selenium firefox issues.
There is race condition in the way firefox is spawned. The exact cause
hasn't been properly diagnosed yet but it's around:
There is race condition in the way firefox is spawned. The exact
cause hasn't been properly diagnosed yet but it's around:
- getting a free port from the OS with selenium.webdriver.common.utils
free_port(),
- getting a free port from the OS with
selenium.webdriver.common.utils free_port(),
- release the port immediately but record it in ff prefs so that ff can
listen on that port for the internal http server.
- release the port immediately but record it in ff prefs so that ff
can listen on that port for the internal http server.
It has been observed that this leads to hanging processes for 'firefox
-silent'.
"""
It has been observed that this leads to hanging processes for
'firefox -silent'.
"""

def _start_from_profile_path(self, path):
self._firefox_env["XRE_PROFILE_PATH"] = path
def _start_from_profile_path(self, path):
self._firefox_env["XRE_PROFILE_PATH"] = path

if platform.system().lower() == 'linux':
self._modify_link_library_path()
command = [self._start_cmd, "-silent"]
if self.command_line is not None:
for cli in self.command_line:
command.append(cli)
if platform.system().lower() == 'linux':
self._modify_link_library_path()
command = [self._start_cmd, "-silent"]
if self.command_line is not None:
for cli in self.command_line:
command.append(cli)

# The following exists upstream and is known to create hanging firefoxes,
# leading to zombies.
# subprocess.Popen(command, stdout=self._log_file,
# stderr=subprocess.STDOUT,
# env=self._firefox_env).communicate()
command[1] = '-foreground'
self.process = subprocess.Popen(
command, stdout=self._log_file, stderr=subprocess.STDOUT,
env=self._firefox_env)
# The following exists upstream and is known to create hanging
# firefoxes, leading to zombies.
# subprocess.Popen(command, stdout=self._log_file,
# stderr=subprocess.STDOUT,
# env=self._firefox_env).communicate()
command[1] = '-foreground'
self.process = subprocess.Popen(
command, stdout=self._log_file, stderr=subprocess.STDOUT,
env=self._firefox_env)

class WebDriver(firefox.webdriver.WebDriver):
"""Workarounds selenium firefox issues."""
class WebDriver(firefox.webdriver.WebDriver):
"""Workarounds selenium firefox issues."""

def __init__(self, firefox_profile=None, firefox_binary=None,
timeout=30, capabilities=None, proxy=None):
try:
super(WebDriver, self).__init__(
firefox_profile, FirefoxBinary(), timeout, capabilities,
proxy)
except selenium_exceptions.WebDriverException:
# If we can't start, cleanup profile
shutil.rmtree(self.profile.path)
if self.profile.tempfolder is not None:
shutil.rmtree(self.profile.tempfolder)
raise
def __init__(self, firefox_profile=None, firefox_binary=None,
timeout=30, capabilities=None, proxy=None):
try:
super(WebDriver, self).__init__(
firefox_profile, FirefoxBinary(), timeout,
capabilities, proxy)
except selenium_exceptions.WebDriverException:
# If we can't start, cleanup profile
shutil.rmtree(self.profile.path)
if self.profile.tempfolder is not None:
shutil.rmtree(self.profile.tempfolder)
raise

except ImportError as e:
# NOTE(saschpe): Several distribution can't ship selenium due to its
# non-free license. So they have to patch it out of test-requirements.txt
# Avoid import failure and force not running selenium tests.
LOG.warning("{0}, force WITH_SELENIUM=False".format(str(e)))
os.environ['WITH_SELENIUM'] = ''
6 changes: 6 additions & 0 deletions openstack_dashboard/dashboards/project/images/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
# License for the specific language governing permissions and limitations
# under the License.

import os
from socket import timeout as socket_timeout # noqa
import unittest

from django.core.urlresolvers import reverse
from django import http
Expand Down Expand Up @@ -411,6 +413,8 @@ def test_modal_create_image_from_url(self):
self.assertTrue("ISO" in body.text,
"ISO should be selected when the extension is *.iso")

@unittest.skipIf(os.environ.get('SELENIUM_PHANTOMJS'),
"PhantomJS cannot test file upload widgets.")
@test.create_stubs({api.glance: ('image_list_detailed',)})
def test_modal_create_image_from_file(self):
driver = self.selenium
Expand Down Expand Up @@ -471,6 +475,8 @@ def test_create_image_from_url(self):
self.assertTrue("ISO" in body.text,
"ISO should be selected when the extension is *.iso")

@unittest.skipIf(os.environ.get('SELENIUM_PHANTOMJS'),
"PhantomJS cannot test file upload widgets.")
@test.create_stubs({api.glance: ('image_list_detailed',)})
def test_create_image_from_file(self):
driver = self.selenium
Expand Down
10 changes: 9 additions & 1 deletion openstack_dashboard/test/integration_tests/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import time

from selenium.common import exceptions
Expand Down Expand Up @@ -145,7 +146,14 @@ def _execute(self, command, params=None):
return result


class WebDriverWrapper(WrapperFindOverride, webdriver.Firefox):
# select the active webdriver based on whether we --selenium-phantomjs or not
if os.environ.get('SELENIUM_PHANTOMJS'):
WebDriver = webdriver.PhantomJS
else:
WebDriver = webdriver.Firefox


class WebDriverWrapper(WrapperFindOverride, WebDriver):
"""Wrapper for webdriver to return WebElementWrapper on find_element."""
def __init__(self, logging_prefs=None, capabilities=None, **kwargs):
if capabilities is None:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
features:
- Selenium tests may now be exercised using the headless PhantomJS driver.
7 changes: 7 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function usage {
echo " --only-selenium Run only the Selenium unit tests"
echo " --with-selenium Run unit tests including Selenium tests"
echo " --selenium-headless Run Selenium tests headless"
echo " --selenium-phantomjs Run Selenium tests using phantomjs (headless)"
echo " --integration Run the integration tests (requires a running "
echo " OpenStack environment)"
echo " --runserver Run the Django development server for"
Expand Down Expand Up @@ -79,6 +80,7 @@ runserver=0
only_selenium=0
with_selenium=0
selenium_headless=0
selenium_phantomjs=0
integration=0
testopts=""
testargs=""
Expand Down Expand Up @@ -121,6 +123,7 @@ function process_option {
--only-selenium) only_selenium=1;;
--with-selenium) with_selenium=1;;
--selenium-headless) selenium_headless=1;;
--selenium-phantomjs) selenium_phantomjs=1;;
--integration) integration=1;;
--docs) just_docs=1;;
--runserver) runserver=1;;
Expand Down Expand Up @@ -347,6 +350,10 @@ function run_tests {
export SELENIUM_HEADLESS=1
fi

if [ $selenium_phantomjs -eq 1 ]; then
export SELENIUM_PHANTOMJS=1
fi

if [ -z "$testargs" ]; then
run_tests_all
else
Expand Down

0 comments on commit 861f9c2

Please sign in to comment.