mirror of https://github.com/django/django.git
Fixed #29883 -- Added selenium hub support to runtests.py.
This commit is contained in:
parent
5a2dd5ec53
commit
d207ac1568
|
@ -2,7 +2,11 @@ import sys
|
||||||
import unittest
|
import unittest
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||||
|
|
||||||
from django.test import LiveServerTestCase, tag
|
from django.test import LiveServerTestCase, tag
|
||||||
|
from django.utils.decorators import classproperty
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
|
|
||||||
|
@ -10,6 +14,10 @@ from django.utils.text import capfirst
|
||||||
class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
||||||
# List of browsers to dynamically create test classes for.
|
# List of browsers to dynamically create test classes for.
|
||||||
browsers = []
|
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.
|
# Sentinel value to differentiate browser-specific instances.
|
||||||
browser = None
|
browser = None
|
||||||
|
|
||||||
|
@ -29,6 +37,10 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
||||||
# either duplicate tests or prevent pickling of its instances.
|
# either duplicate tests or prevent pickling of its instances.
|
||||||
first_browser = test_class.browsers[0]
|
first_browser = test_class.browsers[0]
|
||||||
test_class.browser = first_browser
|
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
|
# Create subclasses for each of the remaining browsers and expose
|
||||||
# them through the test's module namespace.
|
# them through the test's module namespace.
|
||||||
module = sys.modules[test_class.__module__]
|
module = sys.modules[test_class.__module__]
|
||||||
|
@ -37,7 +49,12 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
||||||
cls,
|
cls,
|
||||||
"%s%s" % (capfirst(browser), name),
|
"%s%s" % (capfirst(browser), name),
|
||||||
(test_class,),
|
(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)
|
setattr(module, browser_test_class.__name__, browser_test_class)
|
||||||
return test_class
|
return test_class
|
||||||
|
@ -48,13 +65,31 @@ class SeleniumTestCaseBase(type(LiveServerTestCase)):
|
||||||
def import_webdriver(cls, browser):
|
def import_webdriver(cls, browser):
|
||||||
return import_string("selenium.webdriver.%s.webdriver.WebDriver" % 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):
|
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)()
|
return self.import_webdriver(self.browser)()
|
||||||
|
|
||||||
|
|
||||||
@tag('selenium')
|
@tag('selenium')
|
||||||
class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase):
|
class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase):
|
||||||
implicit_wait = 10
|
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
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
|
|
@ -1303,6 +1303,10 @@ class LiveServerTestCase(TransactionTestCase):
|
||||||
def live_server_url(cls):
|
def live_server_url(cls):
|
||||||
return 'http://%s:%s' % (cls.host, cls.server_thread.port)
|
return 'http://%s:%s' % (cls.host, cls.server_thread.port)
|
||||||
|
|
||||||
|
@classproperty
|
||||||
|
def allowed_host(cls):
|
||||||
|
return cls.host
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
@ -1316,7 +1320,7 @@ class LiveServerTestCase(TransactionTestCase):
|
||||||
connections_override[conn.alias] = conn
|
connections_override[conn.alias] = conn
|
||||||
|
|
||||||
cls._live_server_modified_settings = modify_settings(
|
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._live_server_modified_settings.enable()
|
||||||
cls.server_thread = cls._create_server_thread(connections_override)
|
cls.server_thread = cls._create_server_thread(connections_override)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import atexit
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -436,6 +437,15 @@ if __name__ == "__main__":
|
||||||
'--selenium', action=ActionSelenium, metavar='BROWSERS',
|
'--selenium', action=ActionSelenium, metavar='BROWSERS',
|
||||||
help='A comma-separated list of browsers to run the Selenium tests against.',
|
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(
|
parser.add_argument(
|
||||||
'--debug-sql', action='store_true',
|
'--debug-sql', action='store_true',
|
||||||
help='Turn on the SQL query logger within tests.',
|
help='Turn on the SQL query logger within tests.',
|
||||||
|
@ -456,6 +466,12 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
options = parser.parse_args()
|
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
|
# Allow including a trailing slash on app_labels for tab completion convenience
|
||||||
options.modules = [os.path.normpath(labels) for labels in options.modules]
|
options.modules = [os.path.normpath(labels) for labels in options.modules]
|
||||||
|
|
||||||
|
@ -470,6 +486,9 @@ if __name__ == "__main__":
|
||||||
options.tags = ['selenium']
|
options.tags = ['selenium']
|
||||||
elif 'selenium' not in options.tags:
|
elif 'selenium' not in options.tags:
|
||||||
options.tags.append('selenium')
|
options.tags.append('selenium')
|
||||||
|
if options.selenium_hub:
|
||||||
|
SeleniumTestCaseBase.selenium_hub = options.selenium_hub
|
||||||
|
SeleniumTestCaseBase.external_host = options.external_host
|
||||||
SeleniumTestCaseBase.browsers = options.selenium
|
SeleniumTestCaseBase.browsers = options.selenium
|
||||||
|
|
||||||
if options.bisect:
|
if options.bisect:
|
||||||
|
|
Loading…
Reference in New Issue