summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Forbes <tom@tomforb.es>2018-10-25 18:52:29 +0100
committerTim Graham <timograham@gmail.com>2018-11-03 09:56:14 -0400
commitd207ac1568bb4dee305f6692ed7ddee8a1ff8b99 (patch)
treecfae8b06b55ba09b35d92ae890f2b2e23b12675e
parent5a2dd5ec5330938e9afb77faf6ca89abf39c018c (diff)
Fixed #29883 -- Added selenium hub support to runtests.py.
-rw-r--r--django/test/selenium.py37
-rw-r--r--django/test/testcases.py6
-rwxr-xr-xtests/runtests.py19
3 files changed, 60 insertions, 2 deletions
diff --git a/django/test/selenium.py b/django/test/selenium.py
index 9bf1e1038d..976f6ab06e 100644
--- a/django/test/selenium.py
+++ b/django/test/selenium.py
@@ -2,7 +2,11 @@ import sys
import unittest
from contextlib import contextmanager
+from selenium import webdriver
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+
from django.test import LiveServerTestCase, tag
+from django.utils.decorators import classproperty
from django.utils.module_loading import import_string
from django.utils.text import capfirst
@@ -10,6 +14,10 @@ from django.utils.text import capfirst
class SeleniumTestCaseBase(type(LiveServerTestCase)):
# List of browsers to dynamically create test classes for.
browsers = []
+ # A selenium hub URL to test against.
+ selenium_hub = None
+ # The external host Selenium Hub can reach.
+ external_host = None
# Sentinel value to differentiate browser-specific instances.
browser = None
@@ -29,6 +37,10 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
# either duplicate tests or prevent pickling of its instances.
first_browser = test_class.browsers[0]
test_class.browser = first_browser
+ # Listen on an external interface if using a selenium hub.
+ host = test_class.host if not test_class.selenium_hub else '0.0.0.0'
+ test_class.host = host
+ test_class.external_host = cls.external_host
# Create subclasses for each of the remaining browsers and expose
# them through the test's module namespace.
module = sys.modules[test_class.__module__]
@@ -37,7 +49,12 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
cls,
"%s%s" % (capfirst(browser), name),
(test_class,),
- {'browser': browser, '__module__': test_class.__module__}
+ {
+ 'browser': browser,
+ 'host': host,
+ 'external_host': cls.external_host,
+ '__module__': test_class.__module__,
+ }
)
setattr(module, browser_test_class.__name__, browser_test_class)
return test_class
@@ -48,13 +65,31 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
def import_webdriver(cls, browser):
return import_string("selenium.webdriver.%s.webdriver.WebDriver" % browser)
+ @classmethod
+ def get_capability(cls, browser):
+ return getattr(DesiredCapabilities, browser.upper())
+
def create_webdriver(self):
+ if self.selenium_hub:
+ return webdriver.Remote(
+ command_executor=self.selenium_hub,
+ desired_capabilities=self.get_capability(self.browser),
+ )
return self.import_webdriver(self.browser)()
@tag('selenium')
class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase):
implicit_wait = 10
+ external_host = None
+
+ @classproperty
+ def live_server_url(cls):
+ return 'http://%s:%s' % (cls.external_host or cls.host, cls.server_thread.port)
+
+ @classproperty
+ def allowed_host(cls):
+ return cls.external_host or cls.host
@classmethod
def setUpClass(cls):
diff --git a/django/test/testcases.py b/django/test/testcases.py
index f327e53561..2c358c01af 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -1303,6 +1303,10 @@ class LiveServerTestCase(TransactionTestCase):
def live_server_url(cls):
return 'http://%s:%s' % (cls.host, cls.server_thread.port)
+ @classproperty
+ def allowed_host(cls):
+ return cls.host
+
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -1316,7 +1320,7 @@ class LiveServerTestCase(TransactionTestCase):
connections_override[conn.alias] = conn
cls._live_server_modified_settings = modify_settings(
- ALLOWED_HOSTS={'append': cls.host},
+ ALLOWED_HOSTS={'append': cls.allowed_host},
)
cls._live_server_modified_settings.enable()
cls.server_thread = cls._create_server_thread(connections_override)
diff --git a/tests/runtests.py b/tests/runtests.py
index 88971f43a1..a410777203 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -4,6 +4,7 @@ import atexit
import copy
import os
import shutil
+import socket
import subprocess
import sys
import tempfile
@@ -437,6 +438,15 @@ if __name__ == "__main__":
help='A comma-separated list of browsers to run the Selenium tests against.',
)
parser.add_argument(
+ '--selenium-hub',
+ help='A URL for a selenium hub instance to use in combination with --selenium.',
+ )
+ parser.add_argument(
+ '--external-host', default=socket.gethostname(),
+ help='The external host that can be reached by the selenium hub instance when running Selenium '
+ 'tests via Selenium Hub.',
+ )
+ parser.add_argument(
'--debug-sql', action='store_true',
help='Turn on the SQL query logger within tests.',
)
@@ -456,6 +466,12 @@ if __name__ == "__main__":
options = parser.parse_args()
+ using_selenium_hub = options.selenium and options.selenium_hub
+ if options.selenium_hub and not options.selenium:
+ parser.error('--selenium-hub and --external-host require --selenium to be used.')
+ if using_selenium_hub and not options.external_host:
+ parser.error('--selenium-hub and --external-host must be used together.')
+
# Allow including a trailing slash on app_labels for tab completion convenience
options.modules = [os.path.normpath(labels) for labels in options.modules]
@@ -470,6 +486,9 @@ if __name__ == "__main__":
options.tags = ['selenium']
elif 'selenium' not in options.tags:
options.tags.append('selenium')
+ if options.selenium_hub:
+ SeleniumTestCaseBase.selenium_hub = options.selenium_hub
+ SeleniumTestCaseBase.external_host = options.external_host
SeleniumTestCaseBase.browsers = options.selenium
if options.bisect: