You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

selenium.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import sys
  2. import unittest
  3. from contextlib import contextmanager
  4. from django.test import LiveServerTestCase, tag
  5. from django.utils.decorators import classproperty
  6. from django.utils.module_loading import import_string
  7. from django.utils.text import capfirst
  8. class SeleniumTestCaseBase(type(LiveServerTestCase)):
  9. # List of browsers to dynamically create test classes for.
  10. browsers = []
  11. # A selenium hub URL to test against.
  12. selenium_hub = None
  13. # The external host Selenium Hub can reach.
  14. external_host = None
  15. # Sentinel value to differentiate browser-specific instances.
  16. browser = None
  17. def __new__(cls, name, bases, attrs):
  18. """
  19. Dynamically create new classes and add them to the test module when
  20. multiple browsers specs are provided (e.g. --selenium=firefox,chrome).
  21. """
  22. test_class = super().__new__(cls, name, bases, attrs)
  23. # If the test class is either browser-specific or a test base, return it.
  24. if test_class.browser or not any(name.startswith('test') and callable(value) for name, value in attrs.items()):
  25. return test_class
  26. elif test_class.browsers:
  27. # Reuse the created test class to make it browser-specific.
  28. # We can't rename it to include the browser name or create a
  29. # subclass like we do with the remaining browsers as it would
  30. # either duplicate tests or prevent pickling of its instances.
  31. first_browser = test_class.browsers[0]
  32. test_class.browser = first_browser
  33. # Listen on an external interface if using a selenium hub.
  34. host = test_class.host if not test_class.selenium_hub else '0.0.0.0'
  35. test_class.host = host
  36. test_class.external_host = cls.external_host
  37. # Create subclasses for each of the remaining browsers and expose
  38. # them through the test's module namespace.
  39. module = sys.modules[test_class.__module__]
  40. for browser in test_class.browsers[1:]:
  41. browser_test_class = cls.__new__(
  42. cls,
  43. "%s%s" % (capfirst(browser), name),
  44. (test_class,),
  45. {
  46. 'browser': browser,
  47. 'host': host,
  48. 'external_host': cls.external_host,
  49. '__module__': test_class.__module__,
  50. }
  51. )
  52. setattr(module, browser_test_class.__name__, browser_test_class)
  53. return test_class
  54. # If no browsers were specified, skip this class (it'll still be discovered).
  55. return unittest.skip('No browsers specified.')(test_class)
  56. @classmethod
  57. def import_webdriver(cls, browser):
  58. return import_string("selenium.webdriver.%s.webdriver.WebDriver" % browser)
  59. @classmethod
  60. def get_capability(cls, browser):
  61. from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
  62. return getattr(DesiredCapabilities, browser.upper())
  63. def create_webdriver(self):
  64. if self.selenium_hub:
  65. from selenium import webdriver
  66. return webdriver.Remote(
  67. command_executor=self.selenium_hub,
  68. desired_capabilities=self.get_capability(self.browser),
  69. )
  70. return self.import_webdriver(self.browser)()
  71. @tag('selenium')
  72. class SeleniumTestCase(LiveServerTestCase, metaclass=SeleniumTestCaseBase):
  73. implicit_wait = 10
  74. external_host = None
  75. @classproperty
  76. def live_server_url(cls):
  77. return 'http://%s:%s' % (cls.external_host or cls.host, cls.server_thread.port)
  78. @classproperty
  79. def allowed_host(cls):
  80. return cls.external_host or cls.host
  81. @classmethod
  82. def setUpClass(cls):
  83. cls.selenium = cls.create_webdriver()
  84. cls.selenium.implicitly_wait(cls.implicit_wait)
  85. super().setUpClass()
  86. @classmethod
  87. def _tearDownClassInternal(cls):
  88. # quit() the WebDriver before attempting to terminate and join the
  89. # single-threaded LiveServerThread to avoid a dead lock if the browser
  90. # kept a connection alive.
  91. if hasattr(cls, 'selenium'):
  92. cls.selenium.quit()
  93. super()._tearDownClassInternal()
  94. @contextmanager
  95. def disable_implicit_wait(self):
  96. """Disable the default implicit wait."""
  97. self.selenium.implicitly_wait(0)
  98. try:
  99. yield
  100. finally:
  101. self.selenium.implicitly_wait(self.implicit_wait)