tests.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. from contextlib import contextmanager
  2. from django.contrib.staticfiles.testing import StaticLiveServerTestCase
  3. from django.test import modify_settings
  4. from django.test.selenium import SeleniumTestCase
  5. from django.utils.deprecation import MiddlewareMixin
  6. from django.utils.translation import gettext as _
  7. class CSPMiddleware(MiddlewareMixin):
  8. """The admin's JavaScript should be compatible with CSP."""
  9. def process_response(self, request, response):
  10. response.headers['Content-Security-Policy'] = "default-src 'self'"
  11. return response
  12. @modify_settings(MIDDLEWARE={'append': 'django.contrib.admin.tests.CSPMiddleware'})
  13. class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):
  14. available_apps = [
  15. 'django.contrib.admin',
  16. 'django.contrib.auth',
  17. 'django.contrib.contenttypes',
  18. 'django.contrib.sessions',
  19. 'django.contrib.sites',
  20. ]
  21. def wait_until(self, callback, timeout=10):
  22. """
  23. Block the execution of the tests until the specified callback returns a
  24. value that is not falsy. This method can be called, for example, after
  25. clicking a link or submitting a form. See the other public methods that
  26. call this function for more details.
  27. """
  28. from selenium.webdriver.support.wait import WebDriverWait
  29. WebDriverWait(self.selenium, timeout).until(callback)
  30. def wait_for_and_switch_to_popup(self, num_windows=2, timeout=10):
  31. """
  32. Block until `num_windows` are present and are ready (usually 2, but can
  33. be overridden in the case of pop-ups opening other pop-ups). Switch the
  34. current window to the new pop-up.
  35. """
  36. self.wait_until(lambda d: len(d.window_handles) == num_windows, timeout)
  37. self.selenium.switch_to.window(self.selenium.window_handles[-1])
  38. self.wait_page_ready()
  39. def wait_for(self, css_selector, timeout=10):
  40. """
  41. Block until a CSS selector is found on the page.
  42. """
  43. from selenium.webdriver.common.by import By
  44. from selenium.webdriver.support import expected_conditions as ec
  45. self.wait_until(
  46. ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)),
  47. timeout
  48. )
  49. def wait_for_text(self, css_selector, text, timeout=10):
  50. """
  51. Block until the text is found in the CSS selector.
  52. """
  53. from selenium.webdriver.common.by import By
  54. from selenium.webdriver.support import expected_conditions as ec
  55. self.wait_until(
  56. ec.text_to_be_present_in_element(
  57. (By.CSS_SELECTOR, css_selector), text),
  58. timeout
  59. )
  60. def wait_for_value(self, css_selector, text, timeout=10):
  61. """
  62. Block until the value is found in the CSS selector.
  63. """
  64. from selenium.webdriver.common.by import By
  65. from selenium.webdriver.support import expected_conditions as ec
  66. self.wait_until(
  67. ec.text_to_be_present_in_element_value(
  68. (By.CSS_SELECTOR, css_selector), text),
  69. timeout
  70. )
  71. def wait_until_visible(self, css_selector, timeout=10):
  72. """
  73. Block until the element described by the CSS selector is visible.
  74. """
  75. from selenium.webdriver.common.by import By
  76. from selenium.webdriver.support import expected_conditions as ec
  77. self.wait_until(
  78. ec.visibility_of_element_located((By.CSS_SELECTOR, css_selector)),
  79. timeout
  80. )
  81. def wait_until_invisible(self, css_selector, timeout=10):
  82. """
  83. Block until the element described by the CSS selector is invisible.
  84. """
  85. from selenium.webdriver.common.by import By
  86. from selenium.webdriver.support import expected_conditions as ec
  87. self.wait_until(
  88. ec.invisibility_of_element_located((By.CSS_SELECTOR, css_selector)),
  89. timeout
  90. )
  91. def wait_page_ready(self, timeout=10):
  92. """
  93. Block until the page is ready.
  94. """
  95. self.wait_until(
  96. lambda driver: driver.execute_script('return document.readyState;') == 'complete',
  97. timeout,
  98. )
  99. @contextmanager
  100. def wait_page_loaded(self, timeout=10):
  101. """
  102. Block until a new page has loaded and is ready.
  103. """
  104. from selenium.webdriver.support import expected_conditions as ec
  105. old_page = self.selenium.find_element_by_tag_name('html')
  106. yield
  107. # Wait for the next page to be loaded
  108. self.wait_until(ec.staleness_of(old_page), timeout=timeout)
  109. self.wait_page_ready(timeout=timeout)
  110. def admin_login(self, username, password, login_url='/admin/'):
  111. """
  112. Log in to the admin.
  113. """
  114. self.selenium.get('%s%s' % (self.live_server_url, login_url))
  115. username_input = self.selenium.find_element_by_name('username')
  116. username_input.send_keys(username)
  117. password_input = self.selenium.find_element_by_name('password')
  118. password_input.send_keys(password)
  119. login_text = _('Log in')
  120. with self.wait_page_loaded():
  121. self.selenium.find_element_by_xpath('//input[@value="%s"]' % login_text).click()
  122. def select_option(self, selector, value):
  123. """
  124. Select the <OPTION> with the value `value` inside the <SELECT> widget
  125. identified by the CSS selector `selector`.
  126. """
  127. from selenium.webdriver.support.ui import Select
  128. select = Select(self.selenium.find_element_by_css_selector(selector))
  129. select.select_by_value(value)
  130. def deselect_option(self, selector, value):
  131. """
  132. Deselect the <OPTION> with the value `value` inside the <SELECT> widget
  133. identified by the CSS selector `selector`.
  134. """
  135. from selenium.webdriver.support.ui import Select
  136. select = Select(self.selenium.find_element_by_css_selector(selector))
  137. select.deselect_by_value(value)
  138. def _assertOptionsValues(self, options_selector, values):
  139. if values:
  140. options = self.selenium.find_elements_by_css_selector(options_selector)
  141. actual_values = []
  142. for option in options:
  143. actual_values.append(option.get_attribute('value'))
  144. self.assertEqual(values, actual_values)
  145. else:
  146. # Prevent the `find_elements_by_css_selector` call from blocking
  147. # if the selector doesn't match any options as we expect it
  148. # to be the case.
  149. with self.disable_implicit_wait():
  150. self.wait_until(
  151. lambda driver: not driver.find_elements_by_css_selector(options_selector)
  152. )
  153. def assertSelectOptions(self, selector, values):
  154. """
  155. Assert that the <SELECT> widget identified by `selector` has the
  156. options with the given `values`.
  157. """
  158. self._assertOptionsValues("%s > option" % selector, values)
  159. def assertSelectedOptions(self, selector, values):
  160. """
  161. Assert that the <SELECT> widget identified by `selector` has the
  162. selected options with the given `values`.
  163. """
  164. self._assertOptionsValues("%s > option:checked" % selector, values)
  165. def has_css_class(self, selector, klass):
  166. """
  167. Return True if the element identified by `selector` has the CSS class
  168. `klass`.
  169. """
  170. return (self.selenium.find_element_by_css_selector(selector)
  171. .get_attribute('class').find(klass) != -1)